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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4437>
This commit is contained in:
Nicolas Dufresne 2023-04-14 22:22:06 -04:00
parent 457dd19a90
commit 5c820862fd
4 changed files with 178 additions and 108 deletions

View file

@ -1079,7 +1079,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;
@ -1100,13 +1100,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)
@ -1121,7 +1120,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)
@ -1130,87 +1129,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;
return GST_V4L2_FLOW_LAST_BUFFER;
}
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;
}
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
@ -1760,8 +1686,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 */
@ -1777,8 +1701,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;
@ -1841,17 +1763,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)
@ -2336,11 +2249,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);
}

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

View 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;
if (gst_v4l2_dup (v4l2object, other)) {
gst_v4l2_object_init_poll (v4l2object);
return TRUE;
}
ret = gst_v4l2_dup (v4l2object, other);
return ret;
return FALSE;
}
gboolean
@ -4597,6 +4619,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;
@ -4615,6 +4639,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;
@ -4636,6 +4662,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)) {
@ -5466,3 +5494,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;
}

View file

@ -136,6 +136,9 @@ struct _GstV4l2Object {
/* the video-device's file descriptor */
gint video_fd;
GstV4l2IOMode mode;
GstPoll *poll;
GstPollFD pollfd;
gboolean can_poll_device;
gboolean active;
@ -316,6 +319,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);