sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize) (gst_v4l2_buffer_class_init,...
authorAndy Wingo <wingo@pobox.com>
Tue, 12 Jun 2007 11:23:01 +0000 (11:23 +0000)
committerAndy Wingo <wingo@pobox.com>
Tue, 12 Jun 2007 11:23:01 +0000 (11:23 +0000)
Original commit message from CVS:
2007-06-12  Andy Wingo  <wingo@pobox.com>

* sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize)
(gst_v4l2_buffer_class_init, gst_v4l2_buffer_get_type)
(gst_v4l2_buffer_new): Behave more like ximagesink's buffers, with
finalization and resuscitation. No longer public.
(gst_v4l2_buffer_pool_finalize, gst_v4l2_buffer_pool_init)
(gst_v4l2_buffer_pool_class_init, gst_v4l2_buffer_pool_get_type)
(gst_v4l2_buffer_pool_new, gst_v4l2_buffer_pool_activate)
(gst_v4l2_buffer_pool_destroy): Make the pool follow common
miniobject semantics, and be threadsafe.
(gst_v4l2src_queue_frame): Remove this function, as we just call
the ioctls directly in the two places where we queue buffers.
(gst_v4l2src_grab_frame): Return a flowreturn and fill the buffer
directly.
(gst_v4l2src_capture_init): Use the new buffer_pool_new function
to allocate the pool, which also preallocates the GstBuffers.
(gst_v4l2src_capture_start): Call buffer_pool_activate instead of
queueing the frames directly.

* sys/v4l2/gstv4l2src.h (struct _GstV4l2BufferPool): Make this a
real MiniObject instead of rolling our own refcounting and
finalizing. Give it a lock.
(struct _GstV4l2Buffer): Remove one intermediary object, having
the buffers hold the struct v4l2_buffer directly.

* sys/v4l2/gstv4l2src.c (gst_v4l2src_set_caps): Pass the caps to
capture_init so that it can set them on the buffers that it will
create.
(gst_v4l2src_get_read): For better or for worse, include the
timestamping and offsetting code here; really we should be using
bufferalloc though.
(gst_v4l2src_get_mmap): Just make grab_frame return one of our
preallocated, mmap'd buffers.

ChangeLog
sys/v4l2/gstv4l2src.c
sys/v4l2/gstv4l2src.h
sys/v4l2/v4l2src_calls.c
sys/v4l2/v4l2src_calls.h

index afff6d0..2887e5c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2007-06-12  Andy Wingo  <wingo@pobox.com>
+
+       * sys/v4l2/v4l2src_calls.c (gst_v4l2_buffer_finalize)
+       (gst_v4l2_buffer_class_init, gst_v4l2_buffer_get_type)
+       (gst_v4l2_buffer_new): Behave more like ximagesink's buffers, with
+       finalization and resuscitation. No longer public.
+       (gst_v4l2_buffer_pool_finalize, gst_v4l2_buffer_pool_init)
+       (gst_v4l2_buffer_pool_class_init, gst_v4l2_buffer_pool_get_type)
+       (gst_v4l2_buffer_pool_new, gst_v4l2_buffer_pool_activate)
+       (gst_v4l2_buffer_pool_destroy): Make the pool follow common
+       miniobject semantics, and be threadsafe.
+       (gst_v4l2src_queue_frame): Remove this function, as we just call
+       the ioctls directly in the two places where we queue buffers.
+       (gst_v4l2src_grab_frame): Return a flowreturn and fill the buffer
+       directly.
+       (gst_v4l2src_capture_init): Use the new buffer_pool_new function
+       to allocate the pool, which also preallocates the GstBuffers.
+       (gst_v4l2src_capture_start): Call buffer_pool_activate instead of
+       queueing the frames directly.
+
+       * sys/v4l2/gstv4l2src.h (struct _GstV4l2BufferPool): Make this a
+       real MiniObject instead of rolling our own refcounting and
+       finalizing. Give it a lock.
+       (struct _GstV4l2Buffer): Remove one intermediary object, having
+       the buffers hold the struct v4l2_buffer directly.
+
+       * sys/v4l2/gstv4l2src.c (gst_v4l2src_set_caps): Pass the caps to
+       capture_init so that it can set them on the buffers that it will
+       create.
+       (gst_v4l2src_get_read): For better or for worse, include the
+       timestamping and offsetting code here; really we should be using
+       bufferalloc though.
+       (gst_v4l2src_get_mmap): Just make grab_frame return one of our
+       preallocated, mmap'd buffers.
+
 2007-06-11  Wim Taymans  <wim@fluendo.com>
 
        Patch by: daniel fischer <dan at f3c dot com>
index 67be44e..d359948 100644 (file)
@@ -817,7 +817,7 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
     /* error already posted */
     return FALSE;
 
-  if (!gst_v4l2src_capture_init (v4l2src))
+  if (!gst_v4l2src_capture_init (v4l2src, caps))
     return FALSE;
 
   if (!gst_v4l2src_capture_start (v4l2src))
@@ -873,7 +873,7 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
 
   buffersize = v4l2src->frame_byte_size;
 
-  *buf = gst_v4l2src_buffer_new (v4l2src, buffersize, NULL, NULL);
+  *buf = gst_buffer_new_and_alloc (buffersize);
 
   do {
     amount =
@@ -892,6 +892,34 @@ gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf)
     }
   } while (TRUE);
 
+  GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
+  GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
+  /* timestamps, LOCK to get clock and base time. */
+  {
+    GstClock *clock;
+    GstClockTime timestamp;
+
+    GST_OBJECT_LOCK (v4l2src);
+    if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
+      /* we have a clock, get base time and ref clock */
+      timestamp = GST_ELEMENT (v4l2src)->base_time;
+      gst_object_ref (clock);
+    } else {
+      /* no clock, can't set timestamps */
+      timestamp = GST_CLOCK_TIME_NONE;
+    }
+    GST_OBJECT_UNLOCK (v4l2src);
+
+    if (clock) {
+      /* the time now is the time of the clock minus the base time */
+      timestamp = gst_clock_get_time (clock) - timestamp;
+      gst_object_unref (clock);
+    }
+
+    /* FIXME: use the timestamp from the buffer itself! */
+    GST_BUFFER_TIMESTAMP (*buf) = timestamp;
+  }
+
   return GST_FLOW_OK;
 
   /* ERRORS */
@@ -908,52 +936,7 @@ read_error:
 static GstFlowReturn
 gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf)
 {
-  gint i, num;
-
-  /* grab a frame from the device, post an error */
-  num = gst_v4l2src_grab_frame (v4l2src);
-  if (num == -1)
-    goto grab_failed;
-
-  i = v4l2src->frame_byte_size;
-
-  /* check if this is the last buffer in the queue. If so do a memcpy to put it back asap
-     to avoid framedrops and deadlocks because of stupid elements */
-  /* FIXME: we should use the userptr interface instead, will remove this
-     problem */
-  if (g_atomic_int_get (&v4l2src->pool->refcount) == v4l2src->num_buffers) {
-    GST_LOG_OBJECT (v4l2src, "using memcpy'd buffer");
-
-    *buf = gst_v4l2src_buffer_new (v4l2src, i, NULL, NULL);
-    memcpy (GST_BUFFER_DATA (*buf), v4l2src->pool->buffers[num].start, i);
-
-    /* posts an error message if something went wrong */
-    if (!gst_v4l2src_queue_frame (v4l2src, num))
-      goto queue_failed;
-  } else {
-    GST_LOG_OBJECT (v4l2src, "using mmap'd buffer");
-    *buf =
-        gst_v4l2src_buffer_new (v4l2src, i, v4l2src->pool->buffers[num].start,
-        &v4l2src->pool->buffers[num]);
-
-    /* no need to be careful here, both are > 0, because the element uses them */
-    g_atomic_int_inc (&v4l2src->pool->buffers[num].refcount);
-    g_atomic_int_inc (&v4l2src->pool->refcount);
-  }
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-grab_failed:
-  {
-    GST_DEBUG_OBJECT (v4l2src, "failed to grab a frame");
-    return GST_FLOW_ERROR;
-  }
-queue_failed:
-  {
-    GST_DEBUG_OBJECT (v4l2src, "failed to queue frame");
-    gst_buffer_unref (*buf);
-    return GST_FLOW_ERROR;
-  }
+  return gst_v4l2src_grab_frame (v4l2src, buf);
 }
 
 static GstFlowReturn
index 0162acd..809ddb4 100644 (file)
@@ -54,18 +54,22 @@ typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
 /* global info */
 struct _GstV4l2BufferPool
 {
-  gint refcount;                /* number of users: 1 for every buffer, 1 for element */
-  gint video_fd;
+  GstMiniObject parent;
+
+  GMutex *lock;
+  gboolean running; /* with lock */
+  gint num_live_buffers; /* with lock */
+  gint video_fd; /* a dup(2) of the v4l2object's video_fd */
   guint buffer_count;
-  GstV4l2Buffer *buffers;
+  GstV4l2Buffer **buffers; /* with lock; buffers[n] is NULL that buffer has been
+                            * dequeued and pushed out */
 };
 
-struct _GstV4l2Buffer
-{
-  struct v4l2_buffer buffer;
-  guint8 *start;
-  guint length;
-  gint refcount;                /* add 1 if in use by element, add 1 if in use by GstBuffer */
+struct _GstV4l2Buffer {
+  GstBuffer   buffer;
+
+  struct v4l2_buffer vbuffer;
+
   GstV4l2BufferPool *pool;
 };
 
index 24e265e..8397fd0 100644 (file)
@@ -56,6 +56,298 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug);
 #define MAP_FAILED ((caddr_t) -1)
 #endif
 
+
+#define GST_TYPE_V4L2_BUFFER (gst_v4l2_buffer_get_type())
+#define GST_IS_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER))
+#define GST_V4L2_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER, GstV4l2Buffer))
+
+static void
+gst_v4l2_buffer_finalize (GstV4l2Buffer * buffer)
+{
+  GstV4l2BufferPool *pool;
+  gboolean resuscitated = FALSE;
+
+  pool = buffer->pool;
+
+  GST_LOG ("finalizing buffer %d", buffer->vbuffer.index);
+
+  g_mutex_lock (pool->lock);
+  if (GST_BUFFER_SIZE (buffer) != 0)
+    /* BUFFER_SIZE is only set if the frame was dequeued */
+    pool->num_live_buffers--;
+
+  if (pool->running) {
+    if (ioctl (pool->video_fd, VIDIOC_QBUF, &buffer->vbuffer) < 0) {
+      GST_WARNING ("could not requeue buffer %d", buffer->vbuffer.index);
+    } else {
+      /* FIXME: check that the caps didn't change */
+      gst_buffer_ref (GST_BUFFER (buffer));
+      GST_BUFFER_SIZE (buffer) = 0;
+      pool->buffers[buffer->vbuffer.index] = buffer;
+      resuscitated = TRUE;
+    }
+  }
+  g_mutex_unlock (pool->lock);
+
+  if (!resuscitated) {
+    gst_mini_object_unref (GST_MINI_OBJECT (pool));
+    munmap (GST_BUFFER_DATA (buffer), buffer->vbuffer.length);
+  }
+}
+
+static void
+gst_v4l2_buffer_init (GstV4l2Buffer * xvimage, gpointer g_class)
+{
+  /* NOP */
+}
+
+static void
+gst_v4l2_buffer_class_init (gpointer g_class, gpointer class_data)
+{
+  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
+
+  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
+      gst_v4l2_buffer_finalize;
+}
+
+static GType
+gst_v4l2_buffer_get_type (void)
+{
+  static GType _gst_v4l2_buffer_type;
+
+  if (G_UNLIKELY (_gst_v4l2_buffer_type == 0)) {
+    static const GTypeInfo v4l2_buffer_info = {
+      sizeof (GstBufferClass),
+      NULL,
+      NULL,
+      gst_v4l2_buffer_class_init,
+      NULL,
+      NULL,
+      sizeof (GstV4l2Buffer),
+      0,
+      (GInstanceInitFunc) gst_v4l2_buffer_init,
+      NULL
+    };
+    _gst_v4l2_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
+        "GstV4l2Buffer", &v4l2_buffer_info, 0);
+  }
+  return _gst_v4l2_buffer_type;
+}
+
+static GstV4l2Buffer *
+gst_v4l2_buffer_new (GstV4l2BufferPool * pool, guint index, GstCaps * caps)
+{
+  GstV4l2Buffer *ret;
+
+  ret = (GstV4l2Buffer *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER);
+
+  ret->pool = pool;
+  gst_mini_object_ref (GST_MINI_OBJECT (pool));
+  memset (&ret->vbuffer, 0x00, sizeof (ret->vbuffer));
+  ret->vbuffer.index = index;
+  ret->vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+  ret->vbuffer.memory = V4L2_MEMORY_MMAP;
+
+  if (ioctl (pool->video_fd, VIDIOC_QUERYBUF, &ret->vbuffer) < 0)
+    goto querybuf_failed;
+
+  GST_BUFFER_DATA (ret) = mmap (0, ret->vbuffer.length,
+      PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
+      ret->vbuffer.m.offset);
+
+  if (GST_BUFFER_DATA (ret) == MAP_FAILED)
+    goto mmap_failed;
+
+  GST_BUFFER_FLAG_SET (ret, GST_BUFFER_FLAG_READONLY);
+  gst_buffer_set_caps (GST_BUFFER (ret), caps);
+
+  return ret;
+
+querybuf_failed:
+  {
+    gint errnosave = errno;
+
+    GST_WARNING ("Failed QUERYBUF: %s", g_strerror (errnosave));
+    gst_buffer_unref (GST_BUFFER (ret));
+    errno = errnosave;
+    return NULL;
+  }
+mmap_failed:
+  {
+    gint errnosave = errno;
+
+    GST_WARNING ("Failed to mmap: %s", g_strerror (errnosave));
+    gst_buffer_unref (GST_BUFFER (ret));
+    errno = errnosave;
+    return NULL;
+  }
+}
+
+#define GST_TYPE_V4L2_BUFFER_POOL (gst_v4l2_buffer_pool_get_type())
+#define GST_IS_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER_POOL))
+#define GST_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER_POOL, GstV4l2BufferPool))
+
+static void
+gst_v4l2_buffer_pool_finalize (GstV4l2BufferPool * pool)
+{
+  g_mutex_free (pool->lock);
+  pool->lock = NULL;
+
+  if (pool->video_fd >= 0)
+    close (pool->video_fd);
+
+  if (pool->buffers)
+    g_free (pool->buffers);
+  pool->buffers = NULL;
+}
+
+static void
+gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool, gpointer g_class)
+{
+  pool->lock = g_mutex_new ();
+  pool->running = FALSE;
+  pool->num_live_buffers = 0;
+}
+
+static void
+gst_v4l2_buffer_pool_class_init (gpointer g_class, gpointer class_data)
+{
+  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
+
+  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
+      gst_v4l2_buffer_pool_finalize;
+}
+
+static GType
+gst_v4l2_buffer_pool_get_type (void)
+{
+  static GType _gst_v4l2_buffer_pool_type;
+
+  if (G_UNLIKELY (_gst_v4l2_buffer_pool_type == 0)) {
+    static const GTypeInfo v4l2_buffer_pool_info = {
+      sizeof (GstBufferClass),
+      NULL,
+      NULL,
+      gst_v4l2_buffer_pool_class_init,
+      NULL,
+      NULL,
+      sizeof (GstV4l2BufferPool),
+      0,
+      (GInstanceInitFunc) gst_v4l2_buffer_pool_init,
+      NULL
+    };
+    _gst_v4l2_buffer_pool_type = g_type_register_static (GST_TYPE_MINI_OBJECT,
+        "GstV4l2BufferPool", &v4l2_buffer_pool_info, 0);
+  }
+  return _gst_v4l2_buffer_pool_type;
+}
+
+static GstV4l2BufferPool *
+gst_v4l2_buffer_pool_new (GstV4l2Src * v4l2src, gint fd, gint num_buffers,
+    GstCaps * caps)
+{
+  GstV4l2BufferPool *pool;
+  gint n;
+
+  pool = (GstV4l2BufferPool *) gst_mini_object_new (GST_TYPE_V4L2_BUFFER_POOL);
+
+  pool->video_fd = dup (fd);
+  if (pool->video_fd < 0)
+    goto dup_failed;
+
+  pool->buffer_count = num_buffers;
+  pool->buffers = g_new0 (GstV4l2Buffer *, num_buffers);
+
+  for (n = 0; n < num_buffers; n++) {
+    pool->buffers[n] = gst_v4l2_buffer_new (pool, n, caps);
+    if (!pool->buffers[n])
+      goto buffer_new_failed;
+  }
+
+  return pool;
+
+dup_failed:
+  {
+    gint errnosave = errno;
+
+    gst_mini_object_unref (GST_MINI_OBJECT (pool));
+
+    errno = errnosave;
+
+    return NULL;
+  }
+buffer_new_failed:
+  {
+    gint errnosave = errno;
+
+    gst_mini_object_unref (GST_MINI_OBJECT (pool));
+
+    errno = errnosave;
+
+    return NULL;
+  }
+}
+
+static gboolean
+gst_v4l2_buffer_pool_activate (GstV4l2BufferPool * pool, GstV4l2Src * v4l2src)
+{
+  gint n;
+
+  g_mutex_lock (pool->lock);
+
+  for (n = 0; n < pool->buffer_count; n++) {
+    struct v4l2_buffer *buf = &pool->buffers[n]->vbuffer;
+
+    if (ioctl (pool->video_fd, VIDIOC_QBUF, buf) < 0)
+      goto queue_failed;
+  }
+
+  pool->running = TRUE;
+
+  g_mutex_unlock (pool->lock);
+
+  return TRUE;
+
+queue_failed:
+  {
+    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
+        (_("Could not enqueue buffers in device '%s'."),
+            v4l2src->v4l2object->videodev),
+        ("enqueing buffer %d/%d failed: %s",
+            n, v4l2src->num_buffers, g_strerror (errno)));
+    g_mutex_unlock (pool->lock);
+    return FALSE;
+  }
+}
+
+static void
+gst_v4l2_buffer_pool_destroy (GstV4l2BufferPool * pool)
+{
+  gint n;
+
+  g_mutex_lock (pool->lock);
+  pool->running = FALSE;
+  g_mutex_unlock (pool->lock);
+
+  /* after this point, no more buffers will be queued or dequeued; no buffer
+   * from pool->buffers that is NULL will be set to a buffer, and no buffer that
+   * is not NULL will be pushed out. */
+
+  for (n = 0; n < pool->buffer_count; n++) {
+    GstBuffer *buf;
+
+    g_mutex_lock (pool->lock);
+    buf = GST_BUFFER (pool->buffers[n]);
+    g_mutex_unlock (pool->lock);
+
+    if (buf)
+      /* we own the ref if the buffer is in pool->buffers; drop it. */
+      gst_buffer_unref (buf);
+  }
+
+  gst_mini_object_unref (GST_MINI_OBJECT (pool));
+}
+
 /******************************************************
  * gst_v4l2src_fill_format_list():
  *   create list of supported capture formats
@@ -365,40 +657,12 @@ unknown_type:
 }
 
 /******************************************************
- * gst_v4l2src_queue_frame():
- *   queue a frame for capturing
- * return value: TRUE on success, FALSE on error
- ******************************************************/
-gboolean
-gst_v4l2src_queue_frame (GstV4l2Src * v4l2src, guint i)
-{
-  GST_LOG_OBJECT (v4l2src, "queueing frame %u", i);
-
-  if (ioctl (v4l2src->v4l2object->video_fd, VIDIOC_QBUF,
-          &v4l2src->pool->buffers[i].buffer) < 0)
-    goto qbuf_failed;
-
-  return TRUE;
-
-  /* ERRORS */
-qbuf_failed:
-  {
-    GST_ELEMENT_ERROR (v4l2src, RESOURCE, WRITE,
-        (_("Could not exchange data with device '%s'."),
-            v4l2src->v4l2object->videodev),
-        ("Error queueing buffer %u on device %s. system error: %s", i,
-            v4l2src->v4l2object->videodev, g_strerror (errno)));
-    return FALSE;
-  }
-}
-
-/******************************************************
  * gst_v4l2src_grab_frame ():
  *   grab a frame for capturing
  * return value: The captured frame number or -1 on error.
  ******************************************************/
-gint
-gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
+GstFlowReturn
+gst_v4l2src_grab_frame (GstV4l2Src * v4l2src, GstBuffer ** buf)
 {
 #define NUM_TRIALS 50
   struct v4l2_buffer buffer;
@@ -407,12 +671,13 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
   memset (&buffer, 0x00, sizeof (buffer));
   buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   buffer.memory = V4L2_MEMORY_MMAP;
+
   while (ioctl (v4l2src->v4l2object->video_fd, VIDIOC_DQBUF, &buffer) < 0) {
 
     GST_LOG_OBJECT (v4l2src,
         "problem grabbing frame %d (ix=%d), trials=%d, pool-ct=%d, buf.flags=%d",
-        buffer.sequence, buffer.index, trials, v4l2src->pool->refcount,
-        buffer.flags);
+        buffer.sequence, buffer.index, trials,
+        GST_MINI_OBJECT_REFCOUNT (v4l2src->pool), buffer.flags);
 
     /* if the sync() got interrupted, we can retry */
     switch (errno) {
@@ -471,10 +736,50 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src)
     }
   }
 
+  g_mutex_lock (v4l2src->pool->lock);
+
+  *buf = GST_BUFFER (v4l2src->pool->buffers[buffer.index]);
+  v4l2src->pool->buffers[buffer.index] = NULL;
+  v4l2src->pool->num_live_buffers++;
+
+  g_mutex_unlock (v4l2src->pool->lock);
+
+  /* this can change at every frame, esp. with jpeg */
+  GST_BUFFER_SIZE (*buf) = buffer.bytesused;
+
+  GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
+  GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
+
+  /* timestamps, LOCK to get clock and base time. */
+  {
+    GstClock *clock;
+    GstClockTime timestamp;
+
+    GST_OBJECT_LOCK (v4l2src);
+    if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
+      /* we have a clock, get base time and ref clock */
+      timestamp = GST_ELEMENT (v4l2src)->base_time;
+      gst_object_ref (clock);
+    } else {
+      /* no clock, can't set timestamps */
+      timestamp = GST_CLOCK_TIME_NONE;
+    }
+    GST_OBJECT_UNLOCK (v4l2src);
+
+    if (clock) {
+      /* the time now is the time of the clock minus the base time */
+      timestamp = gst_clock_get_time (clock) - timestamp;
+      gst_object_unref (clock);
+    }
+
+    /* FIXME: use the timestamp from the buffer itself! */
+    GST_BUFFER_TIMESTAMP (*buf) = timestamp;
+  }
+
   GST_LOG_OBJECT (v4l2src, "grabbed frame %d (ix=%d), pool-ct=%d",
-      buffer.sequence, buffer.index, v4l2src->pool->refcount);
+      buffer.sequence, buffer.index, v4l2src->pool->num_live_buffers);
 
-  return buffer.index;
+  return GST_FLOW_OK;
 
   /* ERRORS */
 einval:
@@ -486,13 +791,13 @@ einval:
                 " or no buffers have been allocated yet, or the userptr"
                 " or length are invalid. device %s"),
             v4l2src->v4l2object->videodev));
-    return -1;
+    return GST_FLOW_ERROR;
   }
 enomem:
   {
     GST_ELEMENT_ERROR (v4l2src, RESOURCE, FAILED,
         (_("Failed trying to get video frames from device '%s'. Not enough memory."), v4l2src->v4l2object->videodev), (_("insufficient memory to enqueue a user pointer buffer. device %s."), v4l2src->v4l2object->videodev));
-    return -1;
+    return GST_FLOW_ERROR;
   }
 too_many_trials:
   {
@@ -501,7 +806,7 @@ too_many_trials:
             v4l2src->v4l2object->videodev),
         (_("Failed after %d tries. device %s. system error: %s"),
             NUM_TRIALS, v4l2src->v4l2object->videodev, g_strerror (errno)));
-    return -1;
+    return GST_FLOW_ERROR;
   }
 qbuf_failed:
   {
@@ -510,7 +815,7 @@ qbuf_failed:
             v4l2src->v4l2object->videodev),
         ("Error queueing buffer on device %s. system error: %s",
             v4l2src->v4l2object->videodev, g_strerror (errno)));
-    return -1;
+    return GST_FLOW_ERROR;
   }
 }
 
@@ -646,9 +951,8 @@ invalid_framerate:
  * return value: TRUE on success, FALSE on error
  ******************************************************/
 gboolean
-gst_v4l2src_capture_init (GstV4l2Src * v4l2src)
+gst_v4l2src_capture_init (GstV4l2Src * v4l2src, GstCaps * caps)
 {
-  gint n;
   gint fd = v4l2src->v4l2object->video_fd;
   struct v4l2_requestbuffers breq;
 
@@ -681,33 +985,9 @@ gst_v4l2src_capture_init (GstV4l2Src * v4l2src)
     /* Map the buffers */
     GST_LOG_OBJECT (v4l2src, "initiating buffer pool");
 
-    v4l2src->pool = g_new (GstV4l2BufferPool, 1);
-    gst_atomic_int_set (&v4l2src->pool->refcount, 1);
-    v4l2src->pool->video_fd = fd;
-    v4l2src->pool->buffer_count = v4l2src->num_buffers;
-    v4l2src->pool->buffers = g_new0 (GstV4l2Buffer, v4l2src->num_buffers);
-
-    for (n = 0; n < v4l2src->num_buffers; n++) {
-      GstV4l2Buffer *buffer = &v4l2src->pool->buffers[n];
-
-      gst_atomic_int_set (&buffer->refcount, 1);
-      buffer->pool = v4l2src->pool;
-      memset (&buffer->buffer, 0x00, sizeof (buffer->buffer));
-      buffer->buffer.index = n;
-      buffer->buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-      buffer->buffer.memory = V4L2_MEMORY_MMAP;
-
-      if (ioctl (fd, VIDIOC_QUERYBUF, &buffer->buffer) < 0)
-        goto querybuf_failed;
-
-      buffer->start = mmap (0, buffer->buffer.length, PROT_READ | PROT_WRITE,
-          MAP_SHARED, fd, buffer->buffer.m.offset);
-
-      if (buffer->start == MAP_FAILED)
-        goto mmap_failed;
-
-      buffer->length = buffer->buffer.length;
-    }
+    if (!(v4l2src->pool = gst_v4l2_buffer_pool_new (v4l2src, fd,
+                v4l2src->num_buffers, caps)))
+      goto buffer_pool_new_failed;
 
     GST_INFO_OBJECT (v4l2src, "capturing buffers via mmap()");
     v4l2src->use_mmap = TRUE;
@@ -742,22 +1022,12 @@ no_buffers:
             breq.count, v4l2src->v4l2object->videodev, GST_V4L2_MIN_BUFFERS));
     return FALSE;
   }
-querybuf_failed:
+buffer_pool_new_failed:
   {
     GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
-        (_("Could not retrieve buffer from device '%s'"),
+        (_("Could not map buffers from device '%s'"),
             v4l2src->v4l2object->videodev),
-        ("Failed QUERYBUF: %s", g_strerror (errno)));
-    gst_v4l2src_capture_deinit (v4l2src);
-    return FALSE;
-  }
-mmap_failed:
-  {
-    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
-        (_("Could not map memory in device '%s'."),
-            v4l2src->v4l2object->videodev),
-        ("mmap failed: %s", g_strerror (errno)));
-    gst_v4l2src_capture_deinit (v4l2src);
+        ("Failed to create buffer pool: %s", g_strerror (errno)));
     return FALSE;
   }
 no_supported_capture_method:
@@ -779,7 +1049,6 @@ gboolean
 gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
 {
   gint type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  gint n;
   gint fd = v4l2src->v4l2object->video_fd;
 
   GST_DEBUG_OBJECT (v4l2src, "starting the capturing");
@@ -789,9 +1058,8 @@ gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
   v4l2src->quit = FALSE;
 
   if (v4l2src->use_mmap) {
-    for (n = 0; n < v4l2src->num_buffers; n++)
-      if (!gst_v4l2src_queue_frame (v4l2src, n))
-        goto queue_failed;
+    if (!gst_v4l2_buffer_pool_activate (v4l2src->pool, v4l2src))
+      goto pool_activate_failed;
 
     if (ioctl (fd, VIDIOC_STREAMON, &type) < 0)
       goto streamon_failed;
@@ -802,13 +1070,9 @@ gst_v4l2src_capture_start (GstV4l2Src * v4l2src)
   return TRUE;
 
   /* ERRORS */
-queue_failed:
+pool_activate_failed:
   {
-    GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ,
-        (_("Could not enqueue buffers in device '%s'."),
-            v4l2src->v4l2object->videodev),
-        ("enqueing buffer %d/%d failed: %s",
-            n, v4l2src->num_buffers, g_strerror (errno)));
+    /* already errored */
     return FALSE;
   }
 streamon_failed:
@@ -864,22 +1128,6 @@ streamoff_failed:
   }
 }
 
-static void
-gst_v4l2src_buffer_pool_free (GstV4l2BufferPool * pool, gboolean do_close)
-{
-  guint i;
-
-  for (i = 0; i < pool->buffer_count; i++) {
-    gst_atomic_int_set (&pool->buffers[i].refcount, 0);
-    munmap (pool->buffers[i].start, pool->buffers[i].length);
-  }
-  g_free (pool->buffers);
-  gst_atomic_int_set (&pool->refcount, 0);
-  if (do_close)
-    close (pool->video_fd);
-  g_free (pool);
-}
-
 /******************************************************
  * gst_v4l2src_capture_deinit():
  *   deinitialize the capture system
@@ -898,11 +1146,7 @@ gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src)
   }
 
   if (v4l2src->pool) {
-    if (g_atomic_int_dec_and_test (&v4l2src->pool->refcount)) {
-      /* FIXME: this is crack, should either use user pointer io method or some
-         strategy similar to what filesrc uses with automatic freeing */
-      gst_v4l2src_buffer_pool_free (v4l2src->pool, FALSE);
-    }
+    gst_v4l2_buffer_pool_destroy (v4l2src->pool);
     v4l2src->pool = NULL;
   }
 
@@ -964,134 +1208,3 @@ gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src,
 
   return TRUE;
 }
-
-#define GST_TYPE_V4L2SRC_BUFFER (gst_v4l2src_buffer_get_type())
-#define GST_IS_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2SRC_BUFFER))
-#define GST_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2SRC_BUFFER, GstV4l2SrcBuffer))
-
-typedef struct _GstV4l2SrcBuffer
-{
-  GstBuffer buffer;
-
-  GstV4l2Buffer *buf;
-} GstV4l2SrcBuffer;
-
-static void gst_v4l2src_buffer_class_init (gpointer g_class,
-    gpointer class_data);
-static void gst_v4l2src_buffer_init (GTypeInstance * instance,
-    gpointer g_class);
-static void gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer);
-
-GType
-gst_v4l2src_buffer_get_type (void)
-{
-  static GType _gst_v4l2src_buffer_type;
-
-  if (G_UNLIKELY (_gst_v4l2src_buffer_type == 0)) {
-    static const GTypeInfo v4l2src_buffer_info = {
-      sizeof (GstBufferClass),
-      NULL,
-      NULL,
-      gst_v4l2src_buffer_class_init,
-      NULL,
-      NULL,
-      sizeof (GstV4l2SrcBuffer),
-      0,
-      gst_v4l2src_buffer_init,
-      NULL
-    };
-    _gst_v4l2src_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
-        "GstV4l2SrcBuffer", &v4l2src_buffer_info, 0);
-  }
-  return _gst_v4l2src_buffer_type;
-}
-
-static void
-gst_v4l2src_buffer_class_init (gpointer g_class, gpointer class_data)
-{
-  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
-
-  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
-      gst_v4l2src_buffer_finalize;
-}
-
-static void
-gst_v4l2src_buffer_init (GTypeInstance * instance, gpointer g_class)
-{
-
-}
-
-static void
-gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer)
-{
-  GstV4l2Buffer *buf = v4l2src_buffer->buf;
-
-  if (buf) {
-    GST_LOG ("freeing buffer %p (nr. %d)", buf, buf->buffer.index);
-
-    if (!g_atomic_int_dec_and_test (&buf->refcount)) {
-      /* we're still in use, add to queue again
-       * note: this might fail because the device is already stopped (race)
-       */
-      if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0)
-        GST_INFO ("readding to queue failed, assuming video device is stopped");
-    }
-    if (g_atomic_int_dec_and_test (&buf->pool->refcount)) {
-      /* we're last thing that used all this */
-      gst_v4l2src_buffer_pool_free (buf->pool, TRUE);
-    }
-  }
-}
-
-/* Create a V4l2Src buffer from our mmap'd data area */
-GstBuffer *
-gst_v4l2src_buffer_new (GstV4l2Src * v4l2src, guint size, guint8 * data,
-    GstV4l2Buffer * srcbuf)
-{
-  GstBuffer *buf;
-  GstClockTime timestamp;
-  GstClock *clock;
-
-  if (data == NULL) {
-    buf = gst_buffer_new_and_alloc (size);
-  } else {
-    buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_V4L2SRC_BUFFER);
-    GST_BUFFER_DATA (buf) = data;
-    GST_V4L2SRC_BUFFER (buf)->buf = srcbuf;
-    GST_LOG_OBJECT (v4l2src,
-        "creating buffer  %p (nr. %d)", srcbuf, srcbuf->buffer.index);
-  }
-  GST_BUFFER_SIZE (buf) = size;
-
-  GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
-
-  /* timestamps, LOCK to get clock and base time. */
-  GST_OBJECT_LOCK (v4l2src);
-  if ((clock = GST_ELEMENT_CLOCK (v4l2src))) {
-    /* we have a clock, get base time and ref clock */
-    timestamp = GST_ELEMENT (v4l2src)->base_time;
-    gst_object_ref (clock);
-  } else {
-    /* no clock, can't set timestamps */
-    timestamp = GST_CLOCK_TIME_NONE;
-  }
-  GST_OBJECT_UNLOCK (v4l2src);
-
-  if (clock) {
-    /* the time now is the time of the clock minus the base time */
-    timestamp = gst_clock_get_time (clock) - timestamp;
-    gst_object_unref (clock);
-  }
-
-  /* FIXME: use the timestamp from the buffer itself! */
-  GST_BUFFER_TIMESTAMP (buf) = timestamp;
-
-  /* offsets */
-  GST_BUFFER_OFFSET (buf) = v4l2src->offset++;
-  GST_BUFFER_OFFSET_END (buf) = v4l2src->offset;
-
-  /* the negotiate() method already set caps on the source pad */
-  gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (v4l2src)));
-
-  return buf;
-}
index ea29481..450ea1a 100644 (file)
@@ -33,11 +33,10 @@ gboolean   gst_v4l2src_set_capture       (GstV4l2Src * v4l2src,
                                           guint32 width, guint32 height,
                                          guint32 fps_n, guint32 fps_d);
 
-gboolean   gst_v4l2src_capture_init      (GstV4l2Src * v4l2src);
+gboolean   gst_v4l2src_capture_init      (GstV4l2Src * v4l2src, GstCaps *caps);
 gboolean   gst_v4l2src_capture_start     (GstV4l2Src * v4l2src);
 
-gint       gst_v4l2src_grab_frame        (GstV4l2Src * v4l2src);
-gboolean   gst_v4l2src_queue_frame       (GstV4l2Src * v4l2src, guint i);
+GstFlowReturn gst_v4l2src_grab_frame     (GstV4l2Src * v4l2src, GstBuffer **buf);
 
 gboolean   gst_v4l2src_capture_stop      (GstV4l2Src * v4l2src);
 gboolean   gst_v4l2src_capture_deinit    (GstV4l2Src * v4l2src);
@@ -52,11 +51,5 @@ gboolean   gst_v4l2src_get_size_limits   (GstV4l2Src * v4l2src,
                                           struct v4l2_fmtdesc *fmt,
                                           gint * min_w, gint * max_w,
                                           gint * min_h, gint * max_h);
-/* buffers */
-GstBuffer* gst_v4l2src_buffer_new        (GstV4l2Src * v4l2src,
-                                          guint size, guint8 * data,
-                                          GstV4l2Buffer * srcbuf);
-void       gst_v4l2src_free_buffer       (GstBuffer * buffer);
-
 
 #endif /* __V4L2SRC_CALLS_H__ */