diff options
Diffstat (limited to 'demux/demux_timeline.c')
-rw-r--r-- | demux/demux_timeline.c | 431 |
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, ¶ms, @@ -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) |