diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 1 | ||||
-rw-r--r-- | demux/demux_edl.c | 64 | ||||
-rw-r--r-- | demux/demux_timeline.c | 107 | ||||
-rw-r--r-- | demux/timeline.h | 8 |
4 files changed, 147 insertions, 33 deletions
diff --git a/demux/demux.c b/demux/demux.c index 2ecfc96ba2..88c4374e63 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -953,6 +953,7 @@ static void add_missing_streams(struct demux_internal *in, // Allocate a new sh_stream of the given type. It either has to be released // with talloc_free(), or added to a demuxer with demux_add_sh_stream(). You // cannot add or read packets from the stream before it has been added. +// type may be changed later, but only before demux_add_sh_stream(). struct sh_stream *demux_alloc_sh_stream(enum stream_type type) { struct sh_stream *sh = talloc_ptrtype(NULL, sh); diff --git a/demux/demux_edl.c b/demux/demux_edl.c index 6e1af373ee..e71c1dfd99 100644 --- a/demux/demux_edl.c +++ b/demux/demux_edl.c @@ -21,6 +21,7 @@ #include <stdbool.h> #include <string.h> #include <inttypes.h> +#include <limits.h> #include <math.h> #include "mpv_talloc.h" @@ -47,8 +48,9 @@ struct tl_part { struct tl_parts { bool disable_chapters; - bool dash, no_clip; + bool dash, no_clip, delay_open; char *init_fragment_url; + struct sh_stream *sh_meta; struct tl_part *parts; int num_parts; struct tl_parts *next; @@ -102,6 +104,24 @@ static char *get_param0(struct parse_ctx *ctx, void *ta_ctx, const char *name) return bstrdup0(ta_ctx, get_param(ctx, name)); } +// Optional int parameter. Returns the parsed integer, or def if the parameter +// is missing or on error (sets ctx.error on error). +static int get_param_int(struct parse_ctx *ctx, const char *name, int def) +{ + bstr val = get_param(ctx, name); + if (val.start) { + bstr rest; + long long ival = bstrtoll(val, &rest, 0); + if (!val.len || rest.len || ival < INT_MIN || ival > INT_MAX) { + MP_ERR(ctx, "Invalid integer: '%.*s'\n", BSTR_P(val)); + ctx->error = true; + return def; + } + return ival; + } + return def; +} + // Optional time parameter. Currently a number. // Returns true: parameter was present and valid, *t is set // Returns false: parameter was not present (or broken => ctx.error set) @@ -125,6 +145,8 @@ 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; } @@ -200,6 +222,30 @@ static struct tl_root *parse_edl(bstr str, struct mp_log *log) tl = add_part(root); } 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; + 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; + bstr mt = get_param(&ctx, "media_type"); + if (bstr_equals0(mt, "video")) { + sh->type = sh->codec->type = STREAM_VIDEO; + } else if (bstr_equals0(mt, "audio")) { + sh->type = sh->codec->type = STREAM_AUDIO; + } else if (bstr_equals0(mt, "sub")) { + sh->type = sh->codec->type = STREAM_SUB; + } else { + mp_err(log, "Invalid or missing !delay_open media type.\n"); + goto error; + } + sh->codec->codec = get_param0(&ctx, sh, "codec"); + if (!sh->codec->codec) + sh->codec->codec = "null"; + sh->codec->disp_w = get_param_int(&ctx, "w", 0); + sh->codec->disp_h = get_param_int(&ctx, "h", 0); + tl->delay_open = true; } else { mp_err(log, "Unknown header: '%.*s'\n", BSTR_P(f_type)); goto error; @@ -322,6 +368,11 @@ static struct timeline_par *build_timeline(struct timeline *root, tl->track_layout = NULL; tl->dash = parts->dash; tl->no_clip = parts->no_clip; + 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; if (parts->init_fragment_url && parts->init_fragment_url[0]) { MP_VERBOSE(root, "Opening init fragment...\n"); @@ -366,6 +417,15 @@ static struct timeline_par *build_timeline(struct timeline *root, if (!tl->track_layout) tl->track_layout = open_source(root, tl, part->filename); + } else if (tl->delay_open) { + if (n == 0 && !part->offset_set) { + part->offset = starttime; + part->offset_set = true; + } + if (part->chapter_ts || (part->length < 0 && !tl->no_clip)) { + MP_ERR(root, "Invalid specification for delay_open stream.\n"); + goto error; + } } else { MP_VERBOSE(root, "Opening segment %d...\n", n); @@ -444,7 +504,7 @@ static struct timeline_par *build_timeline(struct timeline *root, } } - if (!tl->track_layout) + if (!tl->track_layout && !tl->delay_open) goto error; if (!root->meta) root->meta = tl->track_layout; diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c index cdad24f02d..a9a762d3e4 100644 --- a/demux/demux_timeline.c +++ b/demux/demux_timeline.c @@ -56,7 +56,7 @@ struct virtual_stream { struct virtual_source { struct timeline_par *tl; - bool dash, no_clip; + bool dash, no_clip, delay_open; struct segment **segments; int num_segments; @@ -204,11 +204,15 @@ static void reopen_lazy_segments(struct demuxer *demuxer, if (src->current->d) return; - close_lazy_segments(demuxer, src); + // Note: in delay_open mode, we must _not_ close segments during demuxing, + // because demuxed packets have demux_packet.codec set to objects owned + // by the segments. Closing them would create dangling pointers. + if (!src->delay_open) + close_lazy_segments(demuxer, src); struct demuxer_params params = { .init_fragment = src->tl->init_fragment, - .skip_lavf_probing = true, + .skip_lavf_probing = src->tl->dash, .stream_flags = demuxer->stream_origin, }; src->current->d = demux_open_url(src->current->url, ¶ms, @@ -309,10 +313,12 @@ static bool do_read_next_packet(struct demuxer *demuxer, if (pkt->stream < 0 || pkt->stream >= seg->num_stream_map) goto drop; - if (!src->no_clip) { + if (!src->no_clip || src->delay_open) { pkt->segmented = true; if (!pkt->codec) pkt->codec = demux_get_stream(seg->d, pkt->stream)->codec; + } + if (!src->no_clip) { if (pkt->start == MP_NOPTS_VALUE || pkt->start < seg->start) pkt->start = seg->start; if (pkt->end == MP_NOPTS_VALUE || pkt->end > seg->end) @@ -495,6 +501,27 @@ static void print_timeline(struct demuxer *demuxer) MP_VERBOSE(demuxer, "Total duration: %f\n", p->duration); } +// Copy various (not all) metadata fields from src to dst, but try not to +// overwrite fields in dst that are unset in src. +// May keep data from src by reference. +// Imperfect and arbitrary, only suited for EDL stuff. +static void apply_meta(struct sh_stream *dst, struct sh_stream *src) +{ + if (src->demuxer_id >= 0) + dst->demuxer_id = src->demuxer_id; + if (src->title) + dst->title = src->title; + if (src->lang) + dst->lang = src->lang; + dst->default_track = src->default_track; + dst->forced_track = src->forced_track; + if (src->hls_bitrate) + dst->hls_bitrate = src->hls_bitrate; + dst->missing_timestamps = src->missing_timestamps; + if (src->attached_picture) + dst->attached_picture = src->attached_picture; +} + static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) { struct priv *p = demuxer->priv; @@ -503,11 +530,12 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) *src = (struct virtual_source){ .tl = tl, .dash = tl->dash, + .delay_open = tl->delay_open, .no_clip = tl->no_clip || tl->dash, .dts = MP_NOPTS_VALUE, }; - if (!tl->num_parts || !tl->track_layout) + if (!tl->num_parts) return false; MP_TARRAY_APPEND(p, p->sources, p->num_sources, src); @@ -516,19 +544,32 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) struct demuxer *meta = tl->track_layout; - int num_streams = demux_get_num_stream(meta); + // delay_open streams normally have meta==NULL, and 1 virtual stream + int num_streams = 0; + if (tl->delay_open) { + num_streams = 1; + } else if (meta) { + num_streams = demux_get_num_stream(meta); + } for (int n = 0; n < num_streams; n++) { - struct sh_stream *sh = demux_get_stream(meta, n); - struct sh_stream *new = demux_alloc_sh_stream(sh->type); - new->demuxer_id = sh->demuxer_id; - new->codec = sh->codec; - new->title = sh->title; - new->lang = sh->lang; - new->default_track = sh->default_track; - new->forced_track = sh->forced_track; - new->hls_bitrate = sh->hls_bitrate; - new->missing_timestamps = sh->missing_timestamps; - new->attached_picture = sh->attached_picture; + 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; + demuxer->is_network = true; + demuxer->is_streaming = true; + } else { + struct sh_stream *sh = demux_get_stream(meta, n); + new = demux_alloc_sh_stream(sh->type); + apply_meta(new, sh); + new->codec = sh->codec; + } + + 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){ @@ -550,6 +591,9 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) demuxer->is_streaming |= part->source->is_streaming; } + if (!part->source) + assert(tl->dash || tl->delay_open); + struct segment *seg = talloc_ptrtype(src, seg); *seg = (struct segment){ .d = part->source, @@ -566,8 +610,10 @@ static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl) MP_TARRAY_APPEND(src, src->segments, src->num_segments, seg); } - demuxer->is_network |= tl->track_layout->is_network; - demuxer->is_streaming |= tl->track_layout->is_streaming; + if (tl->track_layout) { + demuxer->is_network |= tl->track_layout->is_network; + demuxer->is_streaming |= tl->track_layout->is_streaming; + } return true; } @@ -582,14 +628,14 @@ static int d_open(struct demuxer *demuxer, enum demux_check check) demuxer->num_chapters = p->tl->num_chapters; struct demuxer *meta = p->tl->meta; - if (!meta) - return -1; - demuxer->metadata = meta->metadata; - demuxer->attachments = meta->attachments; - demuxer->num_attachments = meta->num_attachments; - demuxer->editions = meta->editions; - demuxer->num_editions = meta->num_editions; - demuxer->edition = meta->edition; + if (meta) { + demuxer->metadata = meta->metadata; + demuxer->attachments = meta->attachments; + demuxer->num_attachments = meta->num_attachments; + demuxer->editions = meta->editions; + demuxer->num_editions = meta->num_editions; + demuxer->edition = meta->edition; + } for (int n = 0; n < p->tl->num_pars; n++) { if (!add_tl(demuxer, p->tl->pars[n])) @@ -609,9 +655,10 @@ static int d_open(struct demuxer *demuxer, enum demux_check check) demuxer->seekable = true; demuxer->partially_seekable = false; - demuxer->filetype = talloc_asprintf(p, "%s/%s", - p->tl->format, - meta->filetype ? meta->filetype : meta->desc->name); + const char *format_name = "unknown"; + if (meta) + format_name = meta->filetype ? meta->filetype : meta->desc->name; + demuxer->filetype = talloc_asprintf(p, "%s/%s", p->tl->format, format_name); reselect_streams(demuxer); diff --git a/demux/timeline.h b/demux/timeline.h index c8151a0606..faeec53b32 100644 --- a/demux/timeline.h +++ b/demux/timeline.h @@ -1,6 +1,8 @@ #ifndef MP_TIMELINE_H_ #define MP_TIMELINE_H_ +#include "common/common.h" + // Single segment in a timeline. struct timeline_part { // (end time must match with start time of the next part) @@ -20,7 +22,11 @@ struct timeline_part { // "par" is short for parallel stream. struct timeline_par { bstr init_fragment; - bool dash, no_clip; + 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; // Segments to play, ordered by time. struct timeline_part *parts; |