From 652ed3bceb4e31fcd5dea9fdc9525fafb3350968 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 3 Jul 2014 15:28:45 -0400 Subject: [PATCH] v4l2bufferpool: Wait before polling if queue is empty In kernel before 3.17, polling during queue underrun would unblock right away and trigger POLLERR. As we are not handling POLLERR, we would endup blocking in DQBUF call, which won't be unblocked correctly when going to NULL state. A deadlock at start caused by locking error in libv4l2 was also seen before this patch. Instead, we wait until the queue is no longer empty before polling. https://bugzilla.gnome.org/show_bug.cgi?id=731015 --- sys/v4l2/gstv4l2bufferpool.c | 23 ++++++++++++++++++++++- sys/v4l2/gstv4l2bufferpool.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index f5c74c8f2c..eb63a57136 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -846,6 +846,11 @@ gst_v4l2_buffer_pool_flush_start (GstBufferPool * bpool) gst_poll_set_flushing (pool->poll, TRUE); + GST_OBJECT_LOCK (pool); + pool->empty = FALSE; + g_cond_broadcast (&pool->empty_cond); + GST_OBJECT_UNLOCK (pool); + if (pool->other_pool) gst_buffer_pool_set_flushing (pool->other_pool, TRUE); } @@ -934,6 +939,11 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool) { gint ret; + GST_OBJECT_LOCK (pool); + while (pool->empty) + g_cond_wait (&pool->empty_cond, GST_OBJECT_GET_LOCK (pool)); + GST_OBJECT_UNLOCK (pool); + if (!pool->can_poll_device) goto done; @@ -1001,6 +1011,11 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf) if (!gst_v4l2_allocator_qbuf (pool->vallocator, group)) goto queue_failed; + GST_OBJECT_LOCK (pool); + pool->empty = FALSE; + g_cond_signal (&pool->empty_cond); + GST_OBJECT_UNLOCK (pool); + return GST_FLOW_OK; already_queued: @@ -1047,7 +1062,11 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) /* mark the buffer outstanding */ pool->buffers[group->buffer.index] = NULL; - g_atomic_int_add (&pool->num_queued, -1); + if (g_atomic_int_dec_and_test (&pool->num_queued)) { + GST_OBJECT_LOCK (pool); + pool->empty = TRUE; + GST_OBJECT_UNLOCK (pool); + } timestamp = GST_TIMEVAL_TO_TIME (group->buffer.timestamp); @@ -1354,6 +1373,8 @@ gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool) { pool->poll = gst_poll_new (TRUE); pool->can_poll_device = TRUE; + g_cond_init (&pool->empty_cond); + pool->empty = TRUE; } static void diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h index 65486bff8e..e805615a07 100644 --- a/sys/v4l2/gstv4l2bufferpool.h +++ b/sys/v4l2/gstv4l2bufferpool.h @@ -53,6 +53,9 @@ struct _GstV4l2BufferPool GstPoll *poll; /* a poll for video_fd */ gboolean can_poll_device; + gboolean empty; + GCond empty_cond; + GstV4l2Allocator *vallocator; GstAllocator *allocator; GstAllocationParams params;