From 2e89f4ecffb2363ffdce08fef579606698aee080 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Sat, 24 May 2014 20:00:14 -0400 Subject: [PATCH] Revert "v4l2bufferpool: Port to bufferpool flush_start/stop method" This reverts commit 2e0fb42e868fc9f6d98b028def80a3e953527307. Conflicts: sys/v4l2/gstv4l2allocator.c sys/v4l2/gstv4l2bufferpool.c sys/v4l2/gstv4l2videodec.c --- sys/v4l2/gstv4l2allocator.c | 6 - sys/v4l2/gstv4l2bufferpool.c | 420 +++++++++++++++++++---------------- sys/v4l2/gstv4l2bufferpool.h | 5 +- sys/v4l2/gstv4l2object.c | 24 +- sys/v4l2/gstv4l2object.h | 2 + sys/v4l2/gstv4l2transform.c | 22 +- sys/v4l2/gstv4l2videodec.c | 73 +++--- sys/v4l2/v4l2_calls.c | 25 +++ 8 files changed, 328 insertions(+), 249 deletions(-) diff --git a/sys/v4l2/gstv4l2allocator.c b/sys/v4l2/gstv4l2allocator.c index bb819c99c1..6f3dfb2ebc 100644 --- a/sys/v4l2/gstv4l2allocator.c +++ b/sys/v4l2/gstv4l2allocator.c @@ -1221,12 +1221,6 @@ gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator, gboolean ret = TRUE; gint i; - g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE); - - /* Buffer already queued */ - if (IS_QUEUED (group->buffer)) - return TRUE; - /* update sizes */ if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { for (i = 0; i < group->n_mem; i++) diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index ce8b99491b..b53ff60b22 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -520,67 +520,78 @@ wrong_config: } static gboolean -gst_v4l2_buffer_pool_streamon (GstV4l2BufferPool * pool) +start_streaming (GstV4l2BufferPool * pool) { GstV4l2Object *obj = pool->obj; + GST_DEBUG_OBJECT (pool, "start streaming"); + + if (pool->streaming) + return TRUE; + 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: - if (!pool->streaming) { - if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) - goto streamon_failed; + { + /* For capture device, we need to re-enqueue buffers before be can let + * the driver stream again */ + if (!V4L2_TYPE_IS_OUTPUT (obj->type) && pool->vallocator) { + GstBufferPool *bpool = GST_BUFFER_POOL (pool); + GstBufferPoolAcquireParams params = { 0 }; + gsize num_allocated, num_to_queue; + GstFlowReturn ret; - pool->streaming = TRUE; + num_allocated = gst_v4l2_allocator_num_allocated (pool->vallocator); + num_to_queue = num_allocated - g_atomic_int_get (&pool->num_queued); - GST_DEBUG_OBJECT (pool, "Started streaming"); + while (num_to_queue > 0) { + GstBuffer *buf; + + params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + &buf, ¶ms); + + if (ret != GST_FLOW_OK) + goto requeue_failed; + + gst_v4l2_buffer_pool_release_buffer (bpool, buf); + num_to_queue--; + } + + if (num_allocated != g_atomic_int_get (&pool->num_queued)) + goto requeue_failed; } + + if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) + goto start_failed; + + GST_DEBUG_OBJECT (pool, "STREAMON"); + break; + } default: + g_assert_not_reached (); break; } + pool->streaming = TRUE; + return TRUE; -streamon_failed: + /* ERRORS */ +start_failed: { GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno, g_strerror (errno)); return FALSE; } -} - -static gboolean -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 (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) - goto streamoff_failed; - - pool->streaming = FALSE; - - GST_DEBUG_OBJECT (pool, "Stopped streaming"); - } - break; - default: - break; - } - - return TRUE; - -streamoff_failed: +requeue_failed: { - GST_ERROR_OBJECT (pool, "error with STREAMOFF %d (%s)", errno, - g_strerror (errno)); + GST_ERROR_OBJECT (pool, "failed to re-enqueue buffers"); return FALSE; } } @@ -607,7 +618,6 @@ static gboolean gst_v4l2_buffer_pool_start (GstBufferPool * bpool) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); GstV4l2Object *obj = pool->obj; GstStructure *config; GstCaps *caps; @@ -733,7 +743,7 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) gst_buffer_pool_config_set_params (config, caps, size, min_buffers, max_buffers); - pclass->set_config (bpool, config); + GST_BUFFER_POOL_CLASS (parent_class)->set_config (bpool, config); gst_structure_free (config); if (pool->other_pool) @@ -741,14 +751,22 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) goto other_pool_failed; /* now, allocate the buffers: */ - if (!pclass->start (bpool)) + if (!GST_BUFFER_POOL_CLASS (parent_class)->start (bpool)) goto start_failed; + /* we can start capturing now, we wait for the playback case until we queued + * the first buffer */ + if (!V4L2_TYPE_IS_OUTPUT (obj->type)) + if (!start_streaming (pool)) + goto start_failed; + 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_group_released), pool); + gst_poll_set_flushing (obj->poll, FALSE); + return TRUE; /* ERRORS */ @@ -779,103 +797,26 @@ other_pool_failed: } } + static gboolean -gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) +stop_streaming (GstV4l2BufferPool * pool) { - 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"); - - if (pool->group_released_handler > 0) { - g_signal_handler_disconnect (pool->vallocator, - pool->group_released_handler); - pool->group_released_handler = 0; - } - - if (pool->other_pool) { - gst_object_unref (pool->other_pool); - pool->other_pool = NULL; - } - - if (!gst_v4l2_buffer_pool_streamoff (pool)) - goto streamoff_failed; - - gst_v4l2_allocator_flush (pool->vallocator); - - 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_buffer_unref (buffer); - else - pclass->release_buffer (bpool, buffer); - - g_atomic_int_add (&pool->num_queued, -1); - } - } - - ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); - - if (ret) { - GstV4l2Return vret; - - vret = gst_v4l2_allocator_stop (pool->vallocator); - - if (vret == GST_V4L2_BUSY) - GST_WARNING_OBJECT (pool, "some buffers are still outstanding"); - - ret = (vret == GST_V4L2_OK); - } - - return ret; - - /* ERRORS */ -streamoff_failed: - GST_ERROR_OBJECT (pool, "device refused to stop streaming"); - return FALSE; -} - -static void -gst_v4l2_buffer_pool_flush_start (GstBufferPool * bpool) -{ - GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - - GST_DEBUG_OBJECT (pool, "start flushing"); - - gst_poll_set_flushing (pool->poll, TRUE); - - if (pool->other_pool) - gst_buffer_pool_set_flushing (pool->other_pool, TRUE); -} - -static void -gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool) -{ - GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); GstV4l2Object *obj = pool->obj; gint i; - GST_DEBUG_OBJECT (pool, "stop flushing"); + GST_DEBUG_OBJECT (pool, "stopping stream"); - /* If we haven't started streaming yet, simply call streamon */ - if (!pool->streaming) - goto streamon; + gst_poll_set_flushing (obj->poll, TRUE); - if (pool->other_pool) - gst_buffer_pool_set_flushing (pool->other_pool, FALSE); + if (!pool->streaming) { + /* it avoid error: STREAMOFF 22 (Invalid argument) when + * attempting to stop a stream not previously started */ + GST_DEBUG_OBJECT (pool, "no need to stop, was not previously started"); + return TRUE; + } - if (!gst_v4l2_buffer_pool_streamoff (pool)) - goto stop_failed; + pool->flushing = TRUE; - gst_v4l2_allocator_flush (pool->vallocator); - - /* Reset our state */ switch (obj->mode) { case GST_V4L2_IO_RW: break; @@ -886,69 +827,118 @@ gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool) { gsize num_allocated; + if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) + goto stop_failed; + + GST_DEBUG_OBJECT (pool, "STREAMOFF"); + + gst_v4l2_allocator_flush (pool->vallocator); + num_allocated = gst_v4l2_allocator_num_allocated (pool->vallocator); for (i = 0; i < num_allocated; i++) { - /* Re-enqueue buffers */ if (pool->buffers[i]) { GstBufferPool *bpool = (GstBufferPool *) pool; GstBuffer *buffer = pool->buffers[i]; pool->buffers[i] = NULL; + g_atomic_int_add (&pool->num_queued, -1); - /* Remove qdata, this will unmap any map data in - * userptr/dmabuf-import */ + /* Remove qdata, this will unmap any map data in userptr */ gst_mini_object_set_qdata (GST_MINI_OBJECT (buffer), GST_V4L2_IMPORT_QUARK, NULL, NULL); if (V4L2_TYPE_IS_OUTPUT (obj->type)) gst_buffer_unref (buffer); else - gst_v4l2_buffer_pool_release_buffer (bpool, buffer); - - g_atomic_int_add (&pool->num_queued, -1); + /* Give back the outstanding buffer to the pool */ + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); } } + g_return_val_if_fail (pool->num_queued == 0, FALSE); break; } default: - g_assert_not_reached (); + g_return_val_if_reached (FALSE); break; } -streamon: - /* Start streaming on capture device only */ - if (!V4L2_TYPE_IS_OUTPUT (obj->type)) - gst_v4l2_buffer_pool_streamon (pool); + pool->flushing = FALSE; + pool->streaming = FALSE; - gst_poll_set_flushing (pool->poll, FALSE); - - return; + return TRUE; /* ERRORS */ stop_failed: { - GST_ERROR_OBJECT (pool, "device refused to flush"); + GST_ERROR_OBJECT (pool, "error with STREAMOFF %d (%s)", errno, + g_strerror (errno)); + return FALSE; + } +} + +static gboolean +gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) +{ + gboolean ret; + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + GstV4l2Object *obj = pool->obj; + + GST_DEBUG_OBJECT (pool, "stopping pool"); + + if (pool->group_released_handler > 0) { + g_signal_handler_disconnect (pool->vallocator, + pool->group_released_handler); + pool->group_released_handler = 0; + } + + gst_poll_set_flushing (obj->poll, TRUE); + if (!stop_streaming (pool)) + goto stop_failed; + + ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); + + if (ret) { + GstV4l2Return vret; + + vret = gst_v4l2_allocator_stop (pool->vallocator); + + if (vret == GST_V4L2_BUSY) { + GST_WARNING_OBJECT (pool, "allocated buffer need to be reclaimed"); + /* FIXME deal with reclaiming */ + } else if (vret == GST_V4L2_ERROR) { + ret = FALSE; + } + } + + return ret; + + /* ERRORS */ +stop_failed: + { + GST_ERROR_OBJECT (pool, "error with STREAMOFF %d (%s)", errno, + g_strerror (errno)); + return FALSE; } } static GstFlowReturn -gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool) +gst_v4l2_object_poll (GstV4l2Object * v4l2object) { gint ret; - if (pool->can_poll_device) { - GST_LOG_OBJECT (pool, "polling device"); - ret = gst_poll_wait (pool->poll, GST_CLOCK_TIME_NONE); - GST_LOG_OBJECT (pool, "device polled %i %s", ret, g_strerror (errno)); + if (v4l2object->can_poll_device) { + GST_LOG_OBJECT (v4l2object->element, "polling device"); + ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE); if (G_UNLIKELY (ret < 0)) { if (errno == EBUSY) goto stopped; if (errno == ENXIO) { - GST_WARNING_OBJECT (pool, + GST_WARNING_OBJECT (v4l2object->element, "v4l2 device doesn't support polling. Disabling"); - pool->can_poll_device = FALSE; + v4l2object->can_poll_device = FALSE; } else { if (errno != EAGAIN && errno != EINTR) goto select_error; @@ -960,12 +950,12 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool) /* ERRORS */ stopped: { - GST_DEBUG_OBJECT (pool, "stop called"); + GST_DEBUG ("stop called"); return GST_FLOW_FLUSHING; } select_error: { - GST_ELEMENT_ERROR (pool->obj->element, RESOURCE, READ, (NULL), + GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL), ("poll error %d: %s (%d)", ret, g_strerror (errno), errno)); return GST_FLOW_ERROR; } @@ -1024,7 +1014,7 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) GstV4l2MemoryGroup *group; gint i; - if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK) + if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK) goto poll_failed; GST_LOG_OBJECT (pool, "dequeueing a buffer"); @@ -1106,14 +1096,17 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, { GstFlowReturn ret; GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "acquire"); + if (GST_BUFFER_POOL_IS_FLUSHING (bpool)) + goto flushing; + /* If this is being called to resurect a lost buffer */ if (params && params->flags & GST_V4L2_POOL_ACQUIRE_FLAG_RESURECT) { - ret = pclass->acquire_buffer (bpool, buffer, params); + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer, + params); goto done; } @@ -1125,7 +1118,8 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, case GST_V4L2_IO_RW: { /* take empty buffer from the pool */ - ret = pclass->acquire_buffer (bpool, buffer, params); + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); break; } case GST_V4L2_IO_DMABUF: @@ -1143,7 +1137,8 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { - if (pclass->acquire_buffer (bpool, ©, params) == GST_FLOW_OK) { + if (GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + ©, params) == GST_FLOW_OK) { gst_v4l2_buffer_pool_release_buffer (bpool, copy); break; } @@ -1181,7 +1176,8 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, switch (obj->mode) { case GST_V4L2_IO_RW: /* get an empty buffer */ - ret = pclass->acquire_buffer (bpool, buffer, params); + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); break; case GST_V4L2_IO_MMAP: @@ -1189,7 +1185,8 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF_IMPORT: /* get a free unqueued buffer */ - ret = pclass->acquire_buffer (bpool, buffer, params); + ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, + buffer, params); break; default: @@ -1206,13 +1203,19 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, } done: return ret; + + /* ERRORS */ +flushing: + { + GST_DEBUG_OBJECT (pool, "We are flushing"); + return GST_FLOW_FLUSHING; + } } static void gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "release buffer %p", buffer); @@ -1225,7 +1228,7 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) switch (obj->mode) { case GST_V4L2_IO_RW: /* release back in the pool */ - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer); break; case GST_V4L2_IO_DMABUF: @@ -1233,17 +1236,23 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF_IMPORT: { - if (gst_v4l2_is_buffer_valid (buffer, NULL)) { + if (pool->flushing) { + /* put back on outstanding list */ + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); + } else if (gst_v4l2_is_buffer_valid (buffer, NULL)) { /* queue back in the device */ if (pool->other_pool) gst_v4l2_buffer_pool_prepare_buffer (pool, buffer, NULL); if (gst_v4l2_buffer_pool_qbuf (pool, buffer) != GST_FLOW_OK) - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); } else { /* Simply release invalide/modified buffer, the allocator will * give it back later */ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); } break; } @@ -1258,7 +1267,7 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) switch (obj->mode) { case GST_V4L2_IO_RW: /* release back in the pool */ - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer); break; case GST_V4L2_IO_MMAP: @@ -1273,7 +1282,8 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) /* Simply release invalide/modified buffer, the allocator will * give it back later */ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); break; } @@ -1291,7 +1301,8 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) gst_v4l2_allocator_reset_group (pool->vallocator, group); /* playback, put the buffer back in the queue to refill later. */ - pclass->release_buffer (bpool, buffer); + GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, + buffer); } else { /* We keep a ref on queued buffer, so this should never happen */ g_assert_not_reached (); @@ -1325,8 +1336,6 @@ gst_v4l2_buffer_pool_finalize (GObject * object) if (pool->video_fd >= 0) v4l2_close (pool->video_fd); - gst_poll_free (pool->poll); - if (pool->vallocator) gst_object_unref (pool->vallocator); @@ -1347,8 +1356,6 @@ gst_v4l2_buffer_pool_finalize (GObject * object) static void gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool) { - pool->poll = gst_poll_new (TRUE); - pool->can_poll_device = TRUE; } static void @@ -1365,8 +1372,6 @@ gst_v4l2_buffer_pool_class_init (GstV4l2BufferPoolClass * klass) bufferpool_class->alloc_buffer = gst_v4l2_buffer_pool_alloc_buffer; bufferpool_class->acquire_buffer = gst_v4l2_buffer_pool_acquire_buffer; bufferpool_class->release_buffer = gst_v4l2_buffer_pool_release_buffer; - bufferpool_class->flush_start = gst_v4l2_buffer_pool_flush_start; - bufferpool_class->flush_stop = gst_v4l2_buffer_pool_flush_stop; } /** @@ -1384,7 +1389,6 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) GstStructure *config; gchar *name, *parent_name; gint fd; - GstPollFD pollfd = GST_POLL_FD_INIT; fd = v4l2_dup (obj->video_fd); if (fd < 0) @@ -1400,16 +1404,8 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) "name", name, NULL); g_free (name); - pollfd.fd = fd; - gst_poll_add_fd (pool->poll, &pollfd); - if (V4L2_TYPE_IS_OUTPUT (obj->type)) - gst_poll_fd_ctl_write (pool->poll, &pollfd, TRUE); - else - gst_poll_fd_ctl_read (pool->poll, &pollfd, TRUE); - pool->video_fd = fd; pool->obj = obj; - pool->can_poll_device = TRUE; pool->vallocator = gst_v4l2_allocator_new (GST_OBJECT (pool), obj->video_fd, &obj->format); @@ -1448,7 +1444,7 @@ gst_v4l2_do_read (GstV4l2BufferPool * pool, GstBuffer * buf) gst_buffer_map (buf, &map, GST_MAP_WRITE); do { - if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK) + if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK) goto poll_error; amount = v4l2_read (obj->video_fd, map.data, toread); @@ -1516,9 +1512,6 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) g_return_val_if_fail (gst_buffer_pool_is_active (bpool), GST_FLOW_ERROR); - if (GST_BUFFER_POOL_IS_FLUSHING (pool)) - return GST_FLOW_FLUSHING; - switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -1637,9 +1630,11 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) /* if we are not streaming yet (this is the first buffer, start * streaming now */ - if (!gst_v4l2_buffer_pool_streamon (pool)) { - gst_buffer_unref (to_queue); - goto start_failed; + if (!pool->streaming) { + if (!start_streaming (pool)) { + gst_buffer_unref (to_queue); + goto start_failed; + } } if (g_atomic_int_get (&pool->num_queued) == @@ -1704,6 +1699,53 @@ start_failed: } } + +/** + * gst_v4l2_buffer_pool_stop_streaming: + * @bpool: a #GstBufferPool + * + * First, set obj->poll to be flushing + * Call STREAMOFF to clear QUEUED flag on every driver buffers. + * Then release all buffers that are in pool->buffers array. + * + * Returns: TRUE on success. + */ +gboolean +gst_v4l2_buffer_pool_stop_streaming (GstV4l2BufferPool * pool) +{ + GST_DEBUG_OBJECT (pool, "stop streaming"); + + if (!stop_streaming (pool)) + goto stop_failed; + + return TRUE; + + /* ERRORS */ +stop_failed: + { + GST_ERROR_OBJECT (pool, "failed to stop streaming"); + return FALSE; + } +} + +gboolean +gst_v4l2_buffer_pool_start_streaming (GstV4l2BufferPool * pool) +{ + GST_DEBUG_OBJECT (pool, "start straming"); + + if (!start_streaming (pool)) + goto start_failed; + + return TRUE; + + /* ERRORS */ +start_failed: + { + GST_ERROR_OBJECT (pool, "failed to start streaming"); + return FALSE; + } +} + void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool, GstBufferPool * other_pool) diff --git a/sys/v4l2/gstv4l2bufferpool.h b/sys/v4l2/gstv4l2bufferpool.h index 8fd0f61501..3459637d51 100644 --- a/sys/v4l2/gstv4l2bufferpool.h +++ b/sys/v4l2/gstv4l2bufferpool.h @@ -50,8 +50,6 @@ struct _GstV4l2BufferPool GstV4l2Object *obj; /* the v4l2 object */ gint video_fd; /* a dup(2) of the v4l2object's video_fd */ - GstPoll *poll; /* a poll for video_fd */ - gboolean can_poll_device; GstV4l2Allocator *vallocator; GstAllocator *allocator; @@ -86,6 +84,9 @@ GstBufferPool * gst_v4l2_buffer_pool_new (GstV4l2Object *obj, GstCaps *c GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * bpool, GstBuffer ** buf); +gboolean gst_v4l2_buffer_pool_stop_streaming (GstV4l2BufferPool * pool); +gboolean gst_v4l2_buffer_pool_start_streaming (GstV4l2BufferPool * pool); + void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool, GstBufferPool * other_pool); diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index a81e65b6f7..a311f49bc2 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -431,6 +431,7 @@ gst_v4l2_object_new (GstElement * element, v4l2object->update_fps_func = update_fps_func; v4l2object->video_fd = -1; + v4l2object->poll = gst_poll_new (TRUE); v4l2object->active = FALSE; v4l2object->videodev = g_strdup (default_device); @@ -460,6 +461,9 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object) if (v4l2object->videodev) g_free (v4l2object->videodev); + if (v4l2object->poll) + gst_poll_free (v4l2object->poll); + if (v4l2object->channel) g_free (v4l2object->channel); @@ -3039,27 +3043,19 @@ gst_v4l2_object_caps_equal (GstV4l2Object * v4l2object, GstCaps * caps) gboolean gst_v4l2_object_unlock (GstV4l2Object * v4l2object) { - gboolean ret = TRUE; + GST_LOG_OBJECT (v4l2object->element, "flush poll"); + gst_poll_set_flushing (v4l2object->poll, TRUE); - GST_LOG_OBJECT (v4l2object->element, "start flushing"); - - if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool)) - gst_buffer_pool_set_flushing (v4l2object->pool, TRUE); - - return ret; + return TRUE; } gboolean gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object) { - gboolean ret = TRUE; + GST_LOG_OBJECT (v4l2object->element, "flush stop poll"); + gst_poll_set_flushing (v4l2object->poll, FALSE); - GST_LOG_OBJECT (v4l2object->element, "stop flushing"); - - if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool)) - gst_buffer_pool_set_flushing (v4l2object->pool, FALSE); - - return ret; + return TRUE; } gboolean diff --git a/sys/v4l2/gstv4l2object.h b/sys/v4l2/gstv4l2object.h index c13cd64568..dbab527444 100644 --- a/sys/v4l2/gstv4l2object.h +++ b/sys/v4l2/gstv4l2object.h @@ -88,6 +88,8 @@ struct _GstV4l2Object { /* the video-device's file descriptor */ gint video_fd; GstV4l2IOMode mode; + GstPoll * poll; + gboolean can_poll_device; gboolean active; gboolean streaming; diff --git a/sys/v4l2/gstv4l2transform.c b/sys/v4l2/gstv4l2transform.c index 4ccddbba75..a314bd0efe 100644 --- a/sys/v4l2/gstv4l2transform.c +++ b/sys/v4l2/gstv4l2transform.c @@ -533,6 +533,19 @@ gst_v4l2_transform_sink_event (GstBaseTransform * trans, GstEvent * event) gst_v4l2_object_unlock (self->v4l2output); gst_v4l2_object_unlock (self->v4l2capture); break; + case GST_EVENT_FLUSH_STOP: + GST_DEBUG_OBJECT (self, "flush stop"); + + if (self->v4l2output->pool) { + gst_v4l2_buffer_pool_stop_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2output->pool)); + gst_v4l2_buffer_pool_start_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool)); + gst_v4l2_object_unlock_stop (self->v4l2output); + } + if (self->v4l2capture->pool) + gst_v4l2_buffer_pool_stop_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool)); default: break; } @@ -542,9 +555,12 @@ gst_v4l2_transform_sink_event (GstBaseTransform * trans, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: /* Buffer should be back now */ - GST_DEBUG_OBJECT (self, "flush stop"); - gst_v4l2_object_unlock_stop (self->v4l2capture); - gst_v4l2_object_unlock_stop (self->v4l2output); + if (self->v4l2capture->pool) { + gst_v4l2_buffer_pool_start_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool)); + gst_v4l2_object_unlock_stop (self->v4l2capture); + } + GST_DEBUG_OBJECT (self, "flush stop done"); break; default: break; diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c index f3e03c78d1..a63db037d8 100644 --- a/sys/v4l2/gstv4l2videodec.c +++ b/sys/v4l2/gstv4l2videodec.c @@ -259,23 +259,23 @@ gst_v4l2_video_dec_flush (GstVideoDecoder * decoder) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); - GST_DEBUG_OBJECT (self, "Flushed"); + GST_DEBUG_OBJECT (self, "Flushing"); - /* Ensure the processing thread has stopped for the reverse playback - * discount case */ - if (g_atomic_int_get (&self->processing)) { - GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); - - gst_v4l2_object_unlock (self->v4l2output); - gst_v4l2_object_unlock (self->v4l2capture); - gst_pad_stop_task (decoder->srcpad); - GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); - } + /* Wait for capture thread to stop */ + GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (decoder->srcpad); + GST_VIDEO_DECODER_STREAM_LOCK (decoder); self->output_flow = GST_FLOW_OK; - gst_v4l2_object_unlock_stop (self->v4l2output); - gst_v4l2_object_unlock_stop (self->v4l2capture); + if (self->v4l2output->pool) + gst_v4l2_buffer_pool_stop_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2output->pool)); + + if (self->v4l2capture->pool) + gst_v4l2_buffer_pool_stop_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool)); return TRUE; } @@ -308,13 +308,15 @@ gst_v4l2_video_dec_finish (GstVideoDecoder * decoder) v4l2output->pool), &buffer); gst_buffer_unref (buffer); } - - /* and ensure the processing thread has stopped in case another error - * occured. */ - gst_v4l2_object_unlock (self->v4l2capture); - gst_pad_stop_task (decoder->srcpad); GST_VIDEO_DECODER_STREAM_LOCK (decoder); + /* Ensure the processing thread has stopped */ + if (g_atomic_int_get (&self->processing)) { + gst_v4l2_object_unlock (self->v4l2capture); + gst_pad_stop_task (decoder->srcpad); + g_assert (g_atomic_int_get (&self->processing) == FALSE); + } + if (ret == GST_FLOW_FLUSHING) ret = self->output_flow; @@ -487,9 +489,11 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, } GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); + gst_v4l2_object_unlock_stop (self->v4l2output); ret = gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL (self-> v4l2output->pool), &codec_data); + gst_v4l2_object_unlock (self->v4l2output); GST_VIDEO_DECODER_STREAM_LOCK (decoder); gst_buffer_unref (codec_data); @@ -528,6 +532,14 @@ gst_v4l2_video_dec_handle_frame (GstVideoDecoder * decoder, GST_DEBUG_OBJECT (self, "Starting decoding thread"); + /* Enable processing input */ + if (!gst_v4l2_buffer_pool_start_streaming (GST_V4L2_BUFFER_POOL + (self->v4l2capture->pool))) + goto start_streaming_failed; + + gst_v4l2_object_unlock_stop (self->v4l2output); + gst_v4l2_object_unlock_stop (self->v4l2capture); + /* Start the processing task, when it quits, the task will disable input * processing to unlock input if draining, or prevent potential block */ g_atomic_int_set (&self->processing, TRUE); @@ -566,6 +578,13 @@ not_negotiated: ret = GST_FLOW_NOT_NEGOTIATED; goto drop; } +start_streaming_failed: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + (_("Failed to re-enabled decoder.")), + ("Could not re-enqueue and start streaming on decide.")); + return GST_FLOW_ERROR; + } activate_failed: { GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, @@ -705,31 +724,17 @@ static gboolean gst_v4l2_video_dec_sink_event (GstVideoDecoder * decoder, GstEvent * event) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (decoder); - gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: GST_DEBUG_OBJECT (self, "flush start"); gst_v4l2_object_unlock (self->v4l2output); gst_v4l2_object_unlock (self->v4l2capture); - break; default: break; } - ret = GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - /* The processing thread should stop now, wait for it */ - gst_pad_stop_task (decoder->srcpad); - GST_DEBUG_OBJECT (self, "flush start done"); - break; - default: - break; - } - - return ret; + return GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event); } static GstStateChangeReturn @@ -737,13 +742,11 @@ gst_v4l2_video_dec_change_state (GstElement * element, GstStateChange transition) { GstV4l2VideoDec *self = GST_V4L2_VIDEO_DEC (element); - GstVideoDecoder *decoder = GST_VIDEO_DECODER (element); if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) { g_atomic_int_set (&self->active, FALSE); gst_v4l2_object_unlock (self->v4l2output); gst_v4l2_object_unlock (self->v4l2capture); - gst_pad_stop_task (decoder->srcpad); } return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index d1cc2842db..793685380b 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -514,6 +514,7 @@ gst_v4l2_open (GstV4l2Object * v4l2object) { struct stat st; int libv4l2_fd; + GstPollFD pollfd = GST_POLL_FD_INIT; GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s", v4l2object->videodev); @@ -550,6 +551,8 @@ gst_v4l2_open (GstV4l2Object * v4l2object) if (libv4l2_fd != -1) v4l2object->video_fd = libv4l2_fd; + v4l2object->can_poll_device = TRUE; + /* get capabilities, error will be posted */ if (!gst_v4l2_get_capabilities (v4l2object)) goto error; @@ -586,6 +589,14 @@ gst_v4l2_open (GstV4l2Object * v4l2object) "Opened device '%s' (%s) successfully", v4l2object->vcap.card, v4l2object->videodev); + pollfd.fd = v4l2object->video_fd; + gst_poll_add_fd (v4l2object->poll, &pollfd); + if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE + || v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE); + else + gst_poll_fd_ctl_write (v4l2object->poll, &pollfd, TRUE); + if (v4l2object->extra_controls) gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls); @@ -661,6 +672,8 @@ error: gboolean gst_v4l2_dup (GstV4l2Object * v4l2object, GstV4l2Object * other) { + GstPollFD pollfd = GST_POLL_FD_INIT; + GST_DEBUG_OBJECT (v4l2object->element, "Trying to dup device %s", other->videodev); @@ -683,7 +696,16 @@ gst_v4l2_dup (GstV4l2Object * v4l2object, GstV4l2Object * other) "Cloned device '%s' (%s) successfully", v4l2object->vcap.card, v4l2object->videodev); + pollfd.fd = v4l2object->video_fd; + gst_poll_add_fd (v4l2object->poll, &pollfd); + if (v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE + || v4l2object->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE); + else + gst_poll_fd_ctl_write (v4l2object->poll, &pollfd, TRUE); + v4l2object->never_interlaced = other->never_interlaced; + v4l2object->can_poll_device = TRUE; return TRUE; @@ -706,6 +728,7 @@ not_open: gboolean gst_v4l2_close (GstV4l2Object * v4l2object) { + GstPollFD pollfd = GST_POLL_FD_INIT; GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s", v4l2object->videodev); @@ -714,6 +737,8 @@ gst_v4l2_close (GstV4l2Object * v4l2object) /* close device */ v4l2_close (v4l2object->video_fd); + pollfd.fd = v4l2object->video_fd; + gst_poll_remove_fd (v4l2object->poll, &pollfd); v4l2object->video_fd = -1; /* empty lists */