mirror of https://github.com/mpv-player/mpv
edl: make it possible to delay-load files with multiple tracks
Until now, delay-loading was for files with single tracks only (basically what DASH and HLS like to expose, so adaptive streaming and codec selection becomes easier - for sites, not for us). But they also provide some interleaved versions, probably for compatibility. Until now, we were forced to eagerly load it (making startup slightly slower). But there is not much missing. We just need a way to provide multiple metadata entries, and use them to represent each track. A side effect is now that the "track_meta" header can be used for normal EDL files too.
This commit is contained in:
parent
6726b7a1ba
commit
6f0297dff4
|
@ -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``
|
||||
|
|
|
@ -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 ::= <entry> ( (';' | '\n') <entry> )*
|
||||
|
@ -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");
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue