mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-05 05:52:37 +00:00
v4l2: Improve buffer management
Query the amount of available buffers when doing set_config(). This allows us to configure the parent bufferpool with the number of buffers to preallocate. Keep track of the provided allocator and use it when we need to allocate a buffer in RW mode. When we are can not allocate the requested max_buffers amount of buffers, make sure we keep 2 buffers around in the pool and copy them into an output buffer. This makes sure that we always have a buffer to capture into. We also need to detect those copied buffers and unref them when they return to the pool.
This commit is contained in:
parent
713ddbf541
commit
eecb9a96a6
3 changed files with 118 additions and 71 deletions
|
@ -243,9 +243,10 @@ gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
|
|||
GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
|
||||
GstV4l2Object *obj = pool->obj;
|
||||
GstCaps *caps;
|
||||
guint size, min_buffers, max_buffers;
|
||||
guint size, min_buffers, max_buffers, num_buffers, copy_threshold;
|
||||
GstAllocator *allocator;
|
||||
GstAllocationParams params;
|
||||
struct v4l2_requestbuffers breq;
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "set config");
|
||||
|
||||
|
@ -276,8 +277,76 @@ gst_v4l2_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
|
|||
|
||||
GST_DEBUG_OBJECT (pool, "config %" GST_PTR_FORMAT, config);
|
||||
|
||||
switch (obj->mode) {
|
||||
case GST_V4L2_IO_RW:
|
||||
/* we preallocate 1 buffer, this value also instructs the latency
|
||||
* calculation to have 1 frame latency max */
|
||||
num_buffers = 1;
|
||||
copy_threshold = 0;
|
||||
break;
|
||||
case GST_V4L2_IO_MMAP:
|
||||
{
|
||||
/* request a reasonable number of buffers when no max specified. We will
|
||||
* copy when we run out of buffers */
|
||||
if (max_buffers == 0)
|
||||
num_buffers = 4;
|
||||
else
|
||||
num_buffers = max_buffers;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* update min buffers with the amount of buffers we just reserved. We need
|
||||
* to configure this value in the bufferpool so that the default start
|
||||
* implementation calls our allocate function */
|
||||
min_buffers = breq.count;
|
||||
|
||||
if (max_buffers == 0 || num_buffers < max_buffers) {
|
||||
/* if we are asked to provide more buffers than we have allocated, start
|
||||
* copying buffers when we only have 2 buffers left in the pool */
|
||||
copy_threshold = 2;
|
||||
} else {
|
||||
/* we are certain that we have enough buffers so we don't need to
|
||||
* copy */
|
||||
copy_threshold = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_V4L2_IO_USERPTR:
|
||||
default:
|
||||
num_buffers = 0;
|
||||
copy_threshold = 0;
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
pool->size = size;
|
||||
pool->max_buffers = max_buffers;
|
||||
pool->num_buffers = num_buffers;
|
||||
pool->copy_threshold = copy_threshold;
|
||||
if (pool->allocator)
|
||||
gst_allocator_unref (pool->allocator);
|
||||
if ((pool->allocator = allocator))
|
||||
gst_allocator_ref (allocator);
|
||||
pool->params = params;
|
||||
|
||||
gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
|
||||
|
@ -298,6 +367,19 @@ wrong_config:
|
|||
GST_ERROR_OBJECT (pool, "invalid config %" GST_PTR_FORMAT, config);
|
||||
return FALSE;
|
||||
}
|
||||
reqbufs_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool,
|
||||
"error requesting %d buffers: %s", num_buffers, g_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
no_buffers:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool,
|
||||
"we received %d from device '%s', we want at least %d",
|
||||
breq.count, obj->videodev, GST_V4L2_MIN_BUFFERS);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -337,55 +419,9 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
|
|||
{
|
||||
GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool);
|
||||
GstV4l2Object *obj = pool->obj;
|
||||
struct v4l2_requestbuffers breq;
|
||||
gint max_buffers;
|
||||
|
||||
max_buffers = pool->max_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",
|
||||
max_buffers);
|
||||
|
||||
if (max_buffers == 0)
|
||||
max_buffers = 4;
|
||||
|
||||
memset (&breq, 0, sizeof (struct v4l2_requestbuffers));
|
||||
breq.type = obj->type;
|
||||
breq.count = max_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 (max_buffers != breq.count) {
|
||||
GST_WARNING_OBJECT (pool, "using %u buffers instead", breq.count);
|
||||
max_buffers = breq.count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_V4L2_IO_USERPTR:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
pool->obj = obj;
|
||||
pool->max_buffers = max_buffers;
|
||||
pool->buffers = g_new0 (GstBuffer *, max_buffers);
|
||||
pool->buffers = g_new0 (GstBuffer *, pool->num_buffers);
|
||||
pool->num_allocated = 0;
|
||||
|
||||
/* now, allocate the buffers: */
|
||||
|
@ -403,19 +439,6 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool)
|
|||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
reqbufs_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool,
|
||||
"error requesting %d buffers: %s", max_buffers, g_strerror (errno));
|
||||
return FALSE;
|
||||
}
|
||||
no_buffers:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool,
|
||||
"we received %d from device '%s', we want at least %d",
|
||||
breq.count, obj->videodev, GST_V4L2_MIN_BUFFERS);
|
||||
return FALSE;
|
||||
}
|
||||
start_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool, "failed to start streaming");
|
||||
|
@ -519,16 +542,22 @@ gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf)
|
|||
gint index;
|
||||
|
||||
meta = GST_V4L2_META_GET (buf);
|
||||
g_assert (meta != NULL);
|
||||
if (meta == NULL) {
|
||||
GST_LOG_OBJECT (pool, "unref copied buffer %p", buf);
|
||||
/* no meta, it was a copied buffer that we can unref */
|
||||
gst_buffer_unref (buf);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
index = meta->vbuffer.index;
|
||||
|
||||
GST_LOG_OBJECT (pool, "enqueue buffer %p, index:%d, queued:%d", buf,
|
||||
index, pool->num_queued);
|
||||
GST_LOG_OBJECT (pool, "enqueue buffer %p, index:%d, queued:%d, flags:%08x",
|
||||
buf, index, pool->num_queued, meta->vbuffer.flags);
|
||||
|
||||
if (pool->buffers[index] != NULL)
|
||||
goto already_queued;
|
||||
|
||||
GST_LOG_OBJECT (pool, "doing QBUF");
|
||||
if (v4l2_ioctl (pool->video_fd, VIDIOC_QBUF, &meta->vbuffer) < 0)
|
||||
goto queue_failed;
|
||||
|
||||
|
@ -699,6 +728,21 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
|
|||
* storage for our buffers. This function does poll first so we can
|
||||
* interrupt it fine. */
|
||||
ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer);
|
||||
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||
goto done;
|
||||
|
||||
/* start copying buffers when we are running low on buffers */
|
||||
if (pool->num_queued < pool->copy_threshold) {
|
||||
GstBuffer *copy;
|
||||
|
||||
/* copy the memory */
|
||||
copy = gst_buffer_copy (*buffer);
|
||||
GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buffer, copy);
|
||||
|
||||
/* and requeue so that we can continue capturing */
|
||||
ret = gst_v4l2_buffer_pool_qbuf (pool, *buffer);
|
||||
*buffer = copy;
|
||||
}
|
||||
break;
|
||||
|
||||
case GST_V4L2_IO_USERPTR:
|
||||
|
@ -734,6 +778,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
|
|||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -822,7 +867,8 @@ gst_v4l2_buffer_pool_finalize (GObject * object)
|
|||
|
||||
if (pool->video_fd >= 0)
|
||||
v4l2_close (pool->video_fd);
|
||||
|
||||
if (pool->allocator)
|
||||
gst_allocator_unref (pool->allocator);
|
||||
g_free (pool->buffers);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
|
|
|
@ -54,11 +54,12 @@ struct _GstV4l2BufferPool
|
|||
GstAllocator *allocator;
|
||||
GstAllocationParams params;
|
||||
guint size;
|
||||
guint max_buffers;
|
||||
gboolean add_videometa;
|
||||
|
||||
guint num_buffers; /* number of buffers we use */
|
||||
guint num_allocated; /* number of buffers allocated by the driver */
|
||||
guint num_queued; /* number of buffers queued in the driver */
|
||||
guint copy_threshold; /* when our pool runs lower, start handing out copies */
|
||||
|
||||
gboolean streaming;
|
||||
|
||||
|
|
|
@ -638,7 +638,7 @@ gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query)
|
|||
case GST_QUERY_LATENCY:{
|
||||
GstClockTime min_latency, max_latency;
|
||||
guint32 fps_n, fps_d;
|
||||
guint max_buffers;
|
||||
guint num_buffers;
|
||||
|
||||
/* device must be open */
|
||||
if (!GST_V4L2_IS_OPEN (obj)) {
|
||||
|
@ -661,12 +661,12 @@ gst_v4l2src_query (GstBaseSrc * bsrc, GstQuery * query)
|
|||
min_latency = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
|
||||
|
||||
/* max latency is total duration of the frame buffer */
|
||||
max_buffers = GST_V4L2_BUFFER_POOL_CAST (obj->pool)->max_buffers;
|
||||
num_buffers = GST_V4L2_BUFFER_POOL_CAST (obj->pool)->num_buffers;
|
||||
|
||||
if (max_buffers == 0)
|
||||
if (num_buffers == 0)
|
||||
max_latency = -1;
|
||||
else
|
||||
max_latency = max_buffers * min_latency;
|
||||
max_latency = num_buffers * min_latency;
|
||||
|
||||
GST_DEBUG_OBJECT (bsrc,
|
||||
"report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
|
||||
|
|
Loading…
Reference in a new issue