From 1f902e2f6e958de9f97df8142564bee82d41256b Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Sat, 5 Aug 2017 12:23:30 -0400 Subject: [PATCH] v4l2bufferpool: Don't stop streaming when pool is flushing The purpose of being able to flush the buffer pool is only to unlock any blocked operation. Doing streamoff/streamon had the side effect of turning off and on the camera. As we do a flush_start / flush_stop sequence when shutting down, that would cause a really quick sequence of streamoff/streamon/streamoff/close which was causing some cameras to stop working. https://bugzilla.gnome.org/show_bug.cgi?id=783945 --- sys/v4l2/gstv4l2bufferpool.c | 245 ++++++++++++++++------------------- sys/v4l2/gstv4l2bufferpool.h | 2 + sys/v4l2/gstv4l2transform.c | 2 + sys/v4l2/gstv4l2videodec.c | 3 + sys/v4l2/gstv4l2videoenc.c | 3 + 5 files changed, 121 insertions(+), 134 deletions(-) diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index 56a8279f0f..b5a9f47b59 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -610,67 +610,6 @@ wrong_config: } } -static gboolean -gst_v4l2_buffer_pool_streamon (GstV4l2BufferPool * pool) -{ - GstV4l2Object *obj = pool->obj; - - switch (obj->mode) { - case GST_V4L2_IO_MMAP: - case GST_V4L2_IO_USERPTR: - case GST_V4L2_IO_DMABUF: - case GST_V4L2_IO_DMABUF_IMPORT: - if (!pool->streaming) { - if (obj->ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) - goto streamon_failed; - - pool->streaming = TRUE; - - GST_DEBUG_OBJECT (pool, "Started streaming"); - } - break; - default: - break; - } - - return TRUE; - -streamon_failed: - { - GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno, - g_strerror (errno)); - return FALSE; - } -} - -static void -gst_v4l2_buffer_pool_streamoff (GstV4l2BufferPool * pool) -{ - GstV4l2Object *obj = pool->obj; - - switch (obj->mode) { - case GST_V4L2_IO_MMAP: - case GST_V4L2_IO_USERPTR: - case GST_V4L2_IO_DMABUF: - case GST_V4L2_IO_DMABUF_IMPORT: - if (pool->streaming) { - if (obj->ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) - GST_WARNING_OBJECT (pool, "STREAMOFF failed with errno %d (%s)", - errno, g_strerror (errno)); - - pool->streaming = FALSE; - - GST_DEBUG_OBJECT (pool, "Stopped streaming"); - - if (pool->vallocator) - gst_v4l2_allocator_flush (pool->vallocator); - } - break; - default: - break; - } -} - static GstFlowReturn gst_v4l2_buffer_pool_resurect_buffer (GstV4l2BufferPool * pool) { @@ -697,6 +636,98 @@ gst_v4l2_buffer_pool_resurect_buffer (GstV4l2BufferPool * pool) return ret; } +static gboolean +gst_v4l2_buffer_pool_streamon (GstV4l2BufferPool * pool) +{ + GstV4l2Object *obj = pool->obj; + + if (pool->streaming) + return TRUE; + + switch (obj->mode) { + case GST_V4L2_IO_MMAP: + case GST_V4L2_IO_USERPTR: + case GST_V4L2_IO_DMABUF: + case GST_V4L2_IO_DMABUF_IMPORT: + if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type)) { + /* For captures, we need to enqueue buffers before we start streaming, + * so the driver don't underflow immediatly. As we have put then back + * into the base class queue, resurect them, then releasing will queue + * them back. */ + while (gst_v4l2_buffer_pool_resurect_buffer (pool) == GST_FLOW_OK) + continue; + } + + if (obj->ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) + goto streamon_failed; + + pool->streaming = TRUE; + + GST_DEBUG_OBJECT (pool, "Started streaming"); + break; + default: + break; + } + + return TRUE; + +streamon_failed: + { + GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno, + g_strerror (errno)); + return FALSE; + } +} + +/* Call with streamlock held, or when streaming threads are down */ +static void +gst_v4l2_buffer_pool_streamoff (GstV4l2BufferPool * pool) +{ + GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); + GstV4l2Object *obj = pool->obj; + gint i; + + if (!pool->streaming) + return; + + switch (obj->mode) { + case GST_V4L2_IO_MMAP: + case GST_V4L2_IO_USERPTR: + case GST_V4L2_IO_DMABUF: + case GST_V4L2_IO_DMABUF_IMPORT: + + if (obj->ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) + GST_WARNING_OBJECT (pool, "STREAMOFF failed with errno %d (%s)", + errno, g_strerror (errno)); + + pool->streaming = FALSE; + + GST_DEBUG_OBJECT (pool, "Stopped streaming"); + + if (pool->vallocator) + gst_v4l2_allocator_flush (pool->vallocator); + break; + default: + break; + } + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (pool->buffers[i]) { + GstBuffer *buffer = pool->buffers[i]; + GstBufferPool *bpool = GST_BUFFER_POOL (pool); + + pool->buffers[i] = NULL; + + if (V4L2_TYPE_IS_OUTPUT (pool->obj->type)) + gst_v4l2_buffer_pool_release_buffer (bpool, buffer); + else /* Don't re-enqueue capture buffer on stop */ + pclass->release_buffer (bpool, buffer); + + g_atomic_int_add (&pool->num_queued, -1); + } + } +} + static gboolean gst_v4l2_buffer_pool_start (GstBufferPool * bpool) { @@ -707,7 +738,7 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) GstCaps *caps; guint size, min_buffers, max_buffers; guint max_latency, min_latency, copy_threshold = 0; - gboolean can_allocate = FALSE; + gboolean can_allocate = FALSE, ret = TRUE; GST_DEBUG_OBJECT (pool, "activating pool"); @@ -838,12 +869,14 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) if (!pclass->start (bpool)) goto start_failed; - if (!V4L2_TYPE_IS_OUTPUT (obj->type)) + if (!V4L2_TYPE_IS_OUTPUT (obj->type)) { pool->group_released_handler = g_signal_connect_swapped (pool->vallocator, "group-released", G_CALLBACK (gst_v4l2_buffer_pool_resurect_buffer), pool); + ret = gst_v4l2_buffer_pool_streamon (pool); + } - return TRUE; + return ret; /* ERRORS */ wrong_config: @@ -877,9 +910,7 @@ static gboolean gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); gboolean ret; - gint i; GST_DEBUG_OBJECT (pool, "stopping pool"); @@ -897,21 +928,6 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) gst_v4l2_buffer_pool_streamoff (pool); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (pool->buffers[i]) { - GstBuffer *buffer = pool->buffers[i]; - - pool->buffers[i] = NULL; - - if (V4L2_TYPE_IS_OUTPUT (pool->obj->type)) - gst_v4l2_buffer_pool_release_buffer (bpool, buffer); - else /* Don't re-enqueue capture buffer on stop */ - pclass->release_buffer (bpool, buffer); - - g_atomic_int_add (&pool->num_queued, -1); - } - } - ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); if (ret && pool->vallocator) { @@ -950,65 +966,12 @@ static void gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstV4l2Object *obj = pool->obj; - GstBuffer *buffers[VIDEO_MAX_FRAME]; - gint i; GST_DEBUG_OBJECT (pool, "stop flushing"); - /* If we haven't started streaming yet, simply call streamon */ - if (!pool->streaming) - goto streamon; - if (pool->other_pool) gst_buffer_pool_set_flushing (pool->other_pool, FALSE); - GST_OBJECT_LOCK (pool); - gst_v4l2_buffer_pool_streamoff (pool); - /* Remember buffers to re-enqueue */ - memcpy (buffers, pool->buffers, sizeof (buffers)); - memset (pool->buffers, 0, sizeof (pool->buffers)); - GST_OBJECT_UNLOCK (pool); - - /* Reset our state */ - switch (obj->mode) { - case GST_V4L2_IO_RW: - break; - case GST_V4L2_IO_MMAP: - case GST_V4L2_IO_USERPTR: - case GST_V4L2_IO_DMABUF: - case GST_V4L2_IO_DMABUF_IMPORT: - { - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - /* Re-enqueue buffers */ - if (buffers[i]) { - GstBufferPool *bpool = (GstBufferPool *) pool; - GstBuffer *buffer = buffers[i]; - - /* Remove qdata, this will unmap any map data in - * userptr/dmabuf-import */ - gst_mini_object_set_qdata (GST_MINI_OBJECT (buffer), - GST_V4L2_IMPORT_QUARK, NULL, NULL); - - if (buffer->pool == NULL) - gst_v4l2_buffer_pool_release_buffer (bpool, buffer); - - g_atomic_int_add (&pool->num_queued, -1); - } - } - - break; - } - default: - g_assert_not_reached (); - break; - } - -streamon: - /* Start streaming on capture device only */ - if (!V4L2_TYPE_IS_OUTPUT (obj->type)) - gst_v4l2_buffer_pool_streamon (pool); - gst_poll_set_flushing (pool->poll, FALSE); } @@ -2037,3 +2000,17 @@ gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool, gboolean copy) pool->enable_copy_threshold = copy; GST_OBJECT_UNLOCK (pool); } + +gboolean +gst_v4l2_buffer_pool_flush (GstBufferPool * bpool) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + gboolean ret = TRUE; + + gst_v4l2_buffer_pool_streamoff (pool); + + if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type)) + ret = gst_v4l2_buffer_pool_streamon (pool); + + return ret; +} diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h index eff7737fc2..0fffc71c08 100644 --- a/sys/v4l2/gstv4l2bufferpool.h +++ b/sys/v4l2/gstv4l2bufferpool.h @@ -107,6 +107,8 @@ void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * poo void gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool, gboolean copy); +gboolean gst_v4l2_buffer_pool_flush (GstBufferPool *pool); + G_END_DECLS #endif /*__GST_V4L2_BUFFER_POOL_H__ */ diff --git a/sys/v4l2/gstv4l2transform.c b/sys/v4l2/gstv4l2transform.c index feed7bf577..0c29f91ce4 100644 --- a/sys/v4l2/gstv4l2transform.c +++ b/sys/v4l2/gstv4l2transform.c @@ -989,6 +989,8 @@ gst_v4l2_transform_sink_event (GstBaseTransform * trans, GstEvent * event) GST_DEBUG_OBJECT (self, "flush stop"); gst_v4l2_object_unlock_stop (self->v4l2capture); gst_v4l2_object_unlock_stop (self->v4l2output); + gst_v4l2_buffer_pool_flush (self->v4l2output->pool); + gst_v4l2_buffer_pool_flush (self->v4l2capture->pool); break; default: break; diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c index 8e369fae74..0f19cc691a 100644 --- a/sys/v4l2/gstv4l2videodec.c +++ b/sys/v4l2/gstv4l2videodec.c @@ -282,6 +282,9 @@ gst_v4l2_video_dec_flush (GstVideoDecoder * decoder) gst_v4l2_object_unlock_stop (self->v4l2output); gst_v4l2_object_unlock_stop (self->v4l2capture); + gst_v4l2_buffer_pool_flush (self->v4l2output->pool); + gst_v4l2_buffer_pool_flush (self->v4l2capture->pool); + return TRUE; } diff --git a/sys/v4l2/gstv4l2videoenc.c b/sys/v4l2/gstv4l2videoenc.c index ee89cec0ca..c322f5dfdd 100644 --- a/sys/v4l2/gstv4l2videoenc.c +++ b/sys/v4l2/gstv4l2videoenc.c @@ -218,6 +218,9 @@ gst_v4l2_video_enc_stop (GstVideoEncoder * encoder) gst_v4l2_object_stop (self->v4l2output); gst_v4l2_object_stop (self->v4l2capture); + gst_v4l2_buffer_pool_flush (self->v4l2output->pool); + gst_v4l2_buffer_pool_flush (self->v4l2capture->pool); + if (self->input_state) { gst_video_codec_state_unref (self->input_state); self->input_state = NULL;