Commit 1f99b152 authored by Song Bing's avatar Song Bing

v4l2videodec: Add resolution change support

Support resolution change event.

https://bugzilla.gnome.org/show_bug.cgi?id=752962
parent f6b91fe3
Pipeline #42255 failed with stages
in 7 minutes and 58 seconds
...@@ -1201,8 +1201,35 @@ queue_failed: ...@@ -1201,8 +1201,35 @@ queue_failed:
} }
static GstFlowReturn static GstFlowReturn
gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer, gst_v4l2_buffer_pool_dqevent (GstV4l2BufferPool * pool)
gboolean wait) {
GstV4l2Object *obj = pool->obj;
struct v4l2_event evt;
memset (&evt, 0x00, sizeof (struct v4l2_event));
if (obj->ioctl (pool->video_fd, VIDIOC_DQEVENT, &evt) < 0)
goto dqevent_failed;
switch (evt.type) {
case V4L2_EVENT_SOURCE_CHANGE:
return GST_V4L2_FLOW_SOURCE_CHANGE;
case V4L2_EVENT_EOS:
return GST_V4L2_FLOW_LAST_BUFFER;
default:
break;
}
return GST_FLOW_OK;
/* ERRORS */
dqevent_failed:
{
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer)
{ {
GstFlowReturn res; GstFlowReturn res;
GstBuffer *outbuf = NULL; GstBuffer *outbuf = NULL;
...@@ -1213,16 +1240,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer, ...@@ -1213,16 +1240,6 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gsize size; gsize size;
gint i; gint i;
if ((res = gst_v4l2_buffer_pool_poll (pool, wait)) < GST_FLOW_OK)
goto poll_failed;
if (res == GST_FLOW_CUSTOM_SUCCESS) {
GST_LOG_OBJECT (pool, "nothing to dequeue");
goto done;
}
GST_LOG_OBJECT (pool, "dequeueing a buffer");
res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group); res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group);
if (res == GST_FLOW_EOS) if (res == GST_FLOW_EOS)
goto eos; goto eos;
...@@ -1350,11 +1367,6 @@ done: ...@@ -1350,11 +1367,6 @@ done:
return res; return res;
/* ERRORS */ /* ERRORS */
poll_failed:
{
GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
return res;
}
eos: eos:
{ {
return GST_FLOW_EOS; return GST_FLOW_EOS;
...@@ -1371,6 +1383,40 @@ no_buffer: ...@@ -1371,6 +1383,40 @@ no_buffer:
} }
} }
static GstFlowReturn
gst_v4l2_buffer_pool_dequeue (GstV4l2BufferPool * pool, GstBuffer ** buffer,
gboolean wait)
{
GstFlowReturn res;
GstV4l2Object *obj = pool->obj;
if ((res = gst_v4l2_buffer_pool_poll (pool, wait)) < GST_FLOW_OK)
goto poll_failed;
if (res == GST_FLOW_CUSTOM_SUCCESS) {
GST_LOG_OBJECT (pool, "nothing to dequeue");
goto done;
}
GST_LOG_OBJECT (pool, "dequeueing a buffer");
if (obj->can_wait_event && gst_poll_fd_has_pri (pool->poll, &pool->pollfd)) {
return gst_v4l2_buffer_pool_dqevent (pool);
}
GST_LOG_OBJECT (pool, "dequeueing a buffer");
return gst_v4l2_buffer_pool_dqbuf (pool, buffer);
/* ERRORS */
poll_failed:
{
GST_DEBUG_OBJECT (pool, "poll error %s", gst_flow_get_name (res));
return res;
}
done:
return res;
}
static GstFlowReturn static GstFlowReturn
gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params) GstBufferPoolAcquireParams * params)
...@@ -1407,7 +1453,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, ...@@ -1407,7 +1453,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
/* just dequeue a buffer, we basically use the queue of v4l2 as the /* just dequeue a buffer, we basically use the queue of v4l2 as the
* storage for our buffers. This function does poll first so we can * storage for our buffers. This function does poll first so we can
* interrupt it fine. */ * interrupt it fine. */
ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer, TRUE); ret = gst_v4l2_buffer_pool_dequeue (pool, buffer, TRUE);
break; break;
} }
default: default:
...@@ -1680,8 +1726,10 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) ...@@ -1680,8 +1726,10 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps)
gst_poll_add_fd (pool->poll, &pool->pollfd); gst_poll_add_fd (pool->poll, &pool->pollfd);
if (V4L2_TYPE_IS_OUTPUT (obj->type)) if (V4L2_TYPE_IS_OUTPUT (obj->type))
gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE); gst_poll_fd_ctl_write (pool->poll, &pool->pollfd, TRUE);
else else {
gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE); gst_poll_fd_ctl_read (pool->poll, &pool->pollfd, TRUE);
gst_poll_fd_ctl_pri (pool->poll, &pool->pollfd, TRUE);
}
pool->video_fd = fd; pool->video_fd = fd;
pool->obj = obj; pool->obj = obj;
...@@ -1866,8 +1914,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -1866,8 +1914,9 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
} }
/* buffer not from our pool, grab a frame and copy it into the target */ /* buffer not from our pool, grab a frame and copy it into the target */
if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp, TRUE)) if ((ret =
!= GST_FLOW_OK) gst_v4l2_buffer_pool_dequeue (pool, &tmp,
TRUE)) != GST_FLOW_OK)
goto done; goto done;
/* An empty buffer on capture indicates the end of stream */ /* An empty buffer on capture indicates the end of stream */
...@@ -2022,7 +2071,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -2022,7 +2071,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
gst_buffer_unref (to_queue); gst_buffer_unref (to_queue);
/* release as many buffer as possible */ /* release as many buffer as possible */
while (gst_v4l2_buffer_pool_dqbuf (pool, &buffer, FALSE) == while (gst_v4l2_buffer_pool_dequeue (pool, &buffer, FALSE) ==
GST_FLOW_OK) { GST_FLOW_OK) {
if (buffer->pool == NULL) if (buffer->pool == NULL)
gst_v4l2_buffer_pool_release_buffer (bpool, buffer); gst_v4l2_buffer_pool_release_buffer (bpool, buffer);
...@@ -2031,7 +2080,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) ...@@ -2031,7 +2080,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf)
if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) {
/* all buffers are queued, try to dequeue one and release it back /* all buffers are queued, try to dequeue one and release it back
* into the pool so that _acquire can get to it again. */ * into the pool so that _acquire can get to it again. */
ret = gst_v4l2_buffer_pool_dqbuf (pool, &buffer, TRUE); ret = gst_v4l2_buffer_pool_dequeue (pool, &buffer, TRUE);
if (ret == GST_FLOW_OK && buffer->pool == NULL) if (ret == GST_FLOW_OK && buffer->pool == NULL)
/* release the rendered buffer back into the pool. This wakes up any /* release the rendered buffer back into the pool. This wakes up any
* thread waiting for a buffer in _acquire(). */ * thread waiting for a buffer in _acquire(). */
......
...@@ -42,8 +42,8 @@ G_BEGIN_DECLS ...@@ -42,8 +42,8 @@ G_BEGIN_DECLS
#define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj)) #define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj))
/* This flow return is used to indicated that the last buffer of a /* This flow return is used to indicated that the last buffer of a
* drain or a resoltuion change has been found. This should normally * drain has been found. This should normally only occure for
* only occure for mem-2-mem devices. */ * mem-2-mem devices. */
#define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS #define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS
/* This flow return is used to indicated that the returned buffer was marked /* This flow return is used to indicated that the returned buffer was marked
...@@ -51,6 +51,11 @@ G_BEGIN_DECLS ...@@ -51,6 +51,11 @@ G_BEGIN_DECLS
* simply waiting for next buffer. */ * simply waiting for next buffer. */
#define GST_V4L2_FLOW_CORRUPTED_BUFFER GST_FLOW_CUSTOM_SUCCESS_1 #define GST_V4L2_FLOW_CORRUPTED_BUFFER GST_FLOW_CUSTOM_SUCCESS_1
/* This flow return is used to indicated that the last buffer of a
* resoltuion change has been found. This should normally only
* occure for mem-2-mem devices. */
#define GST_V4L2_FLOW_SOURCE_CHANGE GST_FLOW_CUSTOM_SUCCESS_2
struct _GstV4l2BufferPool struct _GstV4l2BufferPool
{ {
GstBufferPool parent; GstBufferPool parent;
......
...@@ -535,6 +535,10 @@ gst_v4l2_object_new (GstElement * element, ...@@ -535,6 +535,10 @@ gst_v4l2_object_new (GstElement * element,
v4l2object->munmap = munmap; v4l2object->munmap = munmap;
} }
v4l2object->poll = gst_poll_new (TRUE);
v4l2object->can_wait_event = FALSE;
v4l2object->can_poll_device = TRUE;
return v4l2object; return v4l2object;
} }
...@@ -546,6 +550,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object) ...@@ -546,6 +550,8 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
{ {
g_return_if_fail (v4l2object != NULL); g_return_if_fail (v4l2object != NULL);
gst_poll_free (v4l2object->poll);
g_free (v4l2object->videodev); g_free (v4l2object->videodev);
g_free (v4l2object->channel); g_free (v4l2object->channel);
...@@ -906,6 +912,15 @@ gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other) ...@@ -906,6 +912,15 @@ gst_v4l2_object_open_shared (GstV4l2Object * v4l2object, GstV4l2Object * other)
ret = gst_v4l2_dup (v4l2object, other); ret = gst_v4l2_dup (v4l2object, other);
if (ret && !V4L2_TYPE_IS_OUTPUT (v4l2object->type)) {
gst_poll_fd_init (&v4l2object->pollfd);
v4l2object->pollfd.fd = v4l2object->video_fd;
gst_poll_add_fd (v4l2object->poll, &v4l2object->pollfd);
/* used for dequeue event */
gst_poll_fd_ctl_read (v4l2object->poll, &v4l2object->pollfd, TRUE);
gst_poll_fd_ctl_pri (v4l2object->poll, &v4l2object->pollfd, TRUE);
}
return ret; return ret;
} }
...@@ -3881,6 +3896,93 @@ gst_v4l2_object_try_format (GstV4l2Object * v4l2object, GstCaps * caps, ...@@ -3881,6 +3896,93 @@ gst_v4l2_object_try_format (GstV4l2Object * v4l2object, GstCaps * caps,
return gst_v4l2_object_set_format_full (v4l2object, caps, TRUE, error); return gst_v4l2_object_set_format_full (v4l2object, caps, TRUE, error);
} }
GstFlowReturn
gst_v4l2_object_poll (GstV4l2Object * v4l2object)
{
gint ret;
if (!v4l2object->can_poll_device)
goto done;
GST_LOG_OBJECT (v4l2object, "polling device");
again:
ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
if (G_UNLIKELY (ret < 0)) {
switch (errno) {
case EBUSY:
goto stopped;
case EAGAIN:
case EINTR:
goto again;
case ENXIO:
GST_WARNING_OBJECT (v4l2object,
"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;
done:
return GST_FLOW_OK;
/* ERRORS */
stopped:
{
GST_DEBUG_OBJECT (v4l2object, "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;
}
}
GstFlowReturn
gst_v4l2_object_dqevent (GstV4l2Object * v4l2object)
{
GstFlowReturn res;
struct v4l2_event evt;
if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK)
goto poll_failed;
memset (&evt, 0x00, sizeof (struct v4l2_event));
if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_DQEVENT, &evt) < 0)
goto dqevent_failed;
switch (evt.type) {
case V4L2_EVENT_SOURCE_CHANGE:
return GST_V4L2_FLOW_SOURCE_CHANGE;
case V4L2_EVENT_EOS:
return GST_V4L2_FLOW_LAST_BUFFER;
default:
break;
}
return GST_FLOW_OK;
/* ERRORS */
poll_failed:
{
GST_DEBUG_OBJECT (v4l2object, "poll error %s", gst_flow_get_name (res));
return res;
}
dqevent_failed:
{
return GST_FLOW_ERROR;
}
}
/** /**
* gst_v4l2_object_acquire_format: * gst_v4l2_object_acquire_format:
* @v4l2object: the object * @v4l2object: the object
...@@ -4128,6 +4230,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object) ...@@ -4128,6 +4230,8 @@ gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing"); GST_LOG_OBJECT (v4l2object->dbg_obj, "start flushing");
gst_poll_set_flushing (v4l2object->poll, TRUE);
if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool)) if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
gst_buffer_pool_set_flushing (v4l2object->pool, TRUE); gst_buffer_pool_set_flushing (v4l2object->pool, TRUE);
...@@ -4144,6 +4248,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object) ...@@ -4144,6 +4248,8 @@ gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool)) if (v4l2object->pool && gst_buffer_pool_is_active (v4l2object->pool))
gst_buffer_pool_set_flushing (v4l2object->pool, FALSE); gst_buffer_pool_set_flushing (v4l2object->pool, FALSE);
gst_poll_set_flushing (v4l2object->poll, FALSE);
return ret; return ret;
} }
......
...@@ -130,6 +130,9 @@ struct _GstV4l2Object { ...@@ -130,6 +130,9 @@ struct _GstV4l2Object {
/* the video-device's file descriptor */ /* the video-device's file descriptor */
gint video_fd; gint video_fd;
GstV4l2IOMode mode; GstV4l2IOMode mode;
GstPoll *poll; /* a poll for video_fd */
GstPollFD pollfd;
gboolean can_poll_device;
gboolean active; gboolean active;
gboolean streaming; gboolean streaming;
...@@ -216,6 +219,8 @@ struct _GstV4l2Object { ...@@ -216,6 +219,8 @@ struct _GstV4l2Object {
* on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify * on slow USB firmwares. When this is set, gst_v4l2_set_format() will modify
* the caps to reflect what was negotiated during fixation */ * the caps to reflect what was negotiated during fixation */
gboolean skip_try_fmt_probes; gboolean skip_try_fmt_probes;
gboolean can_wait_event;
gboolean need_wait_event;
}; };
struct _GstV4l2ObjectClassHelper { struct _GstV4l2ObjectClassHelper {
...@@ -299,6 +304,7 @@ gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object); ...@@ -299,6 +304,7 @@ gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object);
GstCaps * gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter); GstCaps * gst_v4l2_object_probe_caps (GstV4l2Object * v4l2object, GstCaps * filter);
GstCaps * gst_v4l2_object_get_caps (GstV4l2Object * v4l2object, GstCaps * filter); GstCaps * gst_v4l2_object_get_caps (GstV4l2Object * v4l2object, GstCaps * filter);
GstFlowReturn gst_v4l2_object_dqevent (GstV4l2Object * v4l2object);
gboolean gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info); gboolean gst_v4l2_object_acquire_format (GstV4l2Object * v4l2object, GstVideoInfo * info);
gboolean gst_v4l2_object_set_crop (GstV4l2Object * obj); gboolean gst_v4l2_object_set_crop (GstV4l2Object * obj);
......
This diff is collapsed.
...@@ -509,6 +509,43 @@ gst_v4l2_adjust_buf_type (GstV4l2Object * v4l2object) ...@@ -509,6 +509,43 @@ gst_v4l2_adjust_buf_type (GstV4l2Object * v4l2object)
} }
} }
gboolean
gst_v4l2_subscribe_event (GstV4l2Object * v4l2object)
{
GstElement *e;
struct v4l2_event_subscription sub;
e = v4l2object->element;
GST_DEBUG_OBJECT (e, "subscribe event");
if (!GST_V4L2_IS_OPEN (v4l2object))
return FALSE;
memset (&sub, 0, sizeof (struct v4l2_event_subscription));
sub.type = V4L2_EVENT_SOURCE_CHANGE;
if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_SUBSCRIBE_EVENT,
&sub) < 0)
goto failed;
sub.type = V4L2_EVENT_EOS;
if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_SUBSCRIBE_EVENT,
&sub) < 0)
goto failed;
v4l2object->can_wait_event = TRUE;
return TRUE;
/* ERRORS */
failed:
{
GST_WARNING_OBJECT (e, "Cannot subscribe V4L2_EVENT_SOURCE_CHANGE or "
"V4L2_EVENT_EOS event for device '%s'.", v4l2object->videodev);
return TRUE;
}
}
/****************************************************** /******************************************************
* gst_v4l2_open(): * gst_v4l2_open():
* open the video device (v4l2object->videodev) * open the video device (v4l2object->videodev)
...@@ -591,6 +628,8 @@ gst_v4l2_open (GstV4l2Object * v4l2object) ...@@ -591,6 +628,8 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
if (v4l2object->extra_controls) if (v4l2object->extra_controls)
gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls); gst_v4l2_set_controls (v4l2object, v4l2object->extra_controls);
gst_v4l2_subscribe_event (v4l2object);
/* UVC devices are never interlaced, and doing VIDIOC_TRY_FMT on them /* UVC devices are never interlaced, and doing VIDIOC_TRY_FMT on them
* causes expensive and slow USB IO, so don't probe them for interlaced * causes expensive and slow USB IO, so don't probe them for interlaced
*/ */
...@@ -689,6 +728,7 @@ gst_v4l2_dup (GstV4l2Object * v4l2object, GstV4l2Object * other) ...@@ -689,6 +728,7 @@ gst_v4l2_dup (GstV4l2Object * v4l2object, GstV4l2Object * other)
v4l2object->never_interlaced = other->never_interlaced; v4l2object->never_interlaced = other->never_interlaced;
v4l2object->no_initial_format = other->no_initial_format; v4l2object->no_initial_format = other->no_initial_format;
v4l2object->can_wait_event = other->can_wait_event;
return TRUE; return TRUE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment