summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-05-25 15:40:22 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:04 +0200
commitf24ff0e9484bc0b38cdcd7137c7b41195620439b (patch)
tree76c56791c5eae8afe1c6572f8f5d04d59a87bf35
parenta84c4de31f32dd46aa5b57b072ee32b913f5d80a (diff)
downloadmpv-f24ff0e9484bc0b38cdcd7137c7b41195620439b.tar.bz2
mpv-f24ff0e9484bc0b38cdcd7137c7b41195620439b.tar.xz
demux: add an explicit start state for backward demuxing
Yay, more subtle state on top of this nightmarish, fragile state machine. But this is what happens when you subvert the laws of nature. This simple checks where playback should "resume" from when no packets were returned to the decoder yet after the seek that initiated backward playback. The main purpose is to process the first returned keyframe range in the same way like all other ranges. This ensures that things like preroll are included properly. Before this commit, it could for example have happened that the start of the first audio frame was slightly broken, because no preroll was included. Since the audio frame is reversed before sending it to the audio output, it would have added an audible discontinuity before the second frame was played; all subsequent frames would have been fine. (Although I didn't test and confirm this particular issue.) In future, this could be useful for certain other things. At least the condition for delaying the backstep seek becomes simpler and more explicit. Move the code that attempts to start demuxing up in dequeue_packet. Before, it was not called when the stream was in back_restarting state. This commit makes streams be in back_restarting state at initialization, so the demuxer would never have started reading. Likewise, we need to call back_demux_see_packets() right after seek in case the seek was within the cache. (We don't bother with checking whether it was a cached seek; nothing happens if it was a normal one.) There is nothing else that would process these cached packets explicitly, although coincidences could sporadically trigger it. The check for back_restart_next in find_backward_restart_pos() now decides whether to use this EOF special code. Since the backward playback start state also sets this variable, we don't need some of the complex checks in dequeue_packet() anymore either.
-rw-r--r--demux/demux.c68
1 files changed, 48 insertions, 20 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 028f6dcef3..92f2eaacc4 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -350,6 +350,7 @@ struct demux_stream {
int64_t back_restart_pos;
double back_restart_dts;
bool back_restart_eof; // restart position is at EOF; overrides pos/dts
+ bool back_restart_next; // restart before next keyframe; overrides above
bool back_restarting; // searching keyframe before restart pos
// Current PTS lower bound for back demuxing.
double back_seek_pos;
@@ -757,6 +758,7 @@ static void ds_clear_reader_state(struct demux_stream *ds,
ds->back_restart_pos = -1;
ds->back_restart_dts = MP_NOPTS_VALUE;
ds->back_restart_eof = false;
+ ds->back_restart_next = false;
ds->back_restarting = false;
ds->back_seek_pos = MP_NOPTS_VALUE;
ds->back_resume_pos = -1;
@@ -1287,6 +1289,30 @@ static void find_backward_restart_pos(struct demux_stream *ds)
// If this is NULL, look for EOF (resume from very last keyframe).
struct demux_packet *back_restart = NULL;
+ if (ds->back_restart_next) {
+ // Initial state. Switch to one of the other modi.
+
+ for (struct demux_packet *cur = first; cur; cur = cur->next) {
+ // Restart for next keyframe after reader_head.
+ if (cur != first && cur->keyframe) {
+ ds->back_restart_dts = cur->dts;
+ ds->back_restart_pos = cur->pos;
+ ds->back_restart_eof = false;
+ ds->back_restart_next = false;
+ break;
+ }
+ }
+
+ if (ds->back_restart_next && ds->eof) {
+ // Restart from end if nothing was found.
+ ds->back_restart_eof = true;
+ ds->back_restart_next = false;
+ }
+
+ if (ds->back_restart_next)
+ return;
+ }
+
if (ds->back_restart_eof) {
// We're trying to find EOF (without discarding packets). Only continue
// if we really reach EOF.
@@ -1424,7 +1450,7 @@ resume_earlier:
if (ds2 == ds || !ds2->eager)
continue;
- if (!ds2->reader_head && !ds2->back_resuming && !ds2->back_restarting) {
+ if (ds2->back_restarting && ds2->back_restart_next) {
MP_VERBOSE(in, "delaying stream %d for %d\n", ds->index, ds2->index);
return;
}
@@ -1476,14 +1502,8 @@ static void step_backwards(struct demux_stream *ds)
assert(!ds->back_restarting);
ds->back_restarting = true;
-
- // No valid restart pos, but EOF reached -> find last restart pos before EOF.
- ds->back_restart_eof = ds->back_restart_dts == MP_NOPTS_VALUE &&
- ds->back_restart_pos < 0 &&
- ds->eof;
-
- if (ds->back_restart_eof)
- MP_VERBOSE(in, "backward eof on stream %d\n", ds->index);
+ ds->back_restart_eof = false;
+ ds->back_restart_next = false;
// Move to start of queue. This is inefficient, because we need to iterate
// the entire fucking packet queue just to update the fw_* stats. But as
@@ -2238,6 +2258,12 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
if (in->blocked)
return 0;
+ if (ds->eager) {
+ in->reading = true; // enable readahead
+ in->eof = false; // force retry
+ pthread_cond_signal(&in->wakeup); // possibly read more
+ }
+
if (ds->back_resuming || ds->back_restarting) {
assert(in->back_demuxing);
return 0;
@@ -2256,12 +2282,6 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
return 1;
}
- if (ds->eager) {
- in->reading = true; // enable readahead
- in->eof = false; // force retry
- pthread_cond_signal(&in->wakeup); // possibly read more
- }
-
bool eof = !ds->reader_head && ds->eof;
if (in->back_demuxing) {
@@ -2270,10 +2290,8 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res)
return -1;
// Next keyframe (or EOF) was reached => step back.
- if ((ds->back_range_started && !ds->back_range_min &&
- ((ds->reader_head && ds->reader_head->keyframe) || eof)) ||
- (!ds->back_range_started && !ds->back_range_min &&
- !ds->reader_head && eof))
+ if (ds->back_range_started && !ds->back_range_min &&
+ ((ds->reader_head && ds->reader_head->keyframe) || eof))
{
step_backwards(ds);
if (ds->back_restarting)
@@ -3276,12 +3294,22 @@ static bool queue_seek(struct demux_internal *in, double seek_pts, int flags,
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
- if (in->back_demuxing && clear_back_state)
+ if (in->back_demuxing && clear_back_state) {
ds->back_seek_pos = seek_pts;
+ ds->back_restarting = ds->eager;
+ ds->back_restart_next = true;
+ }
wakeup_ds(ds);
}
+ if (in->back_demuxing) {
+ // Process possibly cached packets. Separate from the loop above, since
+ // all flags must be set on all streams before this function is called.
+ for (int n = 0; n < in->num_streams; n++)
+ back_demux_see_packets(in->streams[n]->ds);
+ }
+
if (!in->threading && in->seeking)
execute_seek(in);