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);
}
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++;
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;
}
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;
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 */
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
{
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);
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;
/* 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);
return GST_FLOW_OK;
/* ERRORS */
+poll_error:
+ {
+ GST_DEBUG ("poll error %s", gst_flow_get_name (res));
+ return res;
+ }
error:
{
GST_WARNING_OBJECT (pool,
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 */
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
}
}
-/**
- * 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;
}
}
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;
};
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);
#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
{
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)
"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 *
}
break;
#endif
+ case PROP_IO_MODE:
+ v4l2object->req_mode = g_value_get_enum (value);
+ break;
default:
return FALSE;
break;
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;
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);
}
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);
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,
}
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
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);
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)
{
gint32 trials = NUM_TRIALS;
GstBuffer *pool_buffer;
gboolean need_copy;
- gint ret;
pool = v4l2object->pool;
if (!pool)
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)
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:
{
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;
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");
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);
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;
}
ret = GST_FLOW_ERROR;
goto done;
}
-start_failed:
- {
- GST_DEBUG_OBJECT (v4l2object->element, "failed to start streaming");
- ret = GST_FLOW_ERROR;
- goto done;
- }
}
#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
guint size;
GstClockTime duration;
+ /* wanted mode */
+ GstV4l2IOMode req_mode;
+
/* optional pool */
guint32 num_buffers;
guint32 min_queued_bufs;
PROP_CONTRAST, \
PROP_SATURATION, \
PROP_HUE, \
- PROP_TV_NORM
+ PROP_TV_NORM, \
+ PROP_IO_MODE
/* create/destroy */
GstV4l2Object * gst_v4l2_object_new (GstElement * element,
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) \
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;
}
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
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);
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;
/* 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;
}
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);
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
}
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;
+ }
}
/* pads */
GstCaps *probed_caps;
- guint32 frame_byte_size;
-
int decimate;
guint64 offset;