From 78bcbe289e3c35696e193be16a547095c77c628a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 6 Aug 2016 19:24:25 +0200 Subject: [PATCH] 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 ++++++++++++++++++++++++++++++++++----------- demux/demux.h | 6 ------ demux/demux_lavf.c | 1 - demux/demux_mkv.c | 1 - 4 files changed, 34 insertions(+), 19 deletions(-) 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; diff --git a/demux/demux.h b/demux/demux.h index 8c07fe8933..8470047f9d 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -178,12 +178,6 @@ typedef struct demuxer { double start_time; // File format allows PTS resets (even if the current file is without) bool ts_resets_possible; - // Enable fast track switching hacks. This requires from the demuxer: - // - seeking is somewhat reliable; packet contents must not change - // - packet position (demux_packet.pos) is set, not negative, unique, and - // monotonically increasing - // - seeking leaves packet positions invariant - bool allow_refresh_seeks; // The file data was fully read, and there is no need to keep the stream // open, keep the cache active, or to run the demuxer thread. Generating // packets is not slow either (unlike e.g. libavdevice pseudo-demuxers). diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index e2221135f4..a38fb60435 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -865,7 +865,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ? 0 : (double)priv->avfc->start_time / AV_TIME_BASE; - demuxer->allow_refresh_seeks = matches_avinputformat_name(priv, "mp4"); demuxer->fully_read = priv->format_hack.fully_read; return 0; diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c index f454f5fd20..6f45b5a5f2 100644 --- a/demux/demux_mkv.c +++ b/demux/demux_mkv.c @@ -1927,7 +1927,6 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check) process_tags(demuxer); display_create_tracks(demuxer); add_coverart(demuxer); - demuxer->allow_refresh_seeks = true; probe_first_timestamp(demuxer); if (opts->demux_mkv->probe_duration)