v4l2: object: Move the GstPoll into v4l2object
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Sat, 15 Apr 2023 02:22:06 +0000 (22:22 -0400)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 2 May 2023 14:42:43 +0000 (14:42 +0000)
Moves the GstPoll from the buffer pool into v4l2object. This will be
needed to poll for events before the pool has been created.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4525>

subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c
subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h
subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c
subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.h

index 889447f..3f28305 100644 (file)
@@ -1077,7 +1077,7 @@ gst_v4l2_buffer_pool_flush_start (GstBufferPool * bpool)
 
   GST_DEBUG_OBJECT (pool, "start flushing");
 
-  gst_poll_set_flushing (pool->poll, TRUE);
+  gst_poll_set_flushing (pool->obj->poll, TRUE);
 
   GST_OBJECT_LOCK (pool);
   pool->empty = FALSE;
@@ -1098,13 +1098,12 @@ gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool)
   if (pool->other_pool && gst_buffer_pool_is_active (pool->other_pool))
     gst_buffer_pool_set_flushing (pool->other_pool, FALSE);
 
-  gst_poll_set_flushing (pool->poll, FALSE);
+  gst_poll_set_flushing (pool->obj->poll, FALSE);
 }
 
 static GstFlowReturn
 gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
 {
-  gint ret;
   GstClockTime timeout;
 
   if (wait)
@@ -1119,7 +1118,7 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
 
     if (!wait && pool->empty) {
       GST_OBJECT_UNLOCK (pool);
-      goto no_buffers;
+      return GST_V4L2_FLOW_LAST_BUFFER;
     }
 
     while (pool->empty)
@@ -1128,87 +1127,14 @@ gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait)
     GST_OBJECT_UNLOCK (pool);
   }
 
-  if (!pool->can_poll_device) {
+  if (!pool->obj->can_poll_device) {
     if (wait)
-      goto done;
+      return GST_FLOW_OK;
     else
-      goto no_buffers;
-  }
-
-  GST_LOG_OBJECT (pool, "polling device");
-
-again:
-  ret = gst_poll_wait (pool->poll, timeout);
-  if (G_UNLIKELY (ret < 0)) {
-    switch (errno) {
-      case EBUSY:
-        goto stopped;
-      case EAGAIN:
-      case EINTR:
-        goto again;
-      case ENXIO:
-        GST_WARNING_OBJECT (pool,
-            "v4l2 device doesn't support polling. Disabling"
-            " using libv4l2 in this case may cause deadlocks");
-        pool->can_poll_device = FALSE;
-        goto done;
-      default:
-        goto select_error;
-    }
-  }
-
-  if (gst_poll_fd_has_error (pool->poll, &pool->pollfd))
-    goto select_error;
-
-  /* PRI is used to signal that events are available */
-  if (gst_poll_fd_has_pri (pool->poll, &pool->pollfd)) {
-    struct v4l2_event event = { 0, };
-
-    if (!gst_v4l2_dequeue_event (pool->obj, &event))
-      goto dqevent_failed;
-
-    if (event.type != V4L2_EVENT_SOURCE_CHANGE) {
-      GST_INFO_OBJECT (pool, "Received unhandled event, ignoring.");
-      goto again;
-    }
-
-    if ((event.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) == 0) {
-      GST_DEBUG_OBJECT (pool,
-          "Received non-resolution source-change, ignoring.");
-      goto again;
-    }
-
-    return GST_V4L2_FLOW_RESOLUTION_CHANGE;
+      return GST_V4L2_FLOW_LAST_BUFFER;
   }
 
-  if (ret == 0)
-    goto no_buffers;
-
-done:
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-stopped:
-  {
-    GST_DEBUG_OBJECT (pool, "stop called");
-    return GST_FLOW_FLUSHING;
-  }
-select_error:
-  {
-    GST_ELEMENT_ERROR (pool->obj->element, RESOURCE, READ, (NULL),
-        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
-    return GST_FLOW_ERROR;
-  }
-no_buffers:
-  {
-    return GST_V4L2_FLOW_LAST_BUFFER;
-  }
-dqevent_failed:
-  {
-    GST_ELEMENT_ERROR (pool->obj->element, RESOURCE, READ, (NULL),
-        ("dqevent error: %s (%d)", g_strerror (errno), errno));
-    return GST_FLOW_ERROR;
-  }
+  return gst_v4l2_object_poll (pool->obj, timeout);
 }
 
 static GstFlowReturn
@@ -1757,8 +1683,6 @@ gst_v4l2_buffer_pool_finalize (GObject * object)
   if (pool->video_fd >= 0)
     pool->obj->close (pool->video_fd);
 
-  gst_poll_free (pool->poll);
-
   /* This can't be done in dispose method because we must not set pointer
    * to NULL as it is part of the v4l2object and dispose could be called
    * multiple times */
@@ -1774,8 +1698,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;
   g_cond_init (&pool->empty_cond);
   pool->empty = TRUE;
   pool->orphaned = FALSE;
@@ -1838,17 +1760,8 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
   g_object_ref_sink (pool);
   g_free (name);
 
-  gst_poll_fd_init (&pool->pollfd);
-  pool->pollfd.fd = fd;
-  gst_poll_add_fd (pool->poll, &pool->pollfd);
-  if (V4L2_TYPE_IS_OUTPUT (obj->type))
-    gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE);
-  else
-    gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE);
-
   pool->video_fd = fd;
   pool->obj = obj;
-  pool->can_poll_device = TRUE;
 
   pool->vallocator = gst_v4l2_allocator_new (GST_OBJECT (pool), obj);
   if (pool->vallocator == NULL)
@@ -2331,11 +2244,5 @@ gst_v4l2_buffer_pool_flush (GstV4l2Object * v4l2object)
 void
 gst_v4l2_buffer_pool_enable_resolution_change (GstV4l2BufferPool * pool)
 {
-  guint32 input_id = 0;
-
-  /* Make sure we subscribe for the current input */
-  gst_v4l2_get_input (pool->obj, &input_id);
-
-  if (gst_v4l2_subscribe_event (pool->obj, V4L2_EVENT_SOURCE_CHANGE, input_id))
-    gst_poll_fd_ctl_pri (pool->poll, &pool->pollfd, TRUE);
+  gst_v4l2_object_subscribe_event (pool->obj, V4L2_EVENT_SOURCE_CHANGE);
 }
index 60340c2..441dc22 100644 (file)
@@ -64,9 +64,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 */
-  GstPollFD pollfd;
-  gboolean can_poll_device;
 
   gboolean empty;
   GCond empty_cond;
index 07aa3c8..3fb3a77 100644 (file)
@@ -525,6 +525,9 @@ gst_v4l2_object_new (GstElement * element,
 
   v4l2object->no_initial_format = FALSE;
 
+  v4l2object->poll = gst_poll_new (TRUE);
+  v4l2object->can_poll_device = TRUE;
+
   /* We now disable libv4l2 by default, but have an env to enable it. */
 #ifdef HAVE_LIBV4L2
   if (g_getenv ("GST_V4L2_USE_LIBV4L2")) {
@@ -572,6 +575,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
   g_free (v4l2object->par);
   g_free (v4l2object->channel);
 
+  gst_poll_free (v4l2object->poll);
+
   if (v4l2object->formats) {
     gst_v4l2_object_clear_format_list (v4l2object);
   }
@@ -900,6 +905,20 @@ gst_v4l2_set_defaults (GstV4l2Object * v4l2object)
   }
 }
 
+static void
+gst_v4l2_object_init_poll (GstV4l2Object * v4l2object)
+{
+  gst_poll_fd_init (&v4l2object->pollfd);
+  v4l2object->pollfd.fd = v4l2object->video_fd;
+  gst_poll_add_fd (v4l2object->poll, &v4l2object->pollfd);
+  if (V4L2_TYPE_IS_OUTPUT (v4l2object->type))
+    gst_poll_fd_ctl_write (v4l2object->poll, &v4l2object->pollfd, TRUE);
+  else
+    gst_poll_fd_ctl_read (v4l2object->poll, &v4l2object->pollfd, TRUE);
+
+  v4l2object->can_poll_device = TRUE;
+}
+
 gboolean
 gst_v4l2_object_open (GstV4l2Object * v4l2object, GstV4l2Error * error)
 {
@@ -908,17 +927,20 @@ gst_v4l2_object_open (GstV4l2Object * v4l2object, GstV4l2Error * error)
   else
     return FALSE;
 
+  gst_v4l2_object_init_poll (v4l2object);
+
   return TRUE;
 }
 
 gboolean
 gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other)
 {
-  gboolean ret;
-
-  ret = gst_v4l2_dup (v4l2object, other);
+  if (gst_v4l2_dup (v4l2object, other)) {
+    gst_v4l2_object_init_poll (v4l2object);
+    return TRUE;
+  }
 
-  return ret;
+  return FALSE;
 }
 
 gboolean
@@ -4598,6 +4620,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
 
   GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing");
 
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
+
   if (!pool)
     return ret;
 
@@ -4616,6 +4640,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
 
   GST_LOG_OBJECT (v4l2object->dbg_obj, "stop flushing");
 
+  gst_poll_set_flushing (v4l2object->poll, FALSE);
+
   if (!pool)
     return ret;
 
@@ -4637,6 +4663,8 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object)
   if (!GST_V4L2_IS_ACTIVE (v4l2object))
     goto done;
 
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
+
   pool = gst_v4l2_object_get_buffer_pool (v4l2object);
   if (pool) {
     if (!gst_v4l2_buffer_pool_orphan (v4l2object)) {
@@ -5467,3 +5495,135 @@ gst_v4l2_object_get_buffer_pool (GstV4l2Object * v4l2object)
 
   return ret;
 }
+
+/**
+ * gst_v4l2_object_poll:
+ * @v4l2object: a #GstV4l2Object
+ * @timeout: timeout of type #GstClockTime
+ *
+ * Poll the video file descriptor for read when this is a capture, write when
+ * this is an output. It will also watch for errors and source change events.
+ * If a source change event is received, %GST_V4L2_FLOW_RESOLUTION_CHANGE will
+ * be returned. If the poll was interrupted, %GST_FLOW_FLUSHING is returned.
+ * If there was no read or write indicator, %GST_V4L2_FLOW_LAST_BUFFER is
+ * returned. It may also return %GST_FLOW_ERROR if some unexpected error
+ * occured.
+ *
+ * Returns: GST_FLOW_OK if buffers are ready to be queued or dequeued.
+ */
+GstFlowReturn
+gst_v4l2_object_poll (GstV4l2Object * v4l2object, GstClockTime timeout)
+{
+  gint ret;
+
+  if (!v4l2object->can_poll_device) {
+    if (timeout != 0)
+      goto done;
+    else
+      goto no_buffers;
+  }
+
+  GST_LOG_OBJECT (v4l2object->dbg_obj, "polling device");
+
+again:
+  ret = gst_poll_wait (v4l2object->poll, timeout);
+  if (G_UNLIKELY (ret < 0)) {
+    switch (errno) {
+      case EBUSY:
+        goto stopped;
+      case EAGAIN:
+      case EINTR:
+        goto again;
+      case ENXIO:
+        GST_WARNING_OBJECT (v4l2object->dbg_obj,
+            "v4l2 device doesn't support polling. Disabling"
+            " using libv4l2 in this case may cause deadlocks");
+        v4l2object->can_poll_device = FALSE;
+        goto done;
+      default:
+        goto select_error;
+    }
+  }
+
+  if (gst_poll_fd_has_error (v4l2object->poll, &v4l2object->pollfd))
+    goto select_error;
+
+  /* PRI is used to signal that events are available */
+  if (gst_poll_fd_has_pri (v4l2object->poll, &v4l2object->pollfd)) {
+    struct v4l2_event event = { 0, };
+
+    if (!gst_v4l2_dequeue_event (v4l2object, &event))
+      goto dqevent_failed;
+
+    if (event.type != V4L2_EVENT_SOURCE_CHANGE) {
+      GST_INFO_OBJECT (v4l2object->dbg_obj,
+          "Received unhandled event, ignoring.");
+      goto again;
+    }
+
+    if ((event.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) == 0) {
+      GST_DEBUG_OBJECT (v4l2object->dbg_obj,
+          "Received non-resolution source-change, ignoring.");
+      goto again;
+    }
+
+    return GST_V4L2_FLOW_RESOLUTION_CHANGE;
+  }
+
+  if (ret == 0)
+    goto no_buffers;
+
+done:
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+stopped:
+  {
+    GST_DEBUG_OBJECT (v4l2object->dbg_obj, "stop called");
+    return GST_FLOW_FLUSHING;
+  }
+select_error:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+no_buffers:
+  {
+    return GST_V4L2_FLOW_LAST_BUFFER;
+  }
+dqevent_failed:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("dqevent error: %s (%d)", g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+}
+
+/**
+ * gst_v4l2_object_subscribe_event:
+ * @v4l2object: a #GstV4l2Object
+ * @event: the event ID
+ *
+ * Subscribe to an event, and enable polling for these. Note that only
+ * %V4L2_EVENT_SOURCE_CHANGE is currently supported by the poll helper.
+ *
+ * Returns: %TRUE if the driver supports this event
+ */
+gboolean
+gst_v4l2_object_subscribe_event (GstV4l2Object * v4l2object, guint32 event)
+{
+  guint32 id = 0;
+
+  g_return_val_if_fail (v4l2object != NULL, FALSE);
+  g_return_val_if_fail (GST_V4L2_IS_OPEN (v4l2object), FALSE);
+
+  v4l2object->get_in_out_func (v4l2object, &id);
+
+  if (gst_v4l2_subscribe_event (v4l2object, event, id)) {
+    gst_poll_fd_ctl_pri (v4l2object->poll, &v4l2object->pollfd, TRUE);
+    return TRUE;
+  }
+
+  return FALSE;
+}
index 229d15e..f749e62 100644 (file)
@@ -134,6 +134,9 @@ struct _GstV4l2Object {
   /* the video-device's file descriptor */
   gint video_fd;
   GstV4l2IOMode mode;
+  GstPoll *poll;
+  GstPollFD pollfd;
+  gboolean can_poll_device;
 
   gboolean active;
 
@@ -314,6 +317,9 @@ GstBufferPool * gst_v4l2_object_get_buffer_pool (GstV4l2Object * v4l2object);
 
 GstStructure * gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
 
+GstFlowReturn  gst_v4l2_object_poll (GstV4l2Object * v4l2object, GstClockTime timeout);
+gboolean       gst_v4l2_object_subscribe_event (GstV4l2Object * v4l2object, guint32 event);
+
 /* crop / compose */
 gboolean     gst_v4l2_object_set_crop (GstV4l2Object * obj, struct v4l2_rect *result);
 gboolean     gst_v4l2_object_get_crop_bounds (GstV4l2Object * obj, struct v4l2_rect *bounds);