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:
1 parent
a42b1b9103
commit
91b6d8fdd1
5 files changed
+122
-64
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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, ")");
|
||||
|
||||
|
||||
Reference in new issue
Block a user