demux: enrich edition titles with codec/resolution/bitrate info

Extract the codec descriptor out of mp_format_track_metadata into a
shared demux_append_codec_desc() helper, and use it from a new
demux_compose_edition_title() composer that both demux_timeline.c and
demux_lavf.c call when building editions. Codec block is only added
when the program has exactly one stream of that kind, and fields
already present in the prefix (e.g. WxH from yt-dlp's format label)
are skipped to avoid duplication.
This commit is contained in:
Kacper Michajłow committed 2026-04-29 03:45:05 +02:00
1 parent a42b1b9103
commit 91b6d8fdd1
5 files changed
+122 -64

No files matched your search

+70
View File
@@ -1249,6 +1249,76 @@ const char *stream_type_name(enum stream_type type)
}
}
static void append_field(void *ta_ctx, bstr *dst, const char *skip_dup,
const char *value)
{
if (skip_dup && skip_dup[0] && strstr(skip_dup, value))
return;
bstr_xappend_asprintf(ta_ctx, dst, " %s", value);
}
void demux_append_codec_desc(void *ta_ctx, bstr *dst, struct sh_stream *sh,
const char *skip_dup)
{
struct mp_codec_params *c = sh->codec;
bstr_xappend0(ta_ctx, dst, c->codec ? c->codec : "<unknown>");
if (c->codec_profile)
bstr_xappend_asprintf(ta_ctx, dst, " [%s]", c->codec_profile);
if (c->disp_w)
append_field(ta_ctx, dst, skip_dup,
mp_tprintf(32, "%dx%d", c->disp_w, c->disp_h));
if (c->fps && !sh->image) {
char *fps = mp_format_double(ta_ctx, c->fps, 4, false, false, true);
append_field(ta_ctx, dst, skip_dup, mp_tprintf(32, "%s fps", fps));
}
if (c->channels.num)
bstr_xappend_asprintf(ta_ctx, dst, " %dch", c->channels.num);
if (c->samplerate)
bstr_xappend_asprintf(ta_ctx, dst, " %d Hz", c->samplerate);
int bitrate = 0;
if (c->bitrate > 0 && c->bitrate < INT_MAX - 500) {
bitrate = (c->bitrate + 500) / 1000;
} else if (sh->hls_bitrate > 0 && sh->hls_bitrate < INT_MAX - 500) {
bitrate = (sh->hls_bitrate + 500) / 1000;
}
if (bitrate)
append_field(ta_ctx, dst, skip_dup, mp_tprintf(32, "%d kbps", bitrate));
}
char *demux_compose_edition_title(void *ta_ctx, struct demuxer *demuxer,
int program_id, const char *prefix)
{
struct sh_stream *vsh = NULL, *ash = NULL;
int video_count = 0, audio_count = 0;
int num = demux_get_num_stream(demuxer);
for (int i = 0; i < num; i++) {
struct sh_stream *s = demux_get_stream(demuxer, i);
if (s->program_id != program_id)
continue;
if (s->type == STREAM_VIDEO) { video_count++; vsh = s; }
else if (s->type == STREAM_AUDIO) { audio_count++; ash = s; }
}
bstr dst = {0};
if (prefix && prefix[0])
bstr_xappend0(ta_ctx, &dst, prefix);
if (video_count == 1) {
bstr_xappend0(ta_ctx, &dst, dst.len ? " (" : "(");
demux_append_codec_desc(ta_ctx, &dst, vsh, prefix);
bstr_xappend0(ta_ctx, &dst, ")");
}
if (audio_count == 1) {
bstr_xappend0(ta_ctx, &dst, dst.len ? " (" : "(");
demux_append_codec_desc(ta_ctx, &dst, ash, prefix);
bstr_xappend0(ta_ctx, &dst, ")");
}
if (!dst.len)
return NULL;
return bstrto0(ta_ctx, dst);
}
static struct sh_stream *demuxer_get_cc_track_locked(struct sh_stream *stream)
{
struct sh_stream *sh = stream->ds->cc;
+14
View File
@@ -370,4 +370,18 @@ bool demux_matroska_uid_cmp(struct matroska_segment_uid *a,
const char *stream_type_name(enum stream_type type);
// Append a codec descriptor for stream `sh` to output string `dst`
// Fields that are unset are skipped. If `skip_dup` is non-NULL, fields whose
// formatted value already appears in it are skipped too.
void demux_append_codec_desc(void *ta_ctx, bstr *dst, struct sh_stream *sh,
const char *skip_dup);
// Compose a descriptive edition title. Walks the demuxer's streams matching
// `program_id`, and when exactly one video / one audio stream is present,
// appends a parenthesized codec descriptor for it. When multiple tracks of a
// a type are present, no descriptor is appended. Returns NULL if
// nothing useful would be produced; otherwise a talloc'd string.
char *demux_compose_edition_title(void *ta_ctx, struct demuxer *demuxer,
int program_id, const char *prefix);
#endif /* MPLAYER_DEMUXER_H */
+25 -34
View File
@@ -995,57 +995,48 @@ static void build_editions(demuxer_t *demuxer)
};
mp_tags_copy_from_av_dictionary(ed.metadata, prog->metadata);
// AVProgram is unlikely to have a "title" tag, but just in case,
// don't override it.
char *title = mp_tags_get_str(ed.metadata, "title");
if (title)
goto done;
char *service_name = mp_tags_get_str(ed.metadata, "service_name");
if (service_name) {
mp_tags_set_str(ed.metadata, "title", service_name);
goto done;
}
// If program/variant has a single video (or audio) stream, use the
// "comment" metadata as the name.
char *rendition_name = NULL;
int video_count = 0, audio_count = 0;
int video_idx = -1, audio_idx = -1;
for (unsigned j = 0; j < prog->nb_stream_indexes; j++) {
unsigned idx = prog->stream_index[j];
if (idx >= priv->num_streams || !priv->streams[idx]->sh)
continue;
if (priv->streams[idx]->sh->type == STREAM_VIDEO) {
struct sh_stream *sh = priv->streams[idx]->sh;
if (sh->type == STREAM_VIDEO) {
video_count++;
video_idx = idx;
} else if (priv->streams[idx]->sh->type == STREAM_AUDIO) {
} else if (sh->type == STREAM_AUDIO) {
audio_count++;
audio_idx = idx;
}
}
int name_idx = video_count == 1 ? video_idx : audio_count == 1 ? audio_idx : -1;
if (name_idx >= 0)
rendition_name = mp_tags_get_str(priv->streams[name_idx]->sh->tags, "comment");
if (rendition_name) {
mp_tags_set_str(ed.metadata, "title", rendition_name);
goto done;
const char *prefix = mp_tags_get_str(ed.metadata, "title");
if (!prefix)
prefix = mp_tags_get_str(ed.metadata, "service_name");
if (!prefix) {
int name_idx = video_count == 1 ? video_idx
: audio_count == 1 ? audio_idx : -1;
if (name_idx >= 0)
prefix = mp_tags_get_str(priv->streams[name_idx]->sh->tags, "comment");
}
char *vb = mp_tags_get_str(ed.metadata, "variant_bitrate");
char *end;
double rate = vb ? strtoll(vb, &end, 10) : 0;
if (rate > 0 && *end == '\0') {
rate /= 1000.0;
if (rate < 1000) {
mp_tags_set_str(ed.metadata, "title", mp_tprintf(42, "Bitrate: %.f kbps", rate));
} else {
mp_tags_set_str(ed.metadata, "title", mp_tprintf(42, "Bitrate: %.3f Mbps", rate / 1000.0));
if (!prefix) {
char *vb = mp_tags_get_str(ed.metadata, "variant_bitrate");
char *end;
double rate = vb ? strtoll(vb, &end, 10) : 0;
if (rate > 0 && *end == '\0') {
rate /= 1000.0;
prefix = rate < 1000
? mp_tprintf(42, "Bitrate: %.f kbps", rate)
: mp_tprintf(42, "Bitrate: %.3f Mbps", rate / 1000.0);
}
}
done:
char *title = demux_compose_edition_title(demuxer, demuxer,
prog->id, prefix);
if (title)
mp_tags_set_str(ed.metadata, "title", title);
MP_TARRAY_APPEND(demuxer, demuxer->editions, demuxer->num_editions, ed);
}
+9 -10
View File
@@ -667,22 +667,21 @@ static void build_editions(struct demuxer *demuxer)
.metadata = talloc_zero(demuxer, struct mp_tags),
};
// Use title from the first video stream in this program as edition
// title, falling back to any stream with a title.
const char *fallback_title = NULL;
const char *prefix = NULL;
bool prefix_is_video = false;
for (int i = n; i < num_streams; i++) {
struct sh_stream *s = demux_get_stream(demuxer, i);
if (s->program_id != sh->program_id || !s->title)
continue;
if (s->type == STREAM_VIDEO) {
fallback_title = s->title;
break;
if (!prefix || (!prefix_is_video && s->type == STREAM_VIDEO)) {
prefix = s->title;
prefix_is_video = s->type == STREAM_VIDEO;
}
if (!fallback_title)
fallback_title = s->title;
}
if (fallback_title)
mp_tags_set_str(ed.metadata, "title", fallback_title);
char *title = demux_compose_edition_title(demuxer, demuxer,
sh->program_id, prefix);
if (title)
mp_tags_set_str(ed.metadata, "title", title);
MP_TARRAY_APPEND(demuxer, demuxer->editions, demuxer->num_editions, ed);
}
+4 -20
View File
@@ -366,31 +366,15 @@ char *mp_format_track_metadata(void *ctx, struct track *t, bool add_lang)
if (t->title)
bstr_xappend_asprintf(ctx, &dst, "'%s' ", t->title);
const char *codec = s ? s->codec->codec : NULL;
bstr_xappend0(ctx, &dst, "(");
if (add_lang && t->lang)
bstr_xappend_asprintf(ctx, &dst, "%s ", t->lang);
bstr_xappend0(ctx, &dst, codec ? codec : "<unknown>");
if (s && s->codec->codec_profile)
bstr_xappend_asprintf(ctx, &dst, " [%s]", s->codec->codec_profile);
if (s && s->codec->disp_w)
bstr_xappend_asprintf(ctx, &dst, " %dx%d", s->codec->disp_w, s->codec->disp_h);
if (s && s->codec->fps && !t->image) {
char *fps = mp_format_double(ctx, s->codec->fps, 4, false, false, true);
bstr_xappend_asprintf(ctx, &dst, " %s fps", fps);
}
if (s && s->codec->channels.num)
bstr_xappend_asprintf(ctx, &dst, " %dch", s->codec->channels.num);
if (s && s->codec->samplerate)
bstr_xappend_asprintf(ctx, &dst, " %d Hz", s->codec->samplerate);
if (s && s->codec->bitrate > 0 && s->codec->bitrate < INT_MAX - 500) {
bstr_xappend_asprintf(ctx, &dst, " %d kbps", (s->codec->bitrate + 500) / 1000);
} else if (s && s->hls_bitrate > 0 && s->hls_bitrate < INT_MAX - 500) {
bstr_xappend_asprintf(ctx, &dst, " %d kbps", (s->hls_bitrate + 500) / 1000);
if (s) {
demux_append_codec_desc(ctx, &dst, s, NULL);
} else {
bstr_xappend0(ctx, &dst, "<unknown>");
}
bstr_xappend0(ctx, &dst, ")");