From 78bcbe289e3c35696e193be16a547095c77c628a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 6 Aug 2016 19:24:25 +0200 Subject: demux: make refresh seek handling more generic Remove the explicit whitelisting of formats for refresh seeks. Instead, check whether the packet position is somewhat reliable during demuxing. If there are packets without position, or the packet position is not monotonically increasing, then do not use them for refresh seeks. This does not make sure of some requirements, such as deterministic seeks. If that happens, mpv will mess up a bit on stream switching. Also, add another method that uses DTS to identify packets, and prefer it to the packet position method. Even if there's a demuxer which randomizes packet positions, it hardly can do that with DTS. The DTS method is not always available either, though. Some formats do not have a DTS, and others are not always strictly monotonic (possibly due to libavformat codec parsing and timestamp determination issues). --- demux/demux.c | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) (limited to 'demux/demux.c') diff --git a/demux/demux.c b/demux/demux.c index 9e5d6b2f7e..3d61444ee2 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -158,6 +158,8 @@ struct demux_stream { bool eof; // end of demuxed stream? (true if all buffer empty) bool need_refresh; // enabled mid-stream bool refreshing; + bool correct_dts; // packet DTS is strictly monotonically increasing + bool correct_pos; // packet pos is strictly monotonically increasing size_t packs; // number of packets in buffer size_t bytes; // total bytes of packets in buffer double base_ts; // timestamp of the last packet returned to decoder @@ -166,6 +168,7 @@ struct demux_stream { size_t last_br_bytes; // summed packet sizes since last bitrate calculation double bitrate; int64_t last_pos; + double last_dts; struct demux_packet *head; struct demux_packet *tail; @@ -205,6 +208,8 @@ static void ds_flush(struct demux_stream *ds) ds->refreshing = false; ds->need_refresh = false; ds->last_pos = -1; + ds->last_dts = MP_NOPTS_VALUE; + ds->correct_dts = ds->correct_pos = true; } void demux_set_ts_offset(struct demuxer *demuxer, double offset) @@ -405,14 +410,22 @@ static double get_refresh_seek_pts(struct demux_internal *in) double start_ts = in->ref_pts; bool needed = false; bool normal_seek = true; + bool refresh_possible = true; for (int n = 0; n < in->num_streams; n++) { struct demux_stream *ds = in->streams[n]->ds; + + if (!ds->selected) + continue; + if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO) start_ts = MP_PTS_MIN(start_ts, ds->base_ts); + needed |= ds->need_refresh; // If there were no other streams selected, we can use a normal seek. - normal_seek &= ds->need_refresh || !ds->selected; + normal_seek &= ds->need_refresh; ds->need_refresh = false; + + refresh_possible &= ds->correct_dts || ds->correct_pos; } if (!needed || start_ts == MP_NOPTS_VALUE || !demux->desc->seek || @@ -422,14 +435,16 @@ static double get_refresh_seek_pts(struct demux_internal *in) if (normal_seek) return start_ts; - if (!demux->allow_refresh_seeks) + if (!refresh_possible) { + MP_VERBOSE(in, "can't issue refresh seek\n"); return MP_NOPTS_VALUE; + } for (int n = 0; n < in->num_streams; n++) { struct demux_stream *ds = in->streams[n]->ds; - // Streams which didn't read any packets yet can return all packets, - // or they'd be stuck forever; affects newly selected streams too. - if (ds->last_pos != -1) + // Streams which didn't have any packets yet will return all packets, + // other streams return packets only starting from the last position. + if (ds->last_pos != -1 || ds->last_dts != MP_NOPTS_VALUE) ds->refreshing = true; } @@ -447,13 +462,18 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp) struct demux_internal *in = ds->in; pthread_mutex_lock(&in->lock); - bool drop = false; + bool drop = ds->refreshing; if (ds->refreshing) { // Resume reading once the old position was reached (i.e. we start // returning packets where we left off before the refresh). - drop = dp->pos <= ds->last_pos; - if (dp->pos >= ds->last_pos) - ds->refreshing = false; + // If it's the same position, drop, but continue normally next time. + if (ds->correct_dts) { + ds->refreshing = dp->dts < ds->last_dts; + } else if (ds->correct_pos) { + ds->refreshing = dp->pos < ds->last_pos; + } else { + ds->refreshing = false; // should not happen + } } if (!ds->selected || in->seeking || drop) { @@ -462,10 +482,14 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp) return; } + ds->correct_pos &= dp->pos >= 0 && dp->pos > ds->last_pos; + ds->correct_dts &= dp->dts != MP_NOPTS_VALUE && dp->dts > ds->last_dts; + ds->last_pos = dp->pos; + ds->last_dts = dp->dts; + dp->stream = stream->index; dp->next = NULL; - ds->last_pos = dp->pos; ds->packs++; ds->bytes += dp->len; if (ds->tail) { @@ -970,7 +994,6 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src) dst->partially_seekable = src->partially_seekable; dst->filetype = src->filetype; dst->ts_resets_possible = src->ts_resets_possible; - dst->allow_refresh_seeks = src->allow_refresh_seeks; dst->fully_read = src->fully_read; dst->start_time = src->start_time; dst->priv = src->priv; -- cgit v1.2.3