From 05ae571241a1623ea7b58b7cded2d2988931033b Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 23 Oct 2017 20:09:21 +0200 Subject: [PATCH] demux: fix cached SEEK_FORWARD seeks into end of cached regions/EOF Although seeking past the cached range will trigger a low level seek, a seek into the region between cache end and last video key frame would simply seek to the video key frame. This meant that you could get "stuck" at the end of the file instead of terminating playback when trying to seek past the end. One change is that we fix this by _actually_ allowing SEEK_FORWARD to seek past the last video keyframe in find_seek_target(). In that case, or otherwise seeking to cache buffer end, it could happen that we set ds->reader_head=NULL if the seek target is after the current packet. We allow this, because the end of the cached region is defined by the existence of "any" packet, not necessarily a key frame. Seeking there still makes sense, because we know that there is going to be more packets (or EOF) that satisfy the seek target. The problem is that just resuming demuxing with reader_head==NULL will simply return any packets that come its way, even non-keyframe ones. Some decoders will produce ugly soup in this case. (In practice, this was not a problem, because seeking at the end of the cached region was rare before this commit, and also some decoders like h264 will skip broken frames by default anyway.) So the other change of this commit is to enable key frame skipping. As a nasty implementation detail, we use a separate flag, instead of setting reader_head to the first key frame encounted (reader_head being NULL can happen after a normal seek or on playback start, and then we want to mirror the underlying demuxer behavior, for better or worse). This change is relatively untested, so you get to keep the pieces for yourself. --- demux/demux.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/demux/demux.c b/demux/demux.c index 6e13817b87..cca9078611 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -215,6 +215,7 @@ struct demux_stream { size_t last_br_bytes; // summed packet sizes since last bitrate calculation double bitrate; struct demux_packet *reader_head; // points at current decoder position + bool skip_to_keyframe; bool attached_picture_added; // for closed captions (demuxer_feed_caption) @@ -242,6 +243,7 @@ static void ds_clear_reader_state(struct demux_stream *ds) ds->base_ts = ds->last_br_ts = MP_NOPTS_VALUE; ds->last_br_bytes = 0; ds->bitrate = -1; + ds->skip_to_keyframe = false; ds->attached_picture_added = false; } @@ -621,8 +623,10 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp) } // (keep in mind that even if the reader went out of data, the queue is not // necessarily empty due to the backbuffer) - if (!ds->reader_head) + if (!ds->reader_head && (!ds->skip_to_keyframe || dp->keyframe)) { ds->reader_head = dp; + ds->skip_to_keyframe = false; + } // (In theory it'd be more efficient to make this incremental.) if (ds->back_pts == MP_NOPTS_VALUE && dp->keyframe) @@ -650,7 +654,7 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp) dp->len, dp->pts, dp->dts, dp->pos, ds->fw_packs, ds->fw_bytes); // Wake up if this was the first packet after start/possible underrun. - if (ds->in->wakeup_cb && !ds->reader_head->next) + if (ds->in->wakeup_cb && ds->reader_head && !ds->reader_head->next) ds->in->wakeup_cb(ds->in->wakeup_cb_ctx); pthread_cond_signal(&in->wakeup); pthread_mutex_unlock(&in->lock); @@ -1609,8 +1613,11 @@ static struct demux_packet *find_seek_target(struct demux_stream *ds, continue; double diff = range_pts - pts; - if (flags & SEEK_FORWARD) + if (flags & SEEK_FORWARD) { diff = -diff; + if (diff > 0) + continue; + } if (target_diff != MP_NOPTS_VALUE) { if (diff <= 0) { if (target_diff <= 0 && diff <= target_diff) @@ -1684,6 +1691,7 @@ static bool try_seek_cache(struct demux_internal *in, double pts, int flags) struct demux_packet *target = find_seek_target(ds, pts, flags); ds->reader_head = target; + ds->skip_to_keyframe = !target; recompute_buffers(ds); MP_VERBOSE(in, "seeking stream %d (%s) to ",