From 3b6c4e7be1394580472903d8e189aec6de8c03a1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 31 Dec 2019 00:15:35 +0100 Subject: [PATCH] demux: make track switching instant with certain mpegts files When switching tracks, the data for the new track is missing by the amount of data prefetched. This is because all demuxers return interleaved data, and you can't just seek the switched track alone. Normally, this would mean that the new track simply gets no data for a while (e.g. silence if it's an audio track). To avoid this, mpv performs a special "refresh seek" in the demuxer, which tries to resume demuxing from an earlier position, in a way that does not disrupt decoding for the non-changed tracks. (Could write a lot about the reasons for doing something so relatively complex, and the alternatives and their weaknesses, but let's not.) This requires that the demuxer can tell whether a packet after a seek was before or after a previously demuxed packet, sort of like an unique ID. The code can use the byte position (pos) and the DTS for this. The DTS is normally strictly monotonically increasing, the position in most sane file formats too (notably not mp4, in theory). The file at hand had DTS==NOPTS packets (which is fine, usually this happens when PTS can be used instead, but the demux.c code structure doesn't make it easy to use this), and pos==-1 at the same time. The latter is what libavformat likes to return when the packet was produced by a "parser" (or in other words, packets were split or reassembled), and the packet has no real file position. That means the refresh seek mechanism has no packet position and can't work. Fix this by making up a pseudo-position if it's missing. This needs to set the same value every time, which is why it does not work for keyframe packets (which, by definition, could be a seek target). Fixes: #7306 (sort of) --- demux/demux.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/demux/demux.c b/demux/demux.c index d1ff9cc762..c96ff51ca9 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -337,6 +337,7 @@ struct demux_queue { bool correct_dts; // packet DTS is strictly monotonically increasing bool correct_pos; // packet pos is strictly monotonically increasing int64_t last_pos; // for determining correct_pos + int64_t last_pos_fixup; // for filling in unset dp->pos values double last_dts; // for determining correct_dts double last_ts; // timestamp of the last packet added to queue @@ -741,6 +742,7 @@ static void clear_queue(struct demux_queue *queue) queue->correct_dts = queue->correct_pos = true; queue->last_pos = -1; queue->last_ts = queue->last_dts = MP_NOPTS_VALUE; + queue->last_pos_fixup = -1; queue->is_eof = false; queue->is_bof = false; @@ -1761,6 +1763,8 @@ static void attempt_range_joining(struct demux_internal *in) q1->keyframe_latest = q2->keyframe_latest; q1->is_eof = q2->is_eof; + q1->last_pos_fixup = -1; + q2->head = q2->tail = NULL; q2->keyframe_first = NULL; q2->keyframe_latest = NULL; @@ -1996,6 +2000,15 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp) struct demux_queue *queue = ds->queue; bool drop = !ds->selected || in->seeking || ds->sh->attached_picture; + + if (!drop) { + // If libavformat splits packets, some packets will have pos unset, so + // make up one based on the first packet => makes refresh seeks work. + if (dp->pos < 0 && !dp->keyframe && queue->last_pos_fixup >= 0) + dp->pos = queue->last_pos_fixup + 1; + queue->last_pos_fixup = dp->pos; + } + if (!drop && ds->refreshing) { // Resume reading once the old position was reached (i.e. we start // returning packets where we left off before the refresh). @@ -2378,6 +2391,9 @@ static void execute_seek(struct demux_internal *in) !(flags & (SEEK_FORWARD | SEEK_FACTOR)) && pts <= in->d_thread->start_time; + for (int n = 0; n < in->num_streams; n++) + in->streams[n]->ds->queue->last_pos_fixup = -1; + if (in->recorder) mp_recorder_mark_discontinuity(in->recorder);