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
This commit is contained in:
Nicolas Dufresne 2017-08-05 12:23:30 -04:00
parent a802f5df42
commit 1f902e2f6e
5 changed files with 121 additions and 134 deletions

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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;

View file

@ -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;
}

View file

@ -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;