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)
This commit is contained in:
wm4 2019-12-31 00:15:35 +01:00
parent b721f9d095
commit 3b6c4e7be1
1 changed files with 16 additions and 0 deletions

View File

@ -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);