From 7599821c423afac3449b2a33107a0f7acfb1cceb Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Fri, 14 Apr 2023 22:22:06 -0400 Subject: [PATCH] v4l2: object: Move the GstPoll into v4l2object 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: --- .../gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c | 109 +------------ .../gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h | 3 - .../gst-plugins-good/sys/v4l2/gstv4l2object.c | 168 ++++++++++++++++++++- .../gst-plugins-good/sys/v4l2/gstv4l2object.h | 6 + 4 files changed, 178 insertions(+), 108 deletions(-) diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c index 889447f..3f28305 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.c @@ -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); } diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h index 60340c2..441dc22 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2bufferpool.h @@ -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; diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c index 07aa3c8..3fb3a77 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c @@ -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; +} diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.h b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.h index 229d15e..f749e62 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.h +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.h @@ -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); -- 2.7.4