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
diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c
index 56a8279..b5a9f47 100644
--- a/sys/v4l2/gstv4l2bufferpool.c
+++ b/sys/v4l2/gstv4l2bufferpool.c
@@ -610,67 +610,6 @@
   }
 }
 
-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)
 {
@@ -698,6 +637,98 @@
 }
 
 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)
 {
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
@@ -707,7 +738,7 @@
   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 @@
   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 @@
 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_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 @@
 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 @@
   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 eff7737..0fffc71 100644
--- a/sys/v4l2/gstv4l2bufferpool.h
+++ b/sys/v4l2/gstv4l2bufferpool.h
@@ -107,6 +107,8 @@
 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 feed7bf..0c29f91 100644
--- a/sys/v4l2/gstv4l2transform.c
+++ b/sys/v4l2/gstv4l2transform.c
@@ -989,6 +989,8 @@
       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 8e369fa..0f19cc6 100644
--- a/sys/v4l2/gstv4l2videodec.c
+++ b/sys/v4l2/gstv4l2videodec.c
@@ -282,6 +282,9 @@
   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 ee89cec..c322f5d 100644
--- a/sys/v4l2/gstv4l2videoenc.c
+++ b/sys/v4l2/gstv4l2videoenc.c
@@ -218,6 +218,9 @@
   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;