Wait for a frame to become available before capturing it
authorSjoerd Simons <sjoerd.simons@collabora.co.uk>
Sun, 1 Mar 2009 18:55:26 +0000 (19:55 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Sun, 1 Mar 2009 18:55:26 +0000 (19:55 +0100)
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.

sys/v4l2/gstv4l2object.c
sys/v4l2/gstv4l2object.h
sys/v4l2/gstv4l2src.c
sys/v4l2/v4l2_calls.c
sys/v4l2/v4l2src_calls.c

index ffc1046..d848ccf 100644 (file)
@@ -270,6 +270,7 @@ gst_v4l2_object_new (GstElement * element,
   v4l2object->update_fps_func = update_fps_func;
 
   v4l2object->video_fd = -1;
+  v4l2object->poll = gst_poll_new (TRUE);
   v4l2object->buffer = NULL;
   v4l2object->videodev = g_strdup (DEFAULT_PROP_DEVICE);
 
@@ -290,6 +291,9 @@ gst_v4l2_object_destroy (GstV4l2Object * v4l2object)
   if (v4l2object->videodev)
     g_free (v4l2object->videodev);
 
+  if (v4l2object->poll)
+    gst_poll_free (v4l2object->poll);
+
   if (v4l2object->channel)
     g_free (v4l2object->channel);
 
index 5eb557e..c5bc3cb 100644 (file)
@@ -71,6 +71,7 @@ struct _GstV4l2Object {
 
   /* the video-device's file descriptor */
   gint video_fd;
+  GstPoll * poll;
 
   /* the video buffer (mmap()'ed) */
   guint8 **buffer;
index 9f96a21..141fb7b 100644 (file)
@@ -240,6 +240,10 @@ static void gst_v4l2src_finalize (GstV4l2Src * v4l2src);
 /* basesrc methods */
 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_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->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2src_set_caps);
   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->query = GST_DEBUG_FUNCPTR (gst_v4l2src_query);
   basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_v4l2src_fixate);
@@ -1176,6 +1182,29 @@ gst_v4l2src_start (GstBaseSrc * src)
 }
 
 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
 gst_v4l2src_stop (GstBaseSrc * src)
 {
   GstV4l2Src *v4l2src = GST_V4L2SRC (src);
@@ -1203,6 +1232,7 @@ static GstFlowReturn
 gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
 {
   gint amount;
+  gint ret;
 
   gint buffersize;
 
@@ -1211,6 +1241,18 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
   *buf = gst_buffer_new_and_alloc (buffersize);
 
   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 =
         v4l2_read (v4l2src->v4l2object->video_fd, GST_BUFFER_DATA (*buf),
         buffersize);
@@ -1270,6 +1312,17 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
   return GST_FLOW_OK;
 
   /* 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:
   {
     GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
index a6b4b7d..b1875a8 100644 (file)
@@ -400,6 +400,7 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
 {
   struct stat st;
   int libv4l2_fd;
+  GstPollFD pollfd = GST_POLL_FD_INIT;
 
   GST_DEBUG_OBJECT (v4l2object->element, "Trying to open device %s",
       v4l2object->videodev);
@@ -453,6 +454,10 @@ gst_v4l2_open (GstV4l2Object * v4l2object)
       "Opened device '%s' (%s) successfully",
       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;
 
   /* ERRORS */
@@ -508,6 +513,7 @@ error:
 gboolean
 gst_v4l2_close (GstV4l2Object * v4l2object)
 {
+  GstPollFD pollfd = GST_POLL_FD_INIT;
   GST_DEBUG_OBJECT (v4l2object->element, "Trying to close %s",
       v4l2object->videodev);
 
@@ -516,6 +522,8 @@ gst_v4l2_close (GstV4l2Object * v4l2object)
 
   /* close device */
   v4l2_close (v4l2object->video_fd);
+  pollfd.fd = v4l2object->video_fd;
+  gst_poll_remove_fd (v4l2object->poll, &pollfd);
   v4l2object->video_fd = -1;
 
   /* empty lists */
index 971868b..5abfda3 100644 (file)
@@ -976,7 +976,7 @@ default_frame_sizes:
 /******************************************************
  * gst_v4l2src_grab_frame ():
  *   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
 gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
@@ -987,12 +987,28 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
   GstBuffer *pool_buffer;
   gboolean need_copy;
   gint index;
+  gint ret;
 
   memset (&buffer, 0x00, sizeof (buffer));
   buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   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,
         "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;
 
   /* 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:
   {
     GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,