vaapidecode: fix decoder flush.

There are situations where gst_video_decoder_flush() is called, and
this subsequently produces a gst_video_decoder_reset() that kills the
currently active GstVideoCodecFrame. This means that it no longer
exists by the time we reach GstVideoDecoder::finish() callback, thus
possibly resulting in a crash if we assumed spare data was still
available for decode (current_frame_size > 0).

Try to honour GstVideoDecoder::reset() behaviour from GStreamer 1.0
that means a flush, thus performing the actual operations there like
calling gst_video_decoder_have_frame() if pending data is available.
This commit is contained in:
Gwenole Beauchesne 2013-11-21 11:01:41 +01:00
parent af4785b722
commit a6436f27d5

View file

@ -446,20 +446,40 @@ gst_vaapidecode_decode_loop(GstVaapiDecode *decode)
gst_pad_pause_task(decode->srcpad);
}
static GstFlowReturn
gst_vaapidecode_finish(GstVideoDecoder *vdec)
static gboolean
gst_vaapidecode_flush(GstVideoDecoder *vdec)
{
GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
GstVaapiDecoderStatus status;
/* If there is something in GstVideoDecoder's output adapter, then
submit the frame for decoding */
if (decode->current_frame_size)
if (decode->current_frame_size) {
gst_video_decoder_have_frame(vdec);
decode->current_frame_size = 0;
}
status = gst_vaapi_decoder_flush(decode->decoder);
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
goto error_flush;
return TRUE;
/* ERRORS */
error_flush:
{
GST_ERROR("failed to flush decoder (status %d)", status);
return FALSE;
}
}
static GstFlowReturn
gst_vaapidecode_finish(GstVideoDecoder *vdec)
{
GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
GstFlowReturn ret = GST_FLOW_OK;
if (!gst_vaapidecode_flush(vdec))
ret = GST_FLOW_OK;
/* Make sure the decode loop function has a chance to return, thus
possibly unlocking gst_video_decoder_finish_frame() */
@ -470,14 +490,7 @@ gst_vaapidecode_finish(GstVideoDecoder *vdec)
g_mutex_unlock(&decode->decoder_mutex);
gst_pad_stop_task(decode->srcpad);
GST_VIDEO_DECODER_STREAM_LOCK(vdec);
return GST_FLOW_OK;
/* ERRORS */
error_flush:
{
GST_ERROR("failed to flush decoder (status %d)", status);
return GST_FLOW_EOS;
}
return ret;
}
#if GST_CHECK_VERSION(1,0,0)
@ -653,6 +666,9 @@ gst_vaapidecode_reset_full(GstVaapiDecode *decode, GstCaps *caps, gboolean hard)
decode->has_texture_upload_meta = FALSE;
/* Reset tracked frame size */
decode->current_frame_size = 0;
/* Reset timers if hard reset was requested (e.g. seek) */
if (hard) {
GstVideoDecoder * const vdec = GST_VIDEO_DECODER(decode);
@ -681,9 +697,6 @@ gst_vaapidecode_reset_full(GstVaapiDecode *decode, GstCaps *caps, gboolean hard)
return TRUE;
}
/* Reset tracked frame size */
decode->current_frame_size = 0;
gst_vaapidecode_destroy(decode);
return gst_vaapidecode_create(decode, caps);
}
@ -740,6 +753,9 @@ gst_vaapidecode_reset(GstVideoDecoder *vdec, gboolean hard)
{
GstVaapiDecode * const decode = GST_VAAPIDECODE(vdec);
/* In GStreamer 1.0 context, this means a flush */
if (decode->decoder && !hard && !gst_vaapidecode_flush(vdec))
return FALSE;
return gst_vaapidecode_reset_full(decode, decode->sinkpad_caps, hard);
}