v4l2: More work on bufferpools
authorWim Taymans <wim.taymans@collabora.co.uk>
Mon, 18 Jul 2011 16:54:49 +0000 (18:54 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Mon, 18 Jul 2011 16:54:49 +0000 (18:54 +0200)
Add different transport methods to the bufferpool (MMAP and READ/WRITE)
Do more parsing of the bufferpool config.
Start and stop streaming based on the bufferpool state.
Make separate methods for getting a buffer from the pool and filling it with
data. This allows us to fill buffers from other pools too. Either use copy or
read to fill up the target buffers.
Add property to force a transfer mode in v4l2src.
Increase default number of buffers to 4.
Negotiate bufferpool and its properties in v4l2src.

sys/v4l2/gstv4l2bufferpool.c
sys/v4l2/gstv4l2bufferpool.h
sys/v4l2/gstv4l2object.c
sys/v4l2/gstv4l2object.h
sys/v4l2/gstv4l2sink.c
sys/v4l2/gstv4l2src.c
sys/v4l2/gstv4l2src.h

index dc23c17..ace149d 100644 (file)
@@ -83,24 +83,35 @@ static void
 gst_v4l2_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
 {
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
-  gint index;
-  GstMetaV4l2 *meta;
   GstV4l2Object *obj;
 
-  meta = GST_META_V4L2_GET (buffer);
-  g_assert (meta != NULL);
-
   obj = pool->obj;
 
-  index = meta->vbuffer.index;
-  GST_LOG_OBJECT (pool, "finalizing buffer %p %d", buffer, index);
-  pool->buffers[index] = NULL;
-
-  GST_LOG_OBJECT (pool,
-      "buffer %p (data %p, len %u) freed, unmapping",
-      buffer, meta->mem, meta->vbuffer.length);
-  v4l2_munmap (meta->mem, meta->vbuffer.length);
-
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+      break;
+    case GST_V4L2_IO_MMAP:
+    {
+      GstMetaV4l2 *meta;
+      gint index;
+
+      meta = GST_META_V4L2_GET (buffer);
+      g_assert (meta != NULL);
+
+      index = meta->vbuffer.index;
+      GST_LOG_OBJECT (pool,
+          "mmap buffer %p idx %d (data %p, len %u) freed, unmapping", buffer,
+          index, meta->mem, meta->vbuffer.length);
+
+      v4l2_munmap (meta->mem, meta->vbuffer.length);
+      pool->buffers[index] = NULL;
+      break;
+    }
+    case GST_V4L2_IO_USERPTR:
+    default:
+      g_assert_not_reached ();
+      break;
+  }
   gst_buffer_unref (buffer);
 }
 
@@ -118,54 +129,70 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
   obj = pool->obj;
   info = &obj->info;
 
-  newbuf = gst_buffer_new ();
-  meta = GST_META_V4L2_ADD (newbuf);
-
-  index = pool->index;
-
-  GST_LOG_OBJECT (pool, "creating buffer %u, %p", index, newbuf, pool);
-
-  meta->vbuffer.index = index;
-  meta->vbuffer.type = obj->type;
-  meta->vbuffer.memory = V4L2_MEMORY_MMAP;
-
-  if (v4l2_ioctl (pool->video_fd, VIDIOC_QUERYBUF, &meta->vbuffer) < 0)
-    goto querybuf_failed;
-
-  GST_LOG_OBJECT (pool, "  index:     %u", meta->vbuffer.index);
-  GST_LOG_OBJECT (pool, "  type:      %d", meta->vbuffer.type);
-  GST_LOG_OBJECT (pool, "  bytesused: %u", meta->vbuffer.bytesused);
-  GST_LOG_OBJECT (pool, "  flags:     %08x", meta->vbuffer.flags);
-  GST_LOG_OBJECT (pool, "  field:     %d", meta->vbuffer.field);
-  GST_LOG_OBJECT (pool, "  memory:    %d", meta->vbuffer.memory);
-  if (meta->vbuffer.memory == V4L2_MEMORY_MMAP)
-    GST_LOG_OBJECT (pool, "  MMAP offset:  %u", meta->vbuffer.m.offset);
-  GST_LOG_OBJECT (pool, "  length:    %u", meta->vbuffer.length);
-  GST_LOG_OBJECT (pool, "  input:     %u", meta->vbuffer.input);
-
-  meta->mem = v4l2_mmap (0, meta->vbuffer.length,
-      PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
-      meta->vbuffer.m.offset);
-  if (meta->mem == MAP_FAILED)
-    goto mmap_failed;
-
-  gst_buffer_take_memory (newbuf, -1,
-      gst_memory_new_wrapped (0,
-          meta->mem, NULL, meta->vbuffer.length, 0, meta->vbuffer.length));
-
-  /* add metadata to raw video buffers */
-  if (info->finfo) {
-    gsize offset[GST_VIDEO_MAX_PLANES];
-    gint stride[GST_VIDEO_MAX_PLANES];
-
-    offset[0] = 0;
-    stride[0] = obj->bytesperline;
-
-    GST_DEBUG_OBJECT (pool, "adding video meta");
-    gst_buffer_add_meta_video_full (newbuf, info->flags,
-        GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
-        GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
-        offset, stride);
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+    {
+      newbuf =
+          gst_buffer_new_allocate (pool->allocator, pool->size, pool->align);
+      break;
+    }
+    case GST_V4L2_IO_MMAP:
+    {
+      newbuf = gst_buffer_new ();
+      meta = GST_META_V4L2_ADD (newbuf);
+
+      index = pool->index;
+
+      GST_LOG_OBJECT (pool, "creating buffer %u, %p", index, newbuf, pool);
+
+      meta->vbuffer.index = index;
+      meta->vbuffer.type = obj->type;
+      meta->vbuffer.memory = V4L2_MEMORY_MMAP;
+
+      if (v4l2_ioctl (pool->video_fd, VIDIOC_QUERYBUF, &meta->vbuffer) < 0)
+        goto querybuf_failed;
+
+      GST_LOG_OBJECT (pool, "  index:     %u", meta->vbuffer.index);
+      GST_LOG_OBJECT (pool, "  type:      %d", meta->vbuffer.type);
+      GST_LOG_OBJECT (pool, "  bytesused: %u", meta->vbuffer.bytesused);
+      GST_LOG_OBJECT (pool, "  flags:     %08x", meta->vbuffer.flags);
+      GST_LOG_OBJECT (pool, "  field:     %d", meta->vbuffer.field);
+      GST_LOG_OBJECT (pool, "  memory:    %d", meta->vbuffer.memory);
+      if (meta->vbuffer.memory == V4L2_MEMORY_MMAP)
+        GST_LOG_OBJECT (pool, "  MMAP offset:  %u", meta->vbuffer.m.offset);
+      GST_LOG_OBJECT (pool, "  length:    %u", meta->vbuffer.length);
+      GST_LOG_OBJECT (pool, "  input:     %u", meta->vbuffer.input);
+
+      meta->mem = v4l2_mmap (0, meta->vbuffer.length,
+          PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
+          meta->vbuffer.m.offset);
+      if (meta->mem == MAP_FAILED)
+        goto mmap_failed;
+
+      gst_buffer_take_memory (newbuf, -1,
+          gst_memory_new_wrapped (0,
+              meta->mem, NULL, meta->vbuffer.length, 0, meta->vbuffer.length));
+
+      /* add metadata to raw video buffers */
+      if (info->finfo) {
+        gsize offset[GST_VIDEO_MAX_PLANES];
+        gint stride[GST_VIDEO_MAX_PLANES];
+
+        offset[0] = 0;
+        stride[0] = obj->bytesperline;
+
+        GST_DEBUG_OBJECT (pool, "adding video meta, stride %d", stride[0]);
+        gst_buffer_add_meta_video_full (newbuf, info->flags,
+            GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
+            GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
+            offset, stride);
+      }
+      break;
+    }
+    case GST_V4L2_IO_USERPTR:
+    default:
+      g_assert_not_reached ();
+      break;
   }
 
   pool->index++;
@@ -212,8 +239,11 @@ gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
 
   GST_DEBUG_OBJECT (pool, "config %" GST_PTR_FORMAT, config);
 
+  pool->size = size;
   pool->min_buffers = min_buffers;
   pool->max_buffers = max_buffers;
+  pool->prefix = prefix;
+  pool->align = align;
 
   return TRUE;
 
@@ -225,46 +255,93 @@ wrong_config:
 }
 
 static gboolean
-gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
+start_streaming (GstBufferPool * bpool)
 {
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
   GstV4l2Object *obj = pool->obj;
-  gint n;
-  struct v4l2_requestbuffers breq;
-  gint num_buffers;
 
-  num_buffers = pool->max_buffers;
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+      break;
+    case GST_V4L2_IO_MMAP:
+    case GST_V4L2_IO_USERPTR:
+      GST_DEBUG_OBJECT (pool, "STREAMON");
+      if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0)
+        goto start_failed;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
 
-  /* first, lets request buffers, and see how many we can get: */
-  GST_DEBUG_OBJECT (pool, "starting, requesting %d MMAP buffers", num_buffers);
+  pool->streaming = TRUE;
 
-  memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
-  breq.type = obj->type;
-  breq.count = num_buffers;
-  breq.memory = V4L2_MEMORY_MMAP;
+  return TRUE;
 
-  if (v4l2_ioctl (pool->video_fd, VIDIOC_REQBUFS, &breq) < 0)
-    goto reqbufs_failed;
+  /* ERRORS */
+start_failed:
+  {
+    GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno,
+        g_strerror (errno));
+    return FALSE;
+  }
+}
 
-  GST_LOG_OBJECT (pool, " count:  %u", breq.count);
-  GST_LOG_OBJECT (pool, " type:   %d", breq.type);
-  GST_LOG_OBJECT (pool, " memory: %d", breq.memory);
+static gboolean
+gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
+{
+  GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstV4l2Object *obj = pool->obj;
+  gint n;
+  struct v4l2_requestbuffers breq;
+  gint num_buffers;
 
-  if (breq.count < GST_V4L2_MIN_BUFFERS)
-    goto no_buffers;
+  num_buffers = pool->min_buffers;
 
-  if (num_buffers != breq.count) {
-    GST_WARNING_OBJECT (pool, "using %u buffers instead", breq.count);
-    num_buffers = breq.count;
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+    {
+      break;
+    }
+    case GST_V4L2_IO_MMAP:
+    {
+      /* first, lets request buffers, and see how many we can get: */
+      GST_DEBUG_OBJECT (pool, "starting, requesting %d MMAP buffers",
+          num_buffers);
+
+      memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
+      breq.type = obj->type;
+      breq.count = num_buffers;
+      breq.memory = V4L2_MEMORY_MMAP;
+
+      if (v4l2_ioctl (pool->video_fd, VIDIOC_REQBUFS, &breq) < 0)
+        goto reqbufs_failed;
+
+      GST_LOG_OBJECT (pool, " count:  %u", breq.count);
+      GST_LOG_OBJECT (pool, " type:   %d", breq.type);
+      GST_LOG_OBJECT (pool, " memory: %d", breq.memory);
+
+      if (breq.count < GST_V4L2_MIN_BUFFERS)
+        goto no_buffers;
+
+      if (num_buffers != breq.count) {
+        GST_WARNING_OBJECT (pool, "using %u buffers instead", breq.count);
+        num_buffers = breq.count;
+      }
+      break;
+    }
+    case GST_V4L2_IO_USERPTR:
+    default:
+      g_assert_not_reached ();
+      break;
   }
 
   pool->obj = obj;
-  pool->requeuebuf = (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? TRUE : FALSE);
   pool->num_buffers = num_buffers;
   pool->buffers = g_new0 (GstBuffer *, num_buffers);
   pool->index = 0;
 
-  /* now, map the buffers: */
+  /* now, allocate the buffers: */
   for (n = 0; n < num_buffers; n++) {
     GstBuffer *buffer;
 
@@ -273,6 +350,13 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
 
     gst_v4l2_buffer_pool_release_buffer (bpool, buffer);
   }
+
+  /* we can start capturing now, we wait for the playback case until we queued
+   * the first buffer */
+  if (obj->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+    if (!start_streaming (bpool))
+      goto start_failed;
+
   return TRUE;
 
   /* ERRORS */
@@ -294,6 +378,11 @@ buffer_new_failed:
     GST_ERROR_OBJECT (pool, "failed to create a buffer");
     return FALSE;
   }
+start_failed:
+  {
+    GST_ERROR_OBJECT (pool, "failed to start streaming");
+    return FALSE;
+  }
 }
 
 static gboolean
@@ -301,10 +390,29 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool)
 {
   gboolean ret;
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstV4l2Object *obj = pool->obj;
   guint n;
 
   GST_DEBUG_OBJECT (pool, "stopping pool");
 
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+      break;
+    case GST_V4L2_IO_MMAP:
+    case GST_V4L2_IO_USERPTR:
+      /* we actually need to sync on all queued buffers but not
+       * on the non-queued ones */
+      GST_DEBUG_OBJECT (pool, "STREAMOFF");
+      if (v4l2_ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0)
+        goto stop_failed;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  pool->streaming = FALSE;
+
   /* first free the buffers in the queue */
   ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool);
 
@@ -314,16 +422,104 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool)
       gst_v4l2_buffer_pool_free_buffer (bpool, pool->buffers[n]);
   }
   return ret;
+
+  /* ERRORS */
+stop_failed:
+  {
+    GST_ERROR_OBJECT (pool, "error with STREAMOFF %d (%s)", errno,
+        g_strerror (errno));
+    return FALSE;
+  }
+}
+
+static GstFlowReturn
+gst_v4l2_object_poll (GstV4l2Object * v4l2object)
+{
+  gint ret;
+
+  if (v4l2object->can_poll_device) {
+    ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
+    if (G_UNLIKELY (ret < 0)) {
+      if (errno == EBUSY)
+        goto stopped;
+      if (errno == ENXIO) {
+        GST_DEBUG_OBJECT (v4l2object->element,
+            "v4l2 device doesn't support polling. Disabling");
+        v4l2object->can_poll_device = FALSE;
+      } else {
+        if (errno != EAGAIN && errno != EINTR)
+          goto select_error;
+      }
+    }
+  }
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+stopped:
+  {
+    GST_DEBUG ("stop called");
+    return GST_FLOW_WRONG_STATE;
+  }
+select_error:
+  {
+    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
+        ("poll error %d: %s (%d)", ret, g_strerror (errno), errno));
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
+gst_v4l2_buffer_pool_qbuf (GstBufferPool * bpool, GstBuffer * buf)
+{
+  GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstMetaV4l2 *meta;
+  gint index;
+
+  meta = GST_META_V4L2_GET (buf);
+  g_assert (meta != NULL);
+
+  index = meta->vbuffer.index;
+
+  GST_LOG_OBJECT (pool, "enqueue pool buffer %d, queued: %d", index,
+      pool->num_queued);
+
+  if (pool->buffers[index] != NULL)
+    goto already_queued;
+
+  if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &meta->vbuffer) < 0)
+    goto queue_failed;
+
+  pool->buffers[index] = buf;
+
+  pool->num_queued++;
+
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+already_queued:
+  {
+    GST_WARNING_OBJECT (pool, "the buffer was already queued");
+    return GST_FLOW_ERROR;
+  }
+queue_failed:
+  {
+    GST_WARNING_OBJECT (pool, "could not queue a buffer");
+    return GST_FLOW_ERROR;
+  }
 }
 
 static GstFlowReturn
 gst_v4l2_buffer_pool_dqbuf (GstBufferPool * bpool, GstBuffer ** buffer)
 {
+  GstFlowReturn res;
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
   GstBuffer *outbuf;
   struct v4l2_buffer vbuffer;
   GstV4l2Object *obj = pool->obj;
 
+  if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK)
+    goto poll_error;
+
   memset (&vbuffer, 0x00, sizeof (vbuffer));
   vbuffer.type = obj->type;
   vbuffer.memory = V4L2_MEMORY_MMAP;
@@ -340,15 +536,13 @@ gst_v4l2_buffer_pool_dqbuf (GstBufferPool * bpool, GstBuffer ** buffer)
 
   /* mark the buffer outstanding */
   pool->buffers[vbuffer.index] = NULL;
+  pool->num_queued--;
 
   GST_LOG_OBJECT (pool,
       "dequeued frame %d (ix=%d), used %d, flags %08x, pool-queued=%d, buffer=%p",
       vbuffer.sequence, vbuffer.index, vbuffer.bytesused, vbuffer.flags,
       pool->num_queued, outbuf);
 
-  pool->num_queued--;
-  GST_DEBUG_OBJECT (pool, "num_queued: %d", pool->num_queued);
-
   /* set top/bottom field first if v4l2_buffer has the information */
   if (vbuffer.field == V4L2_FIELD_INTERLACED_TB)
     GST_BUFFER_FLAG_SET (outbuf, GST_VIDEO_BUFFER_TFF);
@@ -366,6 +560,11 @@ gst_v4l2_buffer_pool_dqbuf (GstBufferPool * bpool, GstBuffer ** buffer)
   return GST_FLOW_OK;
 
   /* ERRORS */
+poll_error:
+  {
+    GST_DEBUG ("poll error %s", gst_flow_get_name (res));
+    return res;
+  }
 error:
   {
     GST_WARNING_OBJECT (pool,
@@ -426,25 +625,68 @@ static GstFlowReturn
 gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
     GstBufferPoolParams * params)
 {
-  GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
   GstFlowReturn ret;
+  GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstV4l2Object *obj = pool->obj;
 
   GST_DEBUG_OBJECT (pool, "acquire");
 
   if (GST_BUFFER_POOL_IS_FLUSHING (bpool))
     goto flushing;
 
-  if (pool->requeuebuf)
-    ret = gst_v4l2_buffer_pool_dqbuf (bpool, buffer);
-  else {
-    if (pool->num_queued == pool->num_buffers) {
-      ret = gst_v4l2_buffer_pool_dqbuf (bpool, buffer);
-    }
-    ret =
-        GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer,
-        params);
+  switch (obj->type) {
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+      /* capture, This function should return a buffer with new captured data */
+      switch (obj->mode) {
+        case GST_V4L2_IO_RW:
+          /* take empty buffer from the pool */
+          ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
+              buffer, params);
+          break;
+
+        case GST_V4L2_IO_MMAP:
+          /* just dequeue a buffer, we basically use the queue of v4l2 as the
+           * storage for our buffers. */
+          ret = gst_v4l2_buffer_pool_dqbuf (bpool, buffer);
+          break;
+
+        case GST_V4L2_IO_USERPTR:
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+      break;
+
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+      /* playback, This function should return an empty buffer */
+      switch (obj->mode) {
+        case GST_V4L2_IO_RW:
+          /* get an empty buffer */
+          ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
+              buffer, params);
+          break;
+
+        case GST_V4L2_IO_MMAP:
+          /* first try to get a free unqueued buffer */
+          ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
+              buffer, params);
+          if (ret == GST_FLOW_UNEXPECTED) {
+            /* all buffers are queued, try to dequeue one */
+            ret = gst_v4l2_buffer_pool_dqbuf (bpool, buffer);
+          }
+          break;
+
+        case GST_V4L2_IO_USERPTR:
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
   }
-
   return ret;
 
   /* ERRORS */
@@ -459,13 +701,41 @@ static void
 gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
 {
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstV4l2Object *obj = pool->obj;
 
   GST_DEBUG_OBJECT (pool, "release");
 
-  if (pool->requeuebuf)
-    gst_v4l2_buffer_pool_qbuf (bpool, buffer);
-  else
-    GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer);
+  switch (obj->type) {
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+      /* capture, put the buffer back in the queue so that we can refill it
+       * later. */
+      switch (obj->mode) {
+        case GST_V4L2_IO_RW:
+          /* release back in the pool */
+          GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer);
+          break;
+
+        case GST_V4L2_IO_MMAP:
+          /* queue back in the device */
+          gst_v4l2_buffer_pool_qbuf (bpool, buffer);
+          break;
+
+        case GST_V4L2_IO_USERPTR:
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+      break;
+
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+      /* playback, put the buffer back in the queue to refill later. */
+      GST_BUFFER_POOL_CLASS (parent_class)->release_buffer (bpool, buffer);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+  }
 }
 
 static void
@@ -543,52 +813,184 @@ dup_failed:
   }
 }
 
-/**
- * gst_v4l2_buffer_pool_qbuf:
- * @pool: the pool
- * @buf: the buffer to queue
- *
- * Queue a buffer to the driver
- *
- * Returns: %TRUE for success
- */
-gboolean
-gst_v4l2_buffer_pool_qbuf (GstBufferPool * bpool, GstBuffer * buf)
+static GstFlowReturn
+gst_v4l2_do_read (GstBufferPool * bpool, GstBuffer * buf)
 {
+  GstFlowReturn res;
   GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
-  GstMetaV4l2 *meta;
-  gint index;
+  GstV4l2Object *obj = pool->obj;
+  gint amount;
+  gpointer data;
+  gint buffersize;
 
-  meta = GST_META_V4L2_GET (buf);
-  g_assert (meta != NULL);
+  buffersize = gst_buffer_get_size (buf);
 
-  index = meta->vbuffer.index;
+  GST_LOG_OBJECT (pool, "reading %d bytes into buffer %p", buffersize, buf);
 
-  GST_LOG_OBJECT (pool, "enqueue pool buffer %d", index);
+  data = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
 
-  if (pool->buffers[index] != NULL)
-    goto already_queued;
+  do {
+    if ((res = gst_v4l2_object_poll (obj)) != GST_FLOW_OK)
+      goto poll_error;
 
-  if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &meta->vbuffer) < 0)
-    goto queue_failed;
+    amount = v4l2_read (obj->video_fd, data, buffersize);
 
-  pool->buffers[index] = buf;
+    if (amount == buffersize) {
+      break;
+    } else if (amount == -1) {
+      if (errno == EAGAIN || errno == EINTR) {
+        continue;
+      } else
+        goto read_error;
+    } else {
+      /* short reads can happen if a signal interrupts the read */
+      continue;
+    }
+  } while (TRUE);
 
-  pool->num_queued++;
-  GST_DEBUG_OBJECT (pool, "num_queued: %d", pool->num_queued);
+  GST_LOG_OBJECT (pool, "read %d bytes", amount);
+  gst_buffer_unmap (buf, data, amount);
 
-  return TRUE;
+  return GST_FLOW_OK;
 
   /* ERRORS */
-already_queued:
+poll_error:
   {
-    GST_WARNING_OBJECT (pool, "the buffer was already queued");
-    return FALSE;
+    GST_DEBUG ("poll error %s", gst_flow_get_name (res));
+    goto cleanup;
   }
-queue_failed:
+read_error:
   {
-    GST_WARNING_OBJECT (pool, "could not queue a buffer");
-    return FALSE;
+    GST_ELEMENT_ERROR (obj->element, RESOURCE, READ,
+        (_("Error reading %d bytes from device '%s'."),
+            buffersize, obj->videodev), GST_ERROR_SYSTEM);
+    res = GST_FLOW_ERROR;
+    goto cleanup;
+  }
+cleanup:
+  {
+    gst_buffer_unmap (buf, data, 0);
+    return res;
+  }
+}
+
+/**
+ * gst_v4l2_buffer_pool_process:
+ * @bpool: a #GstBufferPool
+ * @buf: a #GstBuffer
+ *
+ * Process @buf in @bpool. For capture devices, this functions fills @buf with
+ * data from the device. For output devices, this functions send the contents of
+ * @buf to the device for playback.
+ *
+ * Returns: %GST_FLOW_OK on success.
+ */
+GstFlowReturn
+gst_v4l2_buffer_pool_process (GstBufferPool * bpool, GstBuffer * buf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
+  GstV4l2Object *obj = pool->obj;
+
+  switch (obj->type) {
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+      /* capture */
+      switch (obj->mode) {
+        case GST_V4L2_IO_RW:
+          /* capture into the buffer */
+          ret = gst_v4l2_do_read (bpool, buf);
+          break;
+
+        case GST_V4L2_IO_MMAP:
+        {
+          GstBuffer *tmp;
+
+          if (buf->pool == bpool)
+            /* nothing, data was inside the buffer when we did _acquire() */
+            goto done;
+
+          /* buffer not from our pool, grab a frame and copy it into the target */
+          if ((ret = gst_v4l2_buffer_pool_dqbuf (bpool, &tmp)) != GST_FLOW_OK)
+            goto done;
+
+          if (!gst_v4l2_object_copy (obj, buf, tmp))
+            goto copy_failed;
+
+          if ((ret = gst_v4l2_buffer_pool_qbuf (bpool, tmp)) != GST_FLOW_OK)
+            goto done;
+          break;
+        }
+
+        case GST_V4L2_IO_USERPTR:
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+      break;
+
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+      /* playback */
+      switch (obj->mode) {
+        case GST_V4L2_IO_RW:
+          /* FIXME, do write() */
+          break;
+
+        case GST_V4L2_IO_MMAP:
+        {
+          GstBuffer *tmp;
+
+          if (buf->pool == bpool) {
+            /* nothing, we can queue directly */
+            ret = gst_v4l2_buffer_pool_qbuf (bpool, buf);
+          } else {
+            ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
+                &tmp, NULL);
+            if (ret == GST_FLOW_UNEXPECTED) {
+              /* all buffers are queued, try to dequeue one */
+              ret = gst_v4l2_buffer_pool_dqbuf (bpool, &tmp);
+            }
+            if (ret != GST_FLOW_OK)
+              goto done;
+
+            /* copy into it and queue */
+            if (!gst_v4l2_object_copy (obj, tmp, buf))
+              goto copy_failed;
+
+            if ((ret = gst_v4l2_buffer_pool_qbuf (bpool, tmp)) != GST_FLOW_OK)
+              goto done;
+
+            /* if we are not streaming yet (this is the first buffer, start
+             * streaming now */
+            if (!pool->streaming)
+              if (!start_streaming (bpool))
+                goto start_failed;
+          }
+          break;
+        }
+
+        case GST_V4L2_IO_USERPTR:
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+done:
+  return ret;
+
+  /* ERRORS */
+copy_failed:
+  {
+    GST_ERROR_OBJECT (obj->element, "failed to copy data");
+    return GST_FLOW_ERROR;
+  }
+start_failed:
+  {
+    GST_ERROR_OBJECT (obj->element, "failed to start streaming");
+    return GST_FLOW_ERROR;
   }
 }
 
index 1d0f112..f72dbb3 100644 (file)
@@ -49,15 +49,20 @@ struct _GstV4l2BufferPool
 
   GstV4l2Object *obj;        /* the v4l2 object */
   gint video_fd;             /* a dup(2) of the v4l2object's video_fd */
-  gboolean requeuebuf;       /* if true, unusued buffers are automatically re-QBUF'd */
 
+  GstAllocator *allocator;
+  guint size;
   guint min_buffers;
   guint max_buffers;
+  guint prefix;
+  guint align;
 
   guint num_buffers;         /* number of buffers allocated by the driver */
   guint num_queued;           /* number of buffers queued in the driver */
   gint index;
 
+  gboolean streaming;
+
   GstBuffer **buffers;
 };
 
@@ -81,7 +86,7 @@ GType gst_v4l2_buffer_pool_get_type (void);
 
 GstBufferPool *     gst_v4l2_buffer_pool_new     (GstV4l2Object *obj);
 
-gboolean            gst_v4l2_buffer_pool_qbuf    (GstBufferPool * bpool, GstBuffer * buf);
+GstFlowReturn       gst_v4l2_buffer_pool_process (GstBufferPool * bpool, GstBuffer * buf);
 
 gint                gst_v4l2_buffer_pool_available_buffers (GstBufferPool *pool);
 
index 65cbcd3..3f8324b 100644 (file)
@@ -61,6 +61,7 @@ GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
 #define DEFAULT_PROP_TV_NORM            0
 #define DEFAULT_PROP_CHANNEL            NULL
 #define DEFAULT_PROP_FREQUENCY          0
+#define DEFAULT_PROP_IO_MODE            GST_V4L2_IO_AUTO
 
 enum
 {
@@ -370,6 +371,26 @@ gst_v4l2_tv_norm_get_type (void)
   return v4l2_tv_norm;
 }
 
+#define GST_TYPE_V4L2_IO_MODE (gst_v4l2_io_mode_get_type ())
+static GType
+gst_v4l2_io_mode_get_type (void)
+{
+  static GType v4l2_io_mode = 0;
+
+  if (!v4l2_io_mode) {
+    static const GEnumValue io_modes[] = {
+      {GST_V4L2_IO_AUTO, "GST_V4L2_IO_AUTO", "auto"},
+      {GST_V4L2_IO_RW, "GST_V4L2_IO_RW", "rw"},
+      {GST_V4L2_IO_MMAP, "GST_V4L2_IO_MMAP", "mmap"},
+      {GST_V4L2_IO_USERPTR, "GST_V4L2_IO_USERPTR", "userptr"},
+
+      {0, NULL, NULL}
+    };
+    v4l2_io_mode = g_enum_register_static ("GstV4l2IOMode", io_modes);
+  }
+  return v4l2_io_mode;
+}
+
 void
 gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
     const char *default_device)
@@ -451,6 +472,17 @@ gst_v4l2_object_install_properties_helper (GObjectClass * gobject_class,
           "video standard",
           GST_TYPE_V4L2_TV_NORM, DEFAULT_PROP_TV_NORM,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstV4l2Src:io-mode
+   *
+   * IO Mode
+   */
+  g_object_class_install_property (gobject_class, PROP_IO_MODE,
+      g_param_spec_enum ("io-mode", "IO mode",
+          "I/O mode",
+          GST_TYPE_V4L2_IO_MODE, DEFAULT_PROP_IO_MODE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 GstV4l2Object *
@@ -612,6 +644,9 @@ gst_v4l2_object_set_property_helper (GstV4l2Object * v4l2object,
       }
       break;
 #endif
+    case PROP_IO_MODE:
+      v4l2object->req_mode = g_value_get_enum (value);
+      break;
     default:
       return FALSE;
       break;
@@ -685,6 +720,9 @@ gst_v4l2_object_get_property_helper (GstV4l2Object * v4l2object,
     case PROP_TV_NORM:
       g_value_set_enum (value, v4l2object->tv_norm);
       break;
+    case PROP_IO_MODE:
+      g_value_set_enum (value, v4l2object->req_mode);
+      break;
     default:
       return FALSE;
       break;
@@ -2044,39 +2082,46 @@ gst_v4l2_object_get_nearest_size (GstV4l2Object * v4l2object,
 static gboolean
 gst_v4l2_object_setup_pool (GstV4l2Object * v4l2object, GstCaps * caps)
 {
+  guint num_buffers;
+  GstStructure *config;
+
   GST_DEBUG_OBJECT (v4l2object->element, "initializing the capture system");
 
   GST_V4L2_CHECK_OPEN (v4l2object);
   GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
 
-  if (v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) {
-    guint num_buffers;
-    GstStructure *config;
+  /* use specified mode */
+  v4l2object->mode = v4l2object->req_mode;
 
-    /* keep track of current number of buffers */
-    num_buffers = v4l2object->num_buffers;
+  if (v4l2object->req_mode == GST_V4L2_IO_AUTO) {
+    /* automatic mode, find transport */
+    if (v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) {
+      GST_INFO_OBJECT (v4l2object->element,
+          "accessing buffers via read()/write()");
+      v4l2object->mode = GST_V4L2_IO_RW;
+    }
+    if (v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) {
+      GST_INFO_OBJECT (v4l2object->element, "accessing buffers via mmap()");
+      v4l2object->mode = GST_V4L2_IO_MMAP;
+    }
+  }
+  /* if still no transport selected, error out */
+  if (v4l2object->mode == GST_V4L2_IO_AUTO)
+    goto no_supported_capture_method;
 
-    /* Map the buffers */
-    GST_LOG_OBJECT (v4l2object->element, "initiating buffer pool");
+  /* keep track of current number of buffers */
+  num_buffers = v4l2object->num_buffers;
 
-    if (!(v4l2object->pool = gst_v4l2_buffer_pool_new (v4l2object)))
-      goto buffer_pool_new_failed;
+  /* Map the buffers */
+  GST_LOG_OBJECT (v4l2object->element, "initiating buffer pool");
 
-    GST_INFO_OBJECT (v4l2object->element, "accessing buffers via mmap()");
-    v4l2object->mode = GST_V4L2_IO_MMAP;
+  if (!(v4l2object->pool = gst_v4l2_buffer_pool_new (v4l2object)))
+    goto buffer_pool_new_failed;
 
-    config = gst_buffer_pool_get_config (v4l2object->pool);
-    gst_buffer_pool_config_set (config, caps, v4l2object->info.size,
-        num_buffers, num_buffers, 0, 0);
-    gst_buffer_pool_set_config (v4l2object->pool, config);
-  } else if (v4l2object->vcap.capabilities & V4L2_CAP_READWRITE) {
-    GST_INFO_OBJECT (v4l2object->element,
-        "accessing buffers via read()/write()");
-    v4l2object->mode = GST_V4L2_IO_RW;
-    v4l2object->pool = NULL;
-  } else {
-    goto no_supported_capture_method;
-  }
+  config = gst_buffer_pool_get_config (v4l2object->pool);
+  gst_buffer_pool_config_set (config, caps, v4l2object->info.size,
+      num_buffers, num_buffers, 0, 0);
+  gst_buffer_pool_set_config (v4l2object->pool, config);
 
   GST_V4L2_SET_ACTIVE (v4l2object);
 
@@ -2140,7 +2185,8 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, GstCaps * caps)
   }
 
   GST_DEBUG_OBJECT (v4l2object->element, "Desired format %dx%d, format "
-      "%" GST_FOURCC_FORMAT, width, height, GST_FOURCC_ARGS (pixelformat));
+      "%" GST_FOURCC_FORMAT " stride: %d", width, height,
+      GST_FOURCC_ARGS (pixelformat), stride);
 
   GST_V4L2_CHECK_OPEN (v4l2object);
   GST_V4L2_CHECK_NOT_ACTIVE (v4l2object);
@@ -2192,9 +2238,10 @@ gst_v4l2_object_set_format (GstV4l2Object * v4l2object, GstCaps * caps)
 
     if (format.fmt.pix.pixelformat != pixelformat)
       goto invalid_pixelformat;
-
-    v4l2object->bytesperline = format.fmt.pix.bytesperline;
   }
+  v4l2object->bytesperline = format.fmt.pix.bytesperline;
+  /* FIXME, size for only one plane */
+  v4l2object->size = v4l2object->bytesperline * height;
 
   /* Is there a reason we require the caller to always specify a framerate? */
   GST_DEBUG_OBJECT (v4l2object->element, "Desired framerate: %u/%u", fps_n,
@@ -2334,50 +2381,21 @@ pool_failed:
 }
 
 gboolean
-gst_v4l2_object_start (GstV4l2Object * v4l2object)
+gst_v4l2_object_unlock (GstV4l2Object * v4l2object)
 {
-  GST_DEBUG_OBJECT (v4l2object->element, "starting");
+  GST_LOG_OBJECT (v4l2object->element, "flush poll");
+  gst_poll_set_flushing (v4l2object->poll, TRUE);
 
-  GST_V4L2_CHECK_OPEN (v4l2object);
-  GST_V4L2_CHECK_ACTIVE (v4l2object);
+  return TRUE;
+}
 
-  if (v4l2object->pool)
-    if (!gst_buffer_pool_set_active (v4l2object->pool, TRUE))
-      goto activate_failed;
+gboolean
+gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
+{
+  GST_LOG_OBJECT (v4l2object->element, "flush stop poll");
+  gst_poll_set_flushing (v4l2object->poll, FALSE);
 
-  switch (v4l2object->mode) {
-    case GST_V4L2_IO_RW:
-      break;
-    case GST_V4L2_IO_MMAP:
-    case GST_V4L2_IO_USERPTR:
-      if (!v4l2object->streaming) {
-        GST_DEBUG_OBJECT (v4l2object->element, "STREAMON");
-        if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMON,
-                &(v4l2object->type)) < 0)
-          goto start_failed;
-        v4l2object->streaming = TRUE;
-      }
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
-  }
   return TRUE;
-
-  /* ERRORS */
-activate_failed:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ,
-        (_("Error starting bufferpool")), (NULL));
-    return FALSE;
-  }
-start_failed:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ,
-        (_("Error starting streaming on device '%s'."), v4l2object->videodev),
-        GST_ERROR_SYSTEM);
-    return FALSE;
-  }
 }
 
 gboolean
@@ -2390,27 +2408,6 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object)
   if (!GST_V4L2_IS_ACTIVE (v4l2object))
     goto done;
 
-  switch (v4l2object->mode) {
-    case GST_V4L2_IO_RW:
-      break;
-    case GST_V4L2_IO_MMAP:
-    case GST_V4L2_IO_USERPTR:
-      if (v4l2object->streaming) {
-        /* we actually need to sync on all queued buffers but not
-         * on the non-queued ones */
-        GST_DEBUG_OBJECT (v4l2object->element, "STREAMOFF");
-        if (v4l2_ioctl (v4l2object->video_fd, VIDIOC_STREAMOFF,
-                &(v4l2object->type)) < 0)
-          goto stop_failed;
-
-        v4l2object->streaming = FALSE;
-      }
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
-  }
-
   if (v4l2object->pool) {
     GST_DEBUG_OBJECT (v4l2object->element, "deactivating pool");
     gst_buffer_pool_set_active (v4l2object->pool, FALSE);
@@ -2422,99 +2419,9 @@ gst_v4l2_object_stop (GstV4l2Object * v4l2object)
 
 done:
   return TRUE;
-
-  /* ERRORS */
-stop_failed:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, OPEN_READ,
-        (_("Error stopping streaming on device '%s'."), v4l2object->videodev),
-        GST_ERROR_SYSTEM);
-    return FALSE;
-  }
-}
-
-static GstFlowReturn
-gst_v4l2_object_get_read (GstV4l2Object * v4l2object, GstBuffer ** buf)
-{
-  GstFlowReturn res;
-  gint amount;
-  gint ret;
-  gpointer data;
-  gint buffersize;
-
-  buffersize = v4l2object->size;
-
-  /* In case the size per frame is unknown assume it's a streaming format (e.g.
-   * mpegts) and grab a reasonable default size instead */
-  if (buffersize == 0)
-    buffersize = 4096;
-
-  *buf = gst_buffer_new_and_alloc (buffersize);
-  data = gst_buffer_map (*buf, NULL, NULL, GST_MAP_WRITE);
-
-  do {
-    ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
-    if (G_UNLIKELY (ret < 0)) {
-      if (errno == EBUSY)
-        goto stopped;
-      if (errno == ENXIO) {
-        GST_DEBUG_OBJECT (v4l2object->element,
-            "v4l2 device doesn't support polling. Disabling");
-        v4l2object->can_poll_device = FALSE;
-      } else {
-        if (errno != EAGAIN && errno != EINTR)
-          goto select_error;
-      }
-    }
-    amount = v4l2_read (v4l2object->video_fd, data, buffersize);
-
-    if (amount == buffersize) {
-      break;
-    } else if (amount == -1) {
-      if (errno == EAGAIN || errno == EINTR) {
-        continue;
-      } else
-        goto read_error;
-    } else {
-      /* short reads can happen if a signal interrupts the read */
-      continue;
-    }
-  } while (TRUE);
-
-  gst_buffer_unmap (*buf, data, amount);
-
-  return GST_FLOW_OK;
-
-  /* ERRORS */
-select_error:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
-        ("select error %d: %s (%d)", ret, g_strerror (errno), errno));
-    res = GST_FLOW_ERROR;
-    goto cleanup;
-  }
-stopped:
-  {
-    GST_DEBUG ("stop called");
-    res = GST_FLOW_WRONG_STATE;
-    goto cleanup;
-  }
-read_error:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ,
-        (_("Error reading %d bytes from device '%s'."),
-            buffersize, v4l2object->videodev), GST_ERROR_SYSTEM);
-    res = GST_FLOW_ERROR;
-    goto cleanup;
-  }
-cleanup:
-  {
-    gst_buffer_unmap (*buf, data, 0);
-    gst_buffer_unref (*buf);
-    return res;
-  }
 }
 
+#if 0
 static GstFlowReturn
 gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf)
 {
@@ -2524,7 +2431,6 @@ gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf)
   gint32 trials = NUM_TRIALS;
   GstBuffer *pool_buffer;
   gboolean need_copy;
-  gint ret;
 
   pool = v4l2object->pool;
   if (!pool)
@@ -2533,21 +2439,8 @@ gst_v4l2_object_get_mmap (GstV4l2Object * v4l2object, GstBuffer ** buf)
   GST_DEBUG_OBJECT (v4l2object->element, "grab frame");
 
   for (;;) {
-    if (v4l2object->can_poll_device) {
-      ret = gst_poll_wait (v4l2object->poll, GST_CLOCK_TIME_NONE);
-      if (G_UNLIKELY (ret < 0)) {
-        if (errno == EBUSY)
-          goto stopped;
-        if (errno == ENXIO) {
-          GST_DEBUG_OBJECT (v4l2object->element,
-              "v4l2 device doesn't support polling. Disabling");
-          v4l2object->can_poll_device = FALSE;
-        } else {
-          if (errno != EAGAIN && errno != EINTR)
-            goto select_error;
-        }
-      }
-    }
+    if ((res = gst_v4l2_object_poll (v4l2object)) != GST_FLOW_OK)
+      goto poll_error;
 
     res = gst_buffer_pool_acquire_buffer (pool, &pool_buffer, NULL);
     if (res != GST_FLOW_OK)
@@ -2618,16 +2511,9 @@ no_buffer_pool:
     GST_DEBUG_OBJECT (v4l2object->element, "no buffer pool");
     return GST_FLOW_WRONG_STATE;
   }
-select_error:
-  {
-    GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
-        ("select error %d: %s (%d)", ret, g_strerror (errno), errno));
-    return GST_FLOW_ERROR;
-  }
-stopped:
+poll_error:
   {
-    GST_DEBUG ("stop called");
-    return GST_FLOW_WRONG_STATE;
+    return res;
   }
 too_many_trials:
   {
@@ -2639,32 +2525,57 @@ too_many_trials:
     return GST_FLOW_ERROR;
   }
 }
+#endif
 
-GstFlowReturn
-gst_v4l2_object_get_buffer (GstV4l2Object * v4l2object, GstBuffer ** buf)
+gboolean
+gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest,
+    GstBuffer * src)
 {
-  GstFlowReturn ret;
+  guint8 *data;
+  gsize size;
 
-  switch (v4l2object->type) {
-    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-      if (v4l2object->mode == GST_V4L2_IO_MMAP) {
-        ret = gst_v4l2_object_get_mmap (v4l2object, buf);
-      } else {
-        ret = gst_v4l2_object_get_read (v4l2object, buf);
-      }
-      break;
-    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-      ret = gst_buffer_pool_acquire_buffer (v4l2object->pool, buf, NULL);
-      break;
-    default:
-      ret = GST_FLOW_ERROR;
-      break;
+  if (v4l2object->info.finfo) {
+    GstVideoFrame src_frame, dest_frame;
+
+    GST_DEBUG_OBJECT (v4l2object->element, "copy video frame");
+
+    /* we have raw video, use videoframe copy to get strides right */
+    if (!gst_video_frame_map (&src_frame, &v4l2object->info, src, GST_MAP_READ))
+      goto invalid_buffer;
+
+    if (!gst_video_frame_map (&dest_frame, &v4l2object->info, dest,
+            GST_MAP_WRITE)) {
+      gst_video_frame_unmap (&src_frame);
+      goto invalid_buffer;
+    }
+
+    gst_video_frame_copy (&dest_frame, &src_frame);
+
+    gst_video_frame_unmap (&src_frame);
+    gst_video_frame_unmap (&dest_frame);
+  } else {
+    GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes");
+    data = gst_buffer_map (src, &size, NULL, GST_MAP_READ);
+    gst_buffer_fill (dest, 0, data, size);
+    gst_buffer_unmap (src, data, size);
+  }
+  GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element,
+      "slow copy into buffer %p", dest);
+
+  return TRUE;
+
+  /* ERRORS */
+invalid_buffer:
+  {
+    /* No Window available to put our image into */
+    GST_WARNING_OBJECT (v4l2object->element, "could not map image");
+    return FALSE;
   }
-  return ret;
 }
 
+
 GstFlowReturn
-gst_v4l2_object_output_buffer (GstV4l2Object * v4l2object, GstBuffer * buf)
+gst_v4l2_object_process_buffer (GstV4l2Object * v4l2object, GstBuffer * buf)
 {
   GstFlowReturn ret;
   GstBuffer *to_queue = NULL;
@@ -2678,9 +2589,6 @@ gst_v4l2_object_output_buffer (GstV4l2Object * v4l2object, GstBuffer * buf)
     to_queue = buf;
     ret = GST_FLOW_OK;
   } else {
-    guint8 *data;
-    gsize size;
-
     /* not our buffer */
     GST_LOG_OBJECT (v4l2object->element, "buffer not from our pool, copying");
 
@@ -2691,44 +2599,13 @@ gst_v4l2_object_output_buffer (GstV4l2Object * v4l2object, GstBuffer * buf)
     if (ret != GST_FLOW_OK)
       goto acquire_failed;
 
-    if (v4l2object->info.finfo) {
-      GstVideoFrame src_frame, dest_frame;
-
-      GST_DEBUG_OBJECT (v4l2object->element, "copy video frame");
-      /* we have raw video, use videoframe copy to get strides right */
-      if (!gst_video_frame_map (&src_frame, &v4l2object->info, buf,
-              GST_MAP_READ))
-        goto invalid_buffer;
-
-      if (!gst_video_frame_map (&dest_frame, &v4l2object->info, to_queue,
-              GST_MAP_WRITE)) {
-        gst_video_frame_unmap (&src_frame);
-        goto invalid_buffer;
-      }
-
-      gst_video_frame_copy (&dest_frame, &src_frame);
-
-      gst_video_frame_unmap (&src_frame);
-      gst_video_frame_unmap (&dest_frame);
-    } else {
-      GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes");
-      data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-      gst_buffer_fill (to_queue, 0, data, size);
-      gst_buffer_unmap (buf, data, size);
-    }
-    GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, v4l2object->element,
-        "slow copy into bufferpool buffer %p", to_queue);
+    if (!gst_v4l2_object_copy (v4l2object, to_queue, buf))
+      goto copy_failed;
   }
 
-  if (!gst_v4l2_buffer_pool_qbuf (v4l2object->pool, to_queue))
+  if (!gst_v4l2_buffer_pool_process (v4l2object->pool, to_queue))
     goto queue_failed;
 
-  if (!v4l2object->streaming) {
-    if (!gst_v4l2_object_start (v4l2object)) {
-      goto start_failed;
-    }
-  }
-
 done:
   if (to_queue != buf)
     gst_buffer_unref (to_queue);
@@ -2747,10 +2624,10 @@ acquire_failed:
     GST_DEBUG_OBJECT (v4l2object->element, "could not get buffer from pool");
     return ret;
   }
-invalid_buffer:
+copy_failed:
   {
     /* No Window available to put our image into */
-    GST_WARNING_OBJECT (v4l2object->element, "could not map image");
+    GST_WARNING_OBJECT (v4l2object->element, "could not copy image");
     ret = GST_FLOW_OK;
     goto done;
   }
@@ -2760,10 +2637,4 @@ queue_failed:
     ret = GST_FLOW_ERROR;
     goto done;
   }
-start_failed:
-  {
-    GST_DEBUG_OBJECT (v4l2object->element, "failed to start streaming");
-    ret = GST_FLOW_ERROR;
-    goto done;
-  }
 }
index 03e8382..f09d78a 100644 (file)
@@ -72,7 +72,7 @@ G_BEGIN_DECLS
 #define GST_V4L2_OBJECT(obj) (GstV4l2Object *)(obj)
 
 typedef enum {
-  GST_V4L2_IO_NONE    = 0,
+  GST_V4L2_IO_AUTO    = 0,
   GST_V4L2_IO_RW      = 1,
   GST_V4L2_IO_MMAP    = 2,
   GST_V4L2_IO_USERPTR = 3
@@ -121,6 +121,9 @@ struct _GstV4l2Object {
   guint size;
   GstClockTime duration;
 
+  /* wanted mode */
+  GstV4l2IOMode req_mode;
+
   /* optional pool */
   guint32 num_buffers;
   guint32 min_queued_bufs;
@@ -174,7 +177,8 @@ GType gst_v4l2_object_get_type (void);
     PROP_CONTRAST,                     \
     PROP_SATURATION,                   \
     PROP_HUE,                           \
-    PROP_TV_NORM
+    PROP_TV_NORM,                       \
+    PROP_IO_MODE
 
 /* create/destroy */
 GstV4l2Object *        gst_v4l2_object_new              (GstElement * element,
@@ -223,14 +227,17 @@ GstStructure* gst_v4l2_object_v4l2fourcc_to_structure (guint32 fourcc);
 
 gboolean      gst_v4l2_object_set_format (GstV4l2Object *v4l2object, GstCaps * caps);
 
-gboolean      gst_v4l2_object_start       (GstV4l2Object *v4l2object);
+gboolean      gst_v4l2_object_unlock      (GstV4l2Object *v4l2object);
+gboolean      gst_v4l2_object_unlock_stop (GstV4l2Object *v4l2object);
+
 gboolean      gst_v4l2_object_stop        (GstV4l2Object *v4l2object);
 
 
-/* capture returns a filled buffer, output returns an empty buffer */
-GstFlowReturn gst_v4l2_object_get_buffer     (GstV4l2Object * v4l2object, GstBuffer ** buf);
-/* output the filled buffer */
-GstFlowReturn gst_v4l2_object_output_buffer  (GstV4l2Object * v4l2object, GstBuffer * buf);
+gboolean      gst_v4l2_object_copy        (GstV4l2Object * v4l2object,
+                                           GstBuffer * dest, GstBuffer *src);
+
+/* capture writes into the buffer, output renders the buffer */
+GstFlowReturn gst_v4l2_object_process_buffer (GstV4l2Object * v4l2object, GstBuffer * buf);
 
 
 #define GST_IMPLEMENT_V4L2_PROBE_METHODS(Type_Class, interface_as_function)                 \
index 14a0f31..72d77f5 100644 (file)
@@ -747,7 +747,7 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
 
   GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf);
 
-  ret = gst_v4l2_object_output_buffer (obj, buf);
+  ret = gst_v4l2_object_process_buffer (obj, buf);
 
   return ret;
 }
index d4e159b..a9511ab 100644 (file)
@@ -64,7 +64,7 @@
 GST_DEBUG_CATEGORY (v4l2src_debug);
 #define GST_CAT_DEFAULT v4l2src_debug
 
-#define PROP_DEF_QUEUE_SIZE         2
+#define PROP_DEF_QUEUE_SIZE         4
 #define PROP_DEF_ALWAYS_COPY        TRUE
 #define PROP_DEF_DECIMATE           1
 
@@ -123,7 +123,7 @@ static GstCaps *gst_v4l2src_get_caps (GstBaseSrc * src, GstCaps * filter);
 static gboolean gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query);
 static gboolean gst_v4l2src_setup_allocation (GstBaseSrc * src,
     GstQuery * query);
-static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** out);
+static GstFlowReturn gst_v4l2src_fill (GstPushSrc * src, GstBuffer * out);
 static void gst_v4l2src_fixate (GstBaseSrc * basesrc, GstCaps * caps);
 static gboolean gst_v4l2src_negotiate (GstBaseSrc * basesrc);
 
@@ -198,7 +198,7 @@ gst_v4l2src_class_init (GstV4l2SrcClass * klass)
   basesrc_class->setup_allocation =
       GST_DEBUG_FUNCPTR (gst_v4l2src_setup_allocation);
 
-  pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_v4l2src_create);
+  pushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_v4l2src_fill);
 
   klass->v4l2_class_devices = NULL;
 
@@ -525,12 +525,6 @@ gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps)
     /* error already posted */
     return FALSE;
 
-  if (!gst_v4l2_object_start (obj))
-    return FALSE;
-
-  /* now store the expected output size */
-  v4l2src->frame_byte_size = obj->size;
-
   return TRUE;
 }
 
@@ -538,15 +532,71 @@ static gboolean
 gst_v4l2src_setup_allocation (GstBaseSrc * bsrc, GstQuery * query)
 {
   GstV4l2Src *src;
+  GstV4l2Object *obj;
   GstBufferPool *pool;
   guint size, min, max, prefix, alignment;
 
   src = GST_V4L2SRC (bsrc);
+  obj = src->v4l2object;
 
   gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
       &alignment, &pool);
 
-  /* do something clever here */
+  GST_DEBUG_OBJECT (src, "allocation: size:%u min:%u max:%u prefix:%u "
+      "align:%u pool:%" GST_PTR_FORMAT, size, min, max, prefix, alignment,
+      pool);
+
+  if (min != 0) {
+    /* if there is a min-buffers suggestion, use it. We add 1 because we need 1
+     * buffer extra to capture while the other two buffers are downstream */
+    min += 1;
+  } else {
+    min = obj->num_buffers;
+  }
+
+  /* select a pool */
+  switch (obj->mode) {
+    case GST_V4L2_IO_RW:
+      if (pool == NULL) {
+        /* no downstream pool, use our own then */
+        GST_DEBUG_OBJECT (src,
+            "read/write mode: no downstream pool, using our own");
+        pool = obj->pool;
+        size = obj->size;
+      } else {
+        /* in READ/WRITE mode, prefer a downstream pool because our own pool
+         * doesn't help much, we have to write to it as well */
+        GST_DEBUG_OBJECT (src, "read/write mode: using downstream pool");
+        /* use the bigest size, when we use our own pool we can't really do any
+         * other size than what the hardware gives us but for downstream pools
+         * we can try */
+        size = MAX (size, obj->size);
+      }
+      break;
+    case GST_V4L2_IO_MMAP:
+    case GST_V4L2_IO_USERPTR:
+      /* in streaming mode, prefer our own pool */
+      pool = obj->pool;
+      size = obj->size;
+      GST_DEBUG_OBJECT (src,
+          "streaming mode: using our own pool %" GST_PTR_FORMAT, pool);
+      break;
+    case GST_V4L2_IO_AUTO:
+    default:
+      GST_WARNING_OBJECT (src, "unhandled mode");
+      break;
+  }
+
+  if (pool) {
+    GstStructure *config;
+    const GstCaps *caps;
+
+    config = gst_buffer_pool_get_config (pool);
+    gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL, NULL);
+    gst_buffer_pool_config_set (config, caps, size, min, max, prefix,
+        alignment);
+    gst_buffer_pool_set_config (pool, config);
+  }
 
   gst_query_set_allocation_params (query, size, min, max, prefix,
       alignment, pool);
@@ -633,22 +683,14 @@ 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;
+  return gst_v4l2_object_unlock (v4l2src->v4l2object);
 }
 
 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;
+  return gst_v4l2_object_unlock_stop (v4l2src->v4l2object);
 }
 
 static gboolean
@@ -702,79 +744,89 @@ gst_v4l2src_change_state (GstElement * element, GstStateChange transition)
 }
 
 static GstFlowReturn
-gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
+gst_v4l2src_fill (GstPushSrc * src, GstBuffer * buf)
 {
   GstV4l2Src *v4l2src = GST_V4L2SRC (src);
   GstV4l2Object *obj = v4l2src->v4l2object;
-  int i;
   GstFlowReturn ret;
+  GstClock *clock;
+  GstClockTime timestamp, duration;
 
+#if 0
+  int i;
   /* decimate, just capture and throw away frames */
   for (i = 0; i < v4l2src->decimate - 1; i++) {
-    ret = gst_v4l2_object_get_buffer (obj, buf);
+    ret = gst_v4l2_buffer_pool_process (obj, buf);
     if (ret != GST_FLOW_OK) {
       return ret;
     }
     gst_buffer_unref (*buf);
   }
+#endif
 
-  ret = gst_v4l2_object_get_buffer (obj, buf);
+  ret = gst_v4l2_buffer_pool_process (obj->pool, buf);
 
-  /* set buffer metadata */
-  if (G_LIKELY (ret == GST_FLOW_OK && *buf)) {
-    GstClock *clock;
-    GstClockTime timestamp, duration;
-
-    GST_BUFFER_OFFSET (*buf) = v4l2src->offset++;
-    GST_BUFFER_OFFSET_END (*buf) = v4l2src->offset;
-
-    /* timestamps, LOCK to get clock and base time. */
-    /* FIXME: element clock and base_time is rarely changing */
-    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 (G_UNLIKELY (ret != GST_FLOW_OK))
+    goto error;
 
-    duration = obj->duration;
+  /* set buffer metadata */
+  GST_BUFFER_OFFSET (buf) = v4l2src->offset++;
+  GST_BUFFER_OFFSET_END (buf) = v4l2src->offset;
+
+  /* timestamps, LOCK to get clock and base time. */
+  /* FIXME: element clock and base_time is rarely changing */
+  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 (G_LIKELY (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);
+  duration = obj->duration;
 
-      /* if we have a framerate adjust timestamp for frame latency */
-      if (GST_CLOCK_TIME_IS_VALID (duration)) {
-        if (timestamp > duration)
-          timestamp -= duration;
-        else
-          timestamp = 0;
-      }
-    }
+  if (G_LIKELY (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);
 
-    /* activate settings for next frame */
+    /* if we have a framerate adjust timestamp for frame latency */
     if (GST_CLOCK_TIME_IS_VALID (duration)) {
-      v4l2src->ctrl_time += duration;
-    } else {
-      /* this is not very good (as it should be the next timestamp),
-       * still good enough for linear fades (as long as it is not -1)
-       */
-      v4l2src->ctrl_time = timestamp;
+      if (timestamp > duration)
+        timestamp -= duration;
+      else
+        timestamp = 0;
     }
-    gst_object_sync_values (G_OBJECT (src), v4l2src->ctrl_time);
-    GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (v4l2src->ctrl_time));
+  }
 
-    /* FIXME: use the timestamp from the buffer itself! */
-    GST_BUFFER_TIMESTAMP (*buf) = timestamp;
-    GST_BUFFER_DURATION (*buf) = duration;
+  /* activate settings for next frame */
+  if (GST_CLOCK_TIME_IS_VALID (duration)) {
+    v4l2src->ctrl_time += duration;
+  } else {
+    /* this is not very good (as it should be the next timestamp),
+     * still good enough for linear fades (as long as it is not -1)
+     */
+    v4l2src->ctrl_time = timestamp;
   }
+  gst_object_sync_values (G_OBJECT (src), v4l2src->ctrl_time);
+  GST_INFO_OBJECT (src, "sync to %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (v4l2src->ctrl_time));
+
+  /* FIXME: use the timestamp from the buffer itself! */
+  GST_BUFFER_TIMESTAMP (buf) = timestamp;
+  GST_BUFFER_DURATION (buf) = duration;
+
   return ret;
+
+  /* ERROR */
+error:
+  {
+    GST_ERROR_OBJECT (src, "error processing buffer");
+    return ret;
+  }
 }
 
 
index 03fd0dd..8ebaa57 100644 (file)
@@ -60,8 +60,6 @@ struct _GstV4l2Src
   /* pads */
   GstCaps *probed_caps;
 
-  guint32 frame_byte_size;
-
   int decimate;
 
   guint64 offset;