mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
v4l2: More work on bufferpools
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.
This commit is contained in:
parent
39716c02a7
commit
55eb26f1e7
7 changed files with 810 additions and 475 deletions
|
@ -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;
|
||||
|
||||
obj = pool->obj;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
"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,6 +129,15 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
|
|||
obj = pool->obj;
|
||||
info = &obj->info;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -161,12 +181,19 @@ gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
|
|||
offset[0] = 0;
|
||||
stride[0] = obj->bytesperline;
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "adding video meta");
|
||||
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;
|
||||
|
||||
|
@ -224,6 +254,39 @@ wrong_config:
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
start_streaming (GstBufferPool * bpool)
|
||||
{
|
||||
GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
|
||||
GstV4l2Object *obj = pool->obj;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pool->streaming = TRUE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
start_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool, "error with STREAMON %d (%s)", errno,
|
||||
g_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
|
||||
{
|
||||
|
@ -233,10 +296,18 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
|
|||
struct v4l2_requestbuffers breq;
|
||||
gint num_buffers;
|
||||
|
||||
num_buffers = pool->max_buffers;
|
||||
num_buffers = pool->min_buffers;
|
||||
|
||||
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);
|
||||
GST_DEBUG_OBJECT (pool, "starting, requesting %d MMAP buffers",
|
||||
num_buffers);
|
||||
|
||||
memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
|
||||
breq.type = obj->type;
|
||||
|
@ -257,14 +328,20 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
|
|||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,14 +2082,32 @@ 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);
|
||||
|
||||
/* use specified mode */
|
||||
v4l2object->mode = v4l2object->req_mode;
|
||||
|
||||
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) {
|
||||
guint num_buffers;
|
||||
GstStructure *config;
|
||||
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;
|
||||
|
||||
/* keep track of current number of buffers */
|
||||
num_buffers = v4l2object->num_buffers;
|
||||
|
@ -2062,21 +2118,10 @@ gst_v4l2_object_setup_pool (GstV4l2Object * v4l2object, GstCaps * caps)
|
|||
if (!(v4l2object->pool = gst_v4l2_buffer_pool_new (v4l2object)))
|
||||
goto buffer_pool_new_failed;
|
||||
|
||||
GST_INFO_OBJECT (v4l2object->element, "accessing buffers via mmap()");
|
||||
v4l2object->mode = GST_V4L2_IO_MMAP;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (v4l2object->pool)
|
||||
if (!gst_buffer_pool_set_active (v4l2object->pool, TRUE))
|
||||
goto activate_failed;
|
||||
|
||||
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:
|
||||
gboolean
|
||||
gst_v4l2_object_unlock_stop (GstV4l2Object * v4l2object)
|
||||
{
|
||||
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;
|
||||
}
|
||||
GST_LOG_OBJECT (v4l2object->element, "flush stop poll");
|
||||
gst_poll_set_flushing (v4l2object->poll, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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:
|
||||
poll_error:
|
||||
{
|
||||
GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, READ, (NULL),
|
||||
("select error %d: %s (%d)", ret, g_strerror (errno), errno));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
stopped:
|
||||
{
|
||||
GST_DEBUG ("stop called");
|
||||
return GST_FLOW_WRONG_STATE;
|
||||
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);
|
||||
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 {
|
||||
ret = gst_v4l2_object_get_read (v4l2object, buf);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
||||
ret = gst_buffer_pool_acquire_buffer (v4l2object->pool, buf, NULL);
|
||||
break;
|
||||
default:
|
||||
ret = GST_FLOW_ERROR;
|
||||
break;
|
||||
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;
|
||||
if (!gst_v4l2_object_copy (v4l2object, to_queue, buf))
|
||||
goto copy_failed;
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,31 +744,34 @@ 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);
|
||||
|
||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||
goto error;
|
||||
|
||||
/* 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;
|
||||
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 */
|
||||
|
@ -771,10 +816,17 @@ gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf)
|
|||
GST_TIME_ARGS (v4l2src->ctrl_time));
|
||||
|
||||
/* FIXME: use the timestamp from the buffer itself! */
|
||||
GST_BUFFER_TIMESTAMP (*buf) = timestamp;
|
||||
GST_BUFFER_DURATION (*buf) = duration;
|
||||
}
|
||||
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||
GST_BUFFER_DURATION (buf) = duration;
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERROR */
|
||||
error:
|
||||
{
|
||||
GST_ERROR_OBJECT (src, "error processing buffer");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -60,8 +60,6 @@ struct _GstV4l2Src
|
|||
/* pads */
|
||||
GstCaps *probed_caps;
|
||||
|
||||
guint32 frame_byte_size;
|
||||
|
||||
int decimate;
|
||||
|
||||
guint64 offset;
|
||||
|
|
Loading…
Reference in a new issue