summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-12-31 00:15:35 +0100
committerwm4 <wm4@nowhere>2019-12-31 00:17:46 +0100
commit3b6c4e7be1394580472903d8e189aec6de8c03a1 (patch)
treea614c5040dc54cd8fd1d12def3c47d23929cc45b
parentb721f9d0957c5c30f86ff485240f68881fc329a6 (diff)
downloadmpv-3b6c4e7be1394580472903d8e189aec6de8c03a1.tar.bz2
mpv-3b6c4e7be1394580472903d8e189aec6de8c03a1.tar.xz
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)
-rw-r--r--demux/demux.c16
1 files changed, 16 insertions, 0 deletions
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);