summaryrefslogtreecommitdiffstats
path: root/demux/demux_timeline.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux_timeline.c')
-rw-r--r--demux/demux_timeline.c431
1 files changed, 248 insertions, 183 deletions
diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c
index ecce06bcb2..5572fb53bf 100644
--- a/demux/demux_timeline.c
+++ b/demux/demux_timeline.c
@@ -51,12 +51,12 @@ struct virtual_stream {
struct virtual_source *src; // group this stream is part of
};
-// This represents a single timeline source. (See timeline.next. For each
-// timeline struct there is a virtual_source.)
+// This represents a single timeline source. (See timeline.pars[]. For each
+// timeline_par struct there is a virtual_source.)
struct virtual_source {
struct timeline_par *tl;
- bool dash, no_clip;
+ bool dash, no_clip, delay_open;
struct segment **segments;
int num_segments;
@@ -78,6 +78,7 @@ struct virtual_source {
struct priv {
struct timeline *tl;
+ bool owns_tl;
double duration;
@@ -89,10 +90,6 @@ struct priv {
int num_sources;
};
-static bool add_tl(struct demuxer *demuxer, struct timeline_par *par);
-static bool do_read_next_packet(struct demuxer *demuxer,
- struct virtual_source *src);
-
static void update_slave_stats(struct demuxer *demuxer, struct demuxer *slave)
{
demux_report_unbuffered_read_bytes(demuxer, demux_get_bytes_read_hack(slave));
@@ -119,6 +116,7 @@ static void associate_streams(struct demuxer *demuxer,
for (int n = 0; n < num_streams; n++) {
struct sh_stream *sh = demux_get_stream(seg->d, n);
struct virtual_stream *other = NULL;
+
for (int i = 0; i < src->num_streams; i++) {
struct virtual_stream *vs = src->streams[i];
@@ -137,6 +135,11 @@ static void associate_streams(struct demuxer *demuxer,
other = vs;
}
+ if (!other) {
+ MP_WARN(demuxer, "Source stream %d (%s) unused and hidden.\n",
+ n, stream_type_name(sh->type));
+ }
+
MP_TARRAY_APPEND(seg, seg->stream_map, seg->num_stream_map, other);
}
}
@@ -207,11 +210,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, &params,
@@ -254,121 +261,17 @@ static void switch_segment(struct demuxer *demuxer, struct virtual_source *src,
src->eos_packets = 0;
}
-static void seek_source(struct demuxer *demuxer, struct virtual_source *src,
- double pts, int flags)
-{
- struct segment *new = src->segments[src->num_segments - 1];
- for (int n = 0; n < src->num_segments; n++) {
- if (pts < src->segments[n]->end) {
- new = src->segments[n];
- break;
- }
- }
-
- switch_segment(demuxer, src, new, pts, flags, false);
-
- src->dts = MP_NOPTS_VALUE;
- TA_FREEP(&src->next);
-}
-
-static void d_seek(struct demuxer *demuxer, double seek_pts, int flags)
-{
- struct priv *p = demuxer->priv;
-
- seek_pts = seek_pts * ((flags & SEEK_FACTOR) ? p->duration : 1);
- flags &= SEEK_FORWARD | SEEK_HR;
-
- // The intention is to seek audio streams to the same target as video
- // streams if they are separate streams. Video streams usually have more
- // coarse keyframe snapping, which could leave video without audio.
- struct virtual_source *master = NULL;
- bool has_slaves = false;
- for (int x = 0; x < p->num_sources; x++) {
- struct virtual_source *src = p->sources[x];
-
- bool any_audio = false, any_video = false;
- for (int i = 0; i < src->num_streams; i++) {
- struct virtual_stream *str = src->streams[i];
- if (str->selected) {
- if (str->sh->type == STREAM_VIDEO)
- any_video = true;
- if (str->sh->type == STREAM_AUDIO)
- any_audio = true;
- }
- }
-
- if (any_video)
- master = src;
- // A true slave stream is audio-only; this also prevents that the master
- // stream is considered a slave stream.
- if (any_audio && !any_video)
- has_slaves = true;
- }
-
- if (!has_slaves)
- master = NULL;
-
- if (master) {
- seek_source(demuxer, master, seek_pts, flags);
- do_read_next_packet(demuxer, master);
- if (master->next && master->next->pts != MP_NOPTS_VALUE) {
- // Assume we got a seek target. Actually apply the heuristic.
- MP_VERBOSE(demuxer, "adjust seek target from %f to %f\n", seek_pts,
- master->next->pts);
- seek_pts = master->next->pts;
- flags &= ~(unsigned)SEEK_FORWARD;
- }
- }
-
- for (int x = 0; x < p->num_sources; x++) {
- struct virtual_source *src = p->sources[x];
- if (src != master)
- seek_source(demuxer, src, seek_pts, flags);
- }
-}
-
-static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt)
-{
- struct priv *p = demuxer->priv;
-
- struct virtual_source *src = NULL;
-
- for (int x = 0; x < p->num_sources; x++) {
- struct virtual_source *cur = p->sources[x];
-
- if (!cur->any_selected || cur->eof_reached)
- continue;
-
- if (!cur->current)
- switch_segment(demuxer, cur, cur->segments[0], 0, 0, true);
-
- if (!cur->any_selected || !cur->current || !cur->current->d)
- continue;
-
- if (!src || cur->dts == MP_NOPTS_VALUE ||
- (src->dts != MP_NOPTS_VALUE && cur->dts < src->dts))
- src = cur;
- }
-
- if (!src)
- return false;
-
- if (!do_read_next_packet(demuxer, src))
- return false;
- *out_pkt = src->next;
- src->next = NULL;
- return true;
-}
-
-static bool do_read_next_packet(struct demuxer *demuxer,
+static void do_read_next_packet(struct demuxer *demuxer,
struct virtual_source *src)
{
if (src->next)
- return 1;
+ return;
struct segment *seg = src->current;
- if (!seg || !seg->d)
- return 0;
+ if (!seg || !seg->d) {
+ src->eof_reached = true;
+ return;
+ }
struct demux_packet *pkt = demux_read_any_packet(seg->d);
if (!pkt || (!src->no_clip && pkt->pts >= seg->end))
@@ -409,19 +312,21 @@ static bool do_read_next_packet(struct demuxer *demuxer,
}
if (!next) {
src->eof_reached = true;
- return false;
+ return;
}
switch_segment(demuxer, src, next, next->start, 0, true);
- return true; // reader will retry
+ return; // reader will retry
}
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)
@@ -455,13 +360,116 @@ static bool do_read_next_packet(struct demuxer *demuxer,
pkt->stream = vs->sh->index;
src->next = pkt;
- return true;
+ return;
drop:
talloc_free(pkt);
+}
+
+static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt)
+{
+ struct priv *p = demuxer->priv;
+ struct virtual_source *src = NULL;
+
+ for (int x = 0; x < p->num_sources; x++) {
+ struct virtual_source *cur = p->sources[x];
+
+ if (!cur->any_selected || cur->eof_reached)
+ continue;
+
+ if (!cur->current)
+ switch_segment(demuxer, cur, cur->segments[0], 0, 0, true);
+
+ if (!cur->any_selected || !cur->current || !cur->current->d)
+ continue;
+
+ if (!src || cur->dts == MP_NOPTS_VALUE ||
+ (src->dts != MP_NOPTS_VALUE && cur->dts < src->dts))
+ src = cur;
+ }
+
+ if (!src)
+ return false;
+
+ do_read_next_packet(demuxer, src);
+ *out_pkt = src->next;
+ src->next = NULL;
return true;
}
+static void seek_source(struct demuxer *demuxer, struct virtual_source *src,
+ double pts, int flags)
+{
+ struct segment *new = src->segments[src->num_segments - 1];
+ for (int n = 0; n < src->num_segments; n++) {
+ if (pts < src->segments[n]->end) {
+ new = src->segments[n];
+ break;
+ }
+ }
+
+ switch_segment(demuxer, src, new, pts, flags, false);
+
+ src->dts = MP_NOPTS_VALUE;
+ TA_FREEP(&src->next);
+}
+
+static void d_seek(struct demuxer *demuxer, double seek_pts, int flags)
+{
+ struct priv *p = demuxer->priv;
+
+ seek_pts = seek_pts * ((flags & SEEK_FACTOR) ? p->duration : 1);
+ flags &= SEEK_FORWARD | SEEK_HR;
+
+ // The intention is to seek audio streams to the same target as video
+ // streams if they are separate streams. Video streams usually have more
+ // coarse keyframe snapping, which could leave video without audio.
+ struct virtual_source *master = NULL;
+ bool has_slaves = false;
+ for (int x = 0; x < p->num_sources; x++) {
+ struct virtual_source *src = p->sources[x];
+
+ bool any_audio = false, any_video = false;
+ for (int i = 0; i < src->num_streams; i++) {
+ struct virtual_stream *str = src->streams[i];
+ if (str->selected) {
+ if (str->sh->type == STREAM_VIDEO)
+ any_video = true;
+ if (str->sh->type == STREAM_AUDIO)
+ any_audio = true;
+ }
+ }
+
+ if (any_video)
+ master = src;
+ // A true slave stream is audio-only; this also prevents that the master
+ // stream is considered a slave stream.
+ if (any_audio && !any_video)
+ has_slaves = true;
+ }
+
+ if (!has_slaves)
+ master = NULL;
+
+ if (master) {
+ seek_source(demuxer, master, seek_pts, flags);
+ do_read_next_packet(demuxer, master);
+ if (master->next && master->next->pts != MP_NOPTS_VALUE) {
+ // Assume we got a seek target. Actually apply the heuristic.
+ MP_VERBOSE(demuxer, "adjust seek target from %f to %f\n", seek_pts,
+ master->next->pts);
+ seek_pts = master->next->pts;
+ flags &= ~(unsigned)SEEK_FORWARD;
+ }
+ }
+
+ for (int x = 0; x < p->num_sources; x++) {
+ struct virtual_source *src = p->sources[x];
+ if (src != master && src->any_selected)
+ seek_source(demuxer, src, seek_pts, flags);
+ }
+}
+
static void print_timeline(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
@@ -498,51 +506,37 @@ static void print_timeline(struct demuxer *demuxer)
MP_VERBOSE(demuxer, "Total duration: %f\n", p->duration);
}
-static int d_open(struct demuxer *demuxer, enum demux_check check)
+// 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)
{
- struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
- p->tl = demuxer->params ? demuxer->params->timeline : NULL;
- if (!p->tl || p->tl->num_pars < 1)
- return -1;
-
- demuxer->chapters = p->tl->chapters;
- 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 (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;
+ dst->image = src->image;
+}
- for (int n = 0; n < p->tl->num_pars; n++) {
- if (!add_tl(demuxer, p->tl->pars[n]))
- return -1;
+// 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;
}
-
- if (!p->num_sources)
- return -1;
-
- demuxer->is_network |= p->tl->is_network;
- demuxer->is_streaming |= p->tl->is_streaming;
-
- demuxer->duration = p->duration;
-
- print_timeline(demuxer);
-
- demuxer->seekable = true;
- demuxer->partially_seekable = false;
-
- demuxer->filetype = talloc_asprintf(p, "%s/%s",
- p->tl->format,
- meta->filetype ? meta->filetype : meta->desc->name);
-
- reselect_streams(demuxer);
-
- return 0;
+ return NULL;
}
static bool add_tl(struct demuxer *demuxer, struct timeline_par *tl)
@@ -553,11 +547,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);
@@ -566,19 +561,33 @@ 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 = tl->num_sh_meta;
+ } 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) {
+ 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 {
+ struct sh_stream *sh = demux_get_stream(meta, n);
+ 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);
+ }
+
demux_add_sh_stream(demuxer, new);
struct virtual_stream *vs = talloc_ptrtype(p, vs);
*vs = (struct virtual_stream){
@@ -600,6 +609,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,
@@ -616,11 +628,62 @@ 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;
}
+static int d_open(struct demuxer *demuxer, enum demux_check check)
+{
+ struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
+ p->tl = demuxer->params ? demuxer->params->timeline : NULL;
+ if (!p->tl || p->tl->num_pars < 1)
+ return -1;
+
+ demuxer->chapters = p->tl->chapters;
+ demuxer->num_chapters = p->tl->num_chapters;
+
+ struct demuxer *meta = p->tl->meta;
+ 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]))
+ return -1;
+ }
+
+ if (!p->num_sources)
+ return -1;
+
+ demuxer->is_network |= p->tl->is_network;
+ demuxer->is_streaming |= p->tl->is_streaming;
+
+ demuxer->duration = p->duration;
+
+ print_timeline(demuxer);
+
+ demuxer->seekable = true;
+ demuxer->partially_seekable = false;
+
+ 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);
+
+ p->owns_tl = true;
+ return 0;
+}
+
static void d_close(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
@@ -633,9 +696,11 @@ static void d_close(struct demuxer *demuxer)
close_lazy_segments(demuxer, src);
}
- struct demuxer *master = p->tl->demuxer;
- timeline_destroy(p->tl);
- demux_free(master);
+ if (p->owns_tl) {
+ struct demuxer *master = p->tl->demuxer;
+ timeline_destroy(p->tl);
+ demux_free(master);
+ }
}
static void d_switched_tracks(struct demuxer *demuxer)