mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-13 10:55:34 +00:00
videodecoder: In reverse playback, flush the output queue after decoding each keyframe chain
This fixes the reverse playback scenario when upstream is not fully parsing the stream and does not send every keyframe chain separately with the DISCONT flag on the keyframe. To explain this, let's suppose we have this stream: 0 1 2 3 4 5 6 7 8 K K K In most circumstances, the upstream parser will chain in the decoder the buffers in the following order: 6 7 8 3 4 5 0 1 2 D D D In this case, GstVideoDecoder will flush the parse queue every time it receives discont (D) and we will eventually get in the output queue: (flush here) 8 7 6 (flush here) 5 4 3 (flush here) 2 1 0 In case the upstream parser doesn't do this work, though, GstVideoDecoder will receive the whole stream at once and will flush the parse queue afterwards: 0 1 2 3 4 5 6 7 8 D During the flush, it will look backwards for keyframes and will decode in this order: 6 7 8 3 4 5 0 1 2 This is the same order that it would receive from upstream if upstream was parsing and looking for the keyframes, only that now there is no flushing of the output queue in between keyframes, which will result in the output queue looking like this: 2 1 0 6 5 3 8 7 6 This will confuse downstream obviously and will play incorrectly. This patch forces the decoder to flush the output queue every time it picks a new keyframe to decode, so it will end up decoding 6 7 8 and then flushing before picking 3 for decoding, so the output will get 8 7 6 before 6 5 3 and the video will play back correctly. https://bugzilla.gnome.org/show_bug.cgi?id=734441
This commit is contained in:
parent
f7d6087d53
commit
a4d97f49e2
1 changed files with 46 additions and 42 deletions
|
@ -2039,53 +2039,57 @@ gst_video_decoder_flush_parse (GstVideoDecoder * dec, gboolean at_eos)
|
|||
res = gst_video_decoder_flush_decode (dec);
|
||||
if (res != GST_FLOW_OK)
|
||||
goto done;
|
||||
|
||||
/* We need to tell the subclass to drain now */
|
||||
GST_DEBUG_OBJECT (dec, "Finishing");
|
||||
if (decoder_class->finish)
|
||||
res = decoder_class->finish (dec);
|
||||
|
||||
if (res != GST_FLOW_OK)
|
||||
goto done;
|
||||
|
||||
/* now send queued data downstream */
|
||||
walk = priv->output_queued;
|
||||
while (walk) {
|
||||
GstBuffer *buf = GST_BUFFER_CAST (walk->data);
|
||||
|
||||
if (G_LIKELY (res == GST_FLOW_OK)) {
|
||||
/* avoid stray DISCONT from forward processing,
|
||||
* which have no meaning in reverse pushing */
|
||||
GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
|
||||
|
||||
/* Last chance to calculate a timestamp as we loop backwards
|
||||
* through the list */
|
||||
if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE)
|
||||
priv->last_timestamp_out = GST_BUFFER_TIMESTAMP (buf);
|
||||
else if (priv->last_timestamp_out != GST_CLOCK_TIME_NONE &&
|
||||
GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) {
|
||||
GST_BUFFER_TIMESTAMP (buf) =
|
||||
priv->last_timestamp_out - GST_BUFFER_DURATION (buf);
|
||||
priv->last_timestamp_out = GST_BUFFER_TIMESTAMP (buf);
|
||||
GST_LOG_OBJECT (dec,
|
||||
"Calculated TS %" GST_TIME_FORMAT " working backwards",
|
||||
GST_TIME_ARGS (priv->last_timestamp_out));
|
||||
}
|
||||
|
||||
res = gst_video_decoder_clip_and_push_buf (dec, buf);
|
||||
} else {
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
priv->output_queued =
|
||||
g_list_delete_link (priv->output_queued, priv->output_queued);
|
||||
walk = priv->output_queued;
|
||||
}
|
||||
|
||||
/* clear buffer and decoder state again
|
||||
* before moving to the previous keyframe */
|
||||
gst_video_decoder_flush (dec, FALSE);
|
||||
}
|
||||
|
||||
walk = priv->parse_gather;
|
||||
}
|
||||
|
||||
/* We need to tell the subclass to drain now */
|
||||
GST_DEBUG_OBJECT (dec, "Finishing");
|
||||
if (decoder_class->finish)
|
||||
res = decoder_class->finish (dec);
|
||||
|
||||
if (res != GST_FLOW_OK)
|
||||
goto done;
|
||||
|
||||
/* now send queued data downstream */
|
||||
walk = priv->output_queued;
|
||||
while (walk) {
|
||||
GstBuffer *buf = GST_BUFFER_CAST (walk->data);
|
||||
|
||||
if (G_LIKELY (res == GST_FLOW_OK)) {
|
||||
/* avoid stray DISCONT from forward processing,
|
||||
* which have no meaning in reverse pushing */
|
||||
GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
|
||||
|
||||
/* Last chance to calculate a timestamp as we loop backwards
|
||||
* through the list */
|
||||
if (GST_BUFFER_TIMESTAMP (buf) != GST_CLOCK_TIME_NONE)
|
||||
priv->last_timestamp_out = GST_BUFFER_TIMESTAMP (buf);
|
||||
else if (priv->last_timestamp_out != GST_CLOCK_TIME_NONE &&
|
||||
GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) {
|
||||
GST_BUFFER_TIMESTAMP (buf) =
|
||||
priv->last_timestamp_out - GST_BUFFER_DURATION (buf);
|
||||
priv->last_timestamp_out = GST_BUFFER_TIMESTAMP (buf);
|
||||
GST_LOG_OBJECT (dec,
|
||||
"Calculated TS %" GST_TIME_FORMAT " working backwards",
|
||||
GST_TIME_ARGS (priv->last_timestamp_out));
|
||||
}
|
||||
|
||||
res = gst_video_decoder_clip_and_push_buf (dec, buf);
|
||||
} else {
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
priv->output_queued =
|
||||
g_list_delete_link (priv->output_queued, priv->output_queued);
|
||||
walk = priv->output_queued;
|
||||
}
|
||||
|
||||
done:
|
||||
return res;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue