diff --git a/DOCS/edl-mpv.rst b/DOCS/edl-mpv.rst index ec60d09ccc..ac85f427d0 100644 --- a/DOCS/edl-mpv.rst +++ b/DOCS/edl-mpv.rst @@ -214,6 +214,16 @@ It provides following parameters change track metadata: ``byterate`` Number of bytes per second this stream uses. (Purely informational.) +``index`` + The numeric index of the track this should map to (default: -1). This is + the 0-based index of the virtual stream as seen by the player, enumerating + all audio/video/subtitle streams. If nothing matches, this is silently + discarded. The special index -1 (the default) has two meanings: if there + was a previous meta data entry (either ``!track_meta`` or ``!delay_open`` + element since the last ``!new_stream``), then this element manipulates + the previous meta data entry. If there was no previous entry, a new meta + data entry that matches all streams is created. + Example:: # mpv EDL v0 @@ -244,6 +254,12 @@ Using multiple segments requires you to specify all offsets and durations (also it was never tested whether it works at all). Interaction with ``mp4_dash`` may be strange. +You can describe multiple sub-tracks by using multiple ``delay_open`` headers +before the same source URL. (If there are multiple sub-tracks of the same media +type, then the mapping to the real stream is probably rather arbitrary.) If the +source contains tracks not described, a warning is logged when the delayed +opening happens, and the track is hidden. + This has the following parameters: ``media_type`` diff --git a/demux/demux_edl.c b/demux/demux_edl.c index e71c1dfd99..f72aeb2c03 100644 --- a/demux/demux_edl.c +++ b/demux/demux_edl.c @@ -50,7 +50,8 @@ struct tl_parts { bool disable_chapters; bool dash, no_clip, delay_open; char *init_fragment_url; - struct sh_stream *sh_meta; + struct sh_stream **sh_meta; + int num_sh_meta; struct tl_part *parts; int num_parts; struct tl_parts *next; @@ -145,12 +146,22 @@ static bool get_param_time(struct parse_ctx *ctx, const char *name, double *t) static struct tl_parts *add_part(struct tl_root *root) { struct tl_parts *tl = talloc_zero(root, struct tl_parts); - tl->sh_meta = demux_alloc_sh_stream(STREAM_TYPE_COUNT); - talloc_steal(tl, tl->sh_meta); MP_TARRAY_APPEND(root, root->pars, root->num_pars, tl); return tl; } +static struct sh_stream *get_meta(struct tl_parts *tl, int index) +{ + for (int n = 0; n < tl->num_sh_meta; n++) { + if (tl->sh_meta[n]->index == index) + return tl->sh_meta[n]; + } + struct sh_stream *sh = demux_alloc_sh_stream(STREAM_TYPE_COUNT); + talloc_steal(tl, sh); + MP_TARRAY_APPEND(tl, tl->sh_meta, tl->num_sh_meta, sh); + return sh; +} + /* Returns a list of parts, or NULL on parse error. * Syntax (without file header or URI prefix): * url ::= ( (';' | '\n') )* @@ -223,12 +234,15 @@ static struct tl_root *parse_edl(bstr str, struct mp_log *log) } else if (bstr_equals0(f_type, "no_chapters")) { tl->disable_chapters = true; } else if (bstr_equals0(f_type, "track_meta")) { - struct sh_stream *sh = tl->sh_meta; + int index = get_param_int(&ctx, "index", -1); + struct sh_stream *sh = index < 0 && tl->num_sh_meta + ? tl->sh_meta[tl->num_sh_meta - 1] + : get_meta(tl, index); sh->lang = get_param0(&ctx, sh, "lang"); sh->title = get_param0(&ctx, sh, "title"); sh->hls_bitrate = get_param_int(&ctx, "byterate", 0) * 8; } else if (bstr_equals0(f_type, "delay_open")) { - struct sh_stream *sh = tl->sh_meta; + struct sh_stream *sh = get_meta(tl, tl->num_sh_meta); bstr mt = get_param(&ctx, "media_type"); if (bstr_equals0(mt, "video")) { sh->type = sh->codec->type = STREAM_VIDEO; @@ -371,8 +385,12 @@ static struct timeline_par *build_timeline(struct timeline *root, tl->delay_open = parts->delay_open; // There is no copy function for sh_stream, so just steal it. - tl->sh_meta = talloc_steal(tl, parts->sh_meta); - parts->sh_meta = NULL; + for (int n = 0; n < parts->num_sh_meta; n++) { + MP_TARRAY_APPEND(tl, tl->sh_meta, tl->num_sh_meta, + talloc_steal(tl, parts->sh_meta[n])); + parts->sh_meta[n] = NULL; + } + parts->num_sh_meta = 0; if (parts->init_fragment_url && parts->init_fragment_url[0]) { MP_VERBOSE(root, "Opening init fragment...\n"); diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c index ab6d29b538..eb923d92e4 100644 --- a/demux/demux_timeline.c +++ b/demux/demux_timeline.c @@ -528,6 +528,17 @@ static void apply_meta(struct sh_stream *dst, struct sh_stream *src) dst->attached_picture = src->attached_picture; } +// This is mostly for EDL user-defined metadata. +static struct sh_stream *find_matching_meta(struct timeline_par *tl, int index) +{ + for (int n = 0; n < tl->num_sh_meta; n++) { + struct sh_stream *sh = tl->sh_meta[n]; + if (sh->index == index || sh->index < 0) + return sh; + } + return NULL; +} + static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) { struct priv *p = demuxer->priv; @@ -553,7 +564,7 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) // delay_open streams normally have meta==NULL, and 1 virtual stream int num_streams = 0; if (tl->delay_open) { - num_streams = 1; + num_streams = tl->num_sh_meta; } else if (meta) { num_streams = demux_get_num_stream(meta); } @@ -561,9 +572,10 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) struct sh_stream *new = NULL; if (tl->delay_open) { - assert(tl->sh_meta); - new = demux_alloc_sh_stream(tl->sh_meta->type); - new->codec = tl->sh_meta->codec; + struct sh_stream *tsh = tl->sh_meta[n]; + new = demux_alloc_sh_stream(tsh->type); + new->codec = tsh->codec; + apply_meta(new, tsh); demuxer->is_network = true; demuxer->is_streaming = true; } else { @@ -571,11 +583,11 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) new = demux_alloc_sh_stream(sh->type); apply_meta(new, sh); new->codec = sh->codec; + struct sh_stream *tsh = find_matching_meta(tl, n); + if (tsh) + apply_meta(new, tsh); } - if (tl->sh_meta) - apply_meta(new, tl->sh_meta); - demux_add_sh_stream(demuxer, new); struct virtual_stream *vs = talloc_ptrtype(p, vs); *vs = (struct virtual_stream){ diff --git a/demux/timeline.h b/demux/timeline.h index faeec53b32..93919a5c51 100644 --- a/demux/timeline.h +++ b/demux/timeline.h @@ -24,9 +24,11 @@ struct timeline_par { bstr init_fragment; bool dash, no_clip, delay_open; - // If non-NULL, _some_ fields are used. If delay_open==true, this must be - // set, and the codec info is used. - struct sh_stream *sh_meta; + // Of any of these, _some_ fields are used. If delay_open==true, this + // describes each sub-track, and the codec info is used. + // In both cases, the metadata is mapped to actual tracks in specific ways. + struct sh_stream **sh_meta; + int num_sh_meta; // Segments to play, ordered by time. struct timeline_part *parts;