mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 11:45:25 +00:00
Wait for a frame to become available before capturing it
Use GstPoll to wait for the fd of the video device to become readable before trying to capture a frame. This speeds up stopping v4l2src a lot as it no longer has to wait for the next frame, especially when capturing with low framerates or when the video device just never generates a frame (which seems a common issue for uvcvideo devices) Fixes bug #563574.
This commit is contained in:
parent
7087da96dc
commit
b6755a7000
5 changed files with 95 additions and 2 deletions
|
@ -270,6 +270,7 @@ gst_v4l2_object_new (GstElement * element,
|
||||||
v4l2object->update_fps_func = update_fps_func;
|
v4l2object->update_fps_func = update_fps_func;
|
||||||
|
|
||||||
v4l2object->video_fd = -1;
|
v4l2object->video_fd = -1;
|
||||||
|
v4l2object->poll = gst_poll_new (TRUE);
|
||||||
v4l2object->buffer = NULL;
|
v4l2object->buffer = NULL;
|
||||||
v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
|
v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
|
||||||
|
|
||||||
|
@ -290,6 +291,9 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
|
||||||
if (v4l2object->videodev)
|
if (v4l2object->videodev)
|
||||||
g_free (v4l2object->videodev);
|
g_free (v4l2object->videodev);
|
||||||
|
|
||||||
|
if (v4l2object->poll)
|
||||||
|
gst_poll_free (v4l2object->poll);
|
||||||
|
|
||||||
if (v4l2object->channel)
|
if (v4l2object->channel)
|
||||||
g_free (v4l2object->channel);
|
g_free (v4l2object->channel);
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ struct _GstV4l2Object {
|
||||||
|
|
||||||
/* the video-device's file descriptor */
|
/* the video-device's file descriptor */
|
||||||
gint video_fd;
|
gint video_fd;
|
||||||
|
GstPoll * poll;
|
||||||
|
|
||||||
/* the video buffer (mmap()'ed) */
|
/* the video buffer (mmap()'ed) */
|
||||||
guint8 **buffer;
|
guint8 **buffer;
|
||||||
|
|
|
@ -240,6 +240,10 @@ static void gst_v4l2src_finalize (GstV4l2Src * v4l2src);
|
||||||
/* basesrc methods */
|
/* basesrc methods */
|
||||||
static gboolean gst_v4l2src_start (GstBaseSrc * src);
|
static gboolean gst_v4l2src_start (GstBaseSrc * src);
|
||||||
|
|
||||||
|
static gboolean gst_v4l2src_unlock (GstBaseSrc * src);
|
||||||
|
|
||||||
|
static gboolean gst_v4l2src_unlock_stop (GstBaseSrc * src);
|
||||||
|
|
||||||
static gboolean gst_v4l2src_stop (GstBaseSrc * src);
|
static gboolean gst_v4l2src_stop (GstBaseSrc * src);
|
||||||
|
|
||||||
static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps);
|
static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps);
|
||||||
|
@ -312,6 +316,8 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass)
|
||||||
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_get_caps);
|
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_get_caps);
|
||||||
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_set_caps);
|
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_set_caps);
|
||||||
basesrc_class->start = GST_DEBUG_FUNCPTR (gst_v4l2src_start);
|
basesrc_class->start = GST_DEBUG_FUNCPTR (gst_v4l2src_start);
|
||||||
|
basesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_v4l2src_unlock);
|
||||||
|
basesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_v4l2src_unlock_stop);
|
||||||
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2src_stop);
|
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2src_stop);
|
||||||
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_v4l2src_query);
|
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_v4l2src_query);
|
||||||
basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_v4l2src_fixate);
|
basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_v4l2src_fixate);
|
||||||
|
@ -1175,6 +1181,29 @@ gst_v4l2src_start (GstBaseSrc * src)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_v4l2src_unlock (GstBaseSrc * src)
|
||||||
|
{
|
||||||
|
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (src, "Flushing");
|
||||||
|
gst_poll_set_flushing (v4l2src->v4l2object->poll, TRUE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_v4l2src_unlock_stop (GstBaseSrc * src)
|
||||||
|
{
|
||||||
|
GstV4l2Src *v4l2src = GST_V4L2SRC (src);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (src, "No longer flushing");
|
||||||
|
gst_poll_set_flushing (v4l2src->v4l2object->poll, FALSE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_v4l2src_stop (GstBaseSrc * src)
|
gst_v4l2src_stop (GstBaseSrc * src)
|
||||||
{
|
{
|
||||||
|
@ -1203,6 +1232,7 @@ static GstFlowReturn
|
||||||
gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
{
|
{
|
||||||
gint amount;
|
gint amount;
|
||||||
|
gint ret;
|
||||||
|
|
||||||
gint buffersize;
|
gint buffersize;
|
||||||
|
|
||||||
|
@ -1211,6 +1241,18 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
*buf = gst_buffer_new_and_alloc (buffersize);
|
*buf = gst_buffer_new_and_alloc (buffersize);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
ret = gst_poll_wait (v4l2src->v4l2object->poll, GST_CLOCK_TIME_NONE);
|
||||||
|
if (G_UNLIKELY (ret < 0)) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto stopped;
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
if (WSAGetLastError () != WSAEINTR)
|
||||||
|
goto select_error;
|
||||||
|
#else
|
||||||
|
if (errno != EAGAIN && errno != EINTR)
|
||||||
|
goto select_error;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
amount =
|
amount =
|
||||||
v4l2_read (v4l2src->v4l2object->video_fd, GST_BUFFER_DATA (*buf),
|
v4l2_read (v4l2src->v4l2object->video_fd, GST_BUFFER_DATA (*buf),
|
||||||
buffersize);
|
buffersize);
|
||||||
|
@ -1270,6 +1312,17 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
|
select_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
|
||||||
|
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
stopped:
|
||||||
|
{
|
||||||
|
GST_DEBUG ("stop called");
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
read_error:
|
read_error:
|
||||||
{
|
{
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
|
||||||
|
|
|
@ -400,6 +400,7 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int libv4l2_fd;
|
int libv4l2_fd;
|
||||||
|
GstPollFD pollfd = GST_POLL_FD_INIT;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
|
GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
|
||||||
v4l2object->videodev);
|
v4l2object->videodev);
|
||||||
|
@ -453,6 +454,10 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
|
||||||
"Opened device '%s' (%s) successfully",
|
"Opened device '%s' (%s) successfully",
|
||||||
v4l2object->vcap.card, v4l2object->videodev);
|
v4l2object->vcap.card, v4l2object->videodev);
|
||||||
|
|
||||||
|
pollfd.fd = v4l2object->video_fd;
|
||||||
|
gst_poll_add_fd (v4l2object->poll, &pollfd);
|
||||||
|
gst_poll_fd_ctl_read (v4l2object->poll, &pollfd, TRUE);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
|
@ -508,6 +513,7 @@ error:
|
||||||
gboolean
|
gboolean
|
||||||
gst_v4l2_close (GstV4l2Object * v4l2object)
|
gst_v4l2_close (GstV4l2Object * v4l2object)
|
||||||
{
|
{
|
||||||
|
GstPollFD pollfd = GST_POLL_FD_INIT;
|
||||||
GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
|
GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
|
||||||
v4l2object->videodev);
|
v4l2object->videodev);
|
||||||
|
|
||||||
|
@ -516,6 +522,8 @@ gst_v4l2_close (GstV4l2Object * v4l2object)
|
||||||
|
|
||||||
/* close device */
|
/* close device */
|
||||||
v4l2_close (v4l2object->video_fd);
|
v4l2_close (v4l2object->video_fd);
|
||||||
|
pollfd.fd = v4l2object->video_fd;
|
||||||
|
gst_poll_remove_fd (v4l2object->poll, &pollfd);
|
||||||
v4l2object->video_fd = -1;
|
v4l2object->video_fd = -1;
|
||||||
|
|
||||||
/* empty lists */
|
/* empty lists */
|
||||||
|
|
|
@ -976,7 +976,7 @@ default_frame_sizes:
|
||||||
/******************************************************
|
/******************************************************
|
||||||
* gst_v4l2src_grab_frame ():
|
* gst_v4l2src_grab_frame ():
|
||||||
* grab a frame for capturing
|
* grab a frame for capturing
|
||||||
* return value: GST_FLOW_OK or GST_FLOW_ERROR
|
* return value: GST_FLOW_OK, GST_FLOW_WRONG_STATE or GST_FLOW_ERROR
|
||||||
******************************************************/
|
******************************************************/
|
||||||
GstFlowReturn
|
GstFlowReturn
|
||||||
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
|
@ -987,12 +987,28 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
GstBuffer *pool_buffer;
|
GstBuffer *pool_buffer;
|
||||||
gboolean need_copy;
|
gboolean need_copy;
|
||||||
gint index;
|
gint index;
|
||||||
|
gint ret;
|
||||||
|
|
||||||
memset (&buffer, 0x00, sizeof (buffer));
|
memset (&buffer, 0x00, sizeof (buffer));
|
||||||
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
buffer.memory = V4L2_MEMORY_MMAP;
|
buffer.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
while (v4l2_ioctl (v4l2src->v4l2object->video_fd, VIDIOC_DQBUF, &buffer) < 0) {
|
for (;;) {
|
||||||
|
ret = gst_poll_wait (v4l2src->v4l2object->poll, GST_CLOCK_TIME_NONE);
|
||||||
|
if (G_UNLIKELY (ret < 0)) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto stopped;
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
if (WSAGetLastError () != WSAEINTR)
|
||||||
|
goto select_error;
|
||||||
|
#else
|
||||||
|
if (errno != EAGAIN && errno != EINTR)
|
||||||
|
goto select_error;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v4l2_ioctl (v4l2src->v4l2object->video_fd, VIDIOC_DQBUF, &buffer) >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
GST_WARNING_OBJECT (v4l2src,
|
GST_WARNING_OBJECT (v4l2src,
|
||||||
"problem grabbing frame %d (ix=%d), trials=%d, pool-ct=%d, buf.flags=%d",
|
"problem grabbing frame %d (ix=%d), trials=%d, pool-ct=%d, buf.flags=%d",
|
||||||
|
@ -1135,6 +1151,17 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
|
select_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL),
|
||||||
|
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
stopped:
|
||||||
|
{
|
||||||
|
GST_DEBUG ("stop called");
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
einval:
|
einval:
|
||||||
{
|
{
|
||||||
GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,
|
GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,
|
||||||
|
|
Loading…
Reference in a new issue