amcvideodec: releasing output buffer after codec is stopped.
I've found there is a bug in gstamcvideodec.c. This bug caused #1178。
This bug is triggered when adaptivedemux decide to select another bitrate.gst_amc_video_dec_set_format()
was called, which calls gst_amc_video_dec_drain()
and gst_amc_video_dec_stop()
, which in turn stops the codec. But the gl thread is still executing, the function _amc_gl_iterate_queue_unlocked()
calls _gl_sync_release_buffer()
and failed because codec is already stopped.
Function _amc_gl_iterate_queue_unlocked()
is only protected with gl_lock
, but gst_amc_video_dec_set_format()
does not lock gl_lock
。
Any idea to fix this bug?
The code in gst_amc_video_dec_set_format()
:
if (needs_disable && is_format_change) {
gst_amc_video_dec_drain (self);
GST_VIDEO_DECODER_STREAM_UNLOCK (self);
gst_amc_video_dec_stop (GST_VIDEO_DECODER (self));
GST_VIDEO_DECODER_STREAM_LOCK (self);
gst_amc_video_dec_close (GST_VIDEO_DECODER (self));
if (!gst_amc_video_dec_open (GST_VIDEO_DECODER (self))) {
GST_ERROR_OBJECT (self, "Failed to open codec again");
return FALSE;
}
if (!gst_amc_video_dec_start (GST_VIDEO_DECODER (self))) {
GST_ERROR_OBJECT (self, "Failed to start codec again");
}
The code called _amc_gl_iterate_queue_unlocked()
:
static void
_amc_gl_wait_gl (GstGLContext * context, struct gl_wait *wait)
{
struct gl_sync *sync = wait->sync_meta->data;
g_mutex_lock (&sync->sink->gl_lock);
wait->ret = _amc_gl_iterate_queue_unlocked (wait->sync_meta, TRUE);
g_mutex_unlock (&sync->sink->gl_lock);
}
The definitio of _amc_gl_iterate_queue_unlocked()
:
static gboolean
_amc_gl_iterate_queue_unlocked (GstGLSyncMeta * sync_meta, gboolean wait)
{
struct gl_sync *sync = sync_meta->data;
struct gl_sync *tmp;
gboolean ret = TRUE;
gint64 end_time;
while ((tmp = g_queue_peek_head (sync->sink->gl_queue))) {
/* skip frames that are ahead of the current wait frame */
if ((gint) (sync->gl_frame_no - tmp->gl_frame_no) < 0) {
GST_TRACE ("gl_sync %p frame %u is ahead of gl_sync %p frame %u", tmp,
tmp->gl_frame_no, sync, sync->gl_frame_no);
break;
}
_gl_sync_release_buffer (tmp, wait);
/* Frames are currently pushed in order and waits need to be performed
* in the same order */
end_time = wait ? 30 * G_TIME_SPAN_MILLISECOND + tmp->released_ts : -1;
if (!_amc_gl_possibly_wait_for_gl_sync (tmp, end_time))
ret = FALSE;
_gl_sync_render_unlocked (tmp);
g_queue_pop_head (tmp->sink->gl_queue);
_gl_sync_unref (tmp);
}
return ret;
}