GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
mem->plane, group->buffer.index);
+ switch (allocator->memory) {
+ case V4L2_MEMORY_DMABUF:
+ close (mem->dmafd);
+ mem->dmafd = -1;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ mem->data = NULL;
+ break;
+ default:
+ break;
+ }
+
/* When all memory are back, put the group back in the free queue */
if (g_atomic_int_dec_and_test (&group->mems_allocated)) {
GST_LOG_OBJECT (allocator, "buffer %u released", group->buffer.index);
return group;
}
+static void
+_cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
+{
+ if (group->mems_allocated > 0) {
+ gint i;
+ /* If one or more mmap worked, we need to unref the memory, otherwise
+ * they will keep a ref on the allocator and leak it. This will put back
+ * the group into the free_queue */
+ for (i = 0; i < group->n_mem; i++)
+ gst_memory_unref (group->mem[i]);
+ } else {
+ /* Otherwise, group has to be on free queue for _stop() to work */
+ gst_atomic_queue_push (allocator->free_queue, group);
+ }
+}
+
GstV4l2MemoryGroup *
gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
{
{
GST_ERROR_OBJECT (allocator, "Failed to mmap buffer: %s",
g_strerror (errno));
-
- if (group->mems_allocated > 0) {
- /* If one or more mmap worked, we need to unref the memory, otherwise
- * they will keep a ref on the allocator and leak it. This will put back
- * the group into the free_queue */
- for (i = 0; i < group->n_mem; i++)
- gst_memory_unref (group->mem[i]);
- } else {
- /* Otherwise, group has to be on free queue for _stop() to work */
- gst_atomic_queue_push (allocator->free_queue, group);
- }
+ _cleanup_failed_alloc (allocator, group);
return NULL;
}
}
}
cleanup:
{
- if (group->mems_allocated > 0) {
- for (i = 0; i < group->n_mem; i++)
- gst_memory_unref (group->mem[i]);
+ _cleanup_failed_alloc (allocator, group);
+ return NULL;
+ }
+}
+
+GstV4l2MemoryGroup *
+gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
+{
+ GstV4l2MemoryGroup *group;
+ gint i;
+
+ g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
+
+ group = gst_v4l2_allocator_alloc (allocator);
+
+ if (group == NULL)
+ return NULL;
+
+ for (i = 0; i < group->n_mem; i++) {
+ GST_LOG_OBJECT (allocator, "allocation empty DMABUF import group");
+
+ if (group->mem[i] == NULL) {
+ group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
+ NULL, 0, 0, 0, 0, i, NULL, -1, group);
} else {
- gst_atomic_queue_push (allocator->free_queue, group);
+ /* Take back the allocator reference */
+ gst_object_ref (allocator);
}
- return NULL;
+
+ group->mems_allocated++;
}
+
+ gst_v4l2_allocator_clear_dmabufin (allocator, group);
+
+ return group;
}
-#if 0
GstV4l2MemoryGroup *
+gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
+{
+ GstV4l2MemoryGroup *group;
+ gint i;
+
+ g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
+
+ group = gst_v4l2_allocator_alloc (allocator);
+
+ if (group == NULL)
+ return NULL;
+
+ for (i = 0; i < group->n_mem; i++) {
+
+ GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
+
+ if (group->mem[i] == NULL) {
+ group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
+ NULL, 0, 0, 0, 0, i, NULL, -1, group);
+ } else {
+ /* Take back the allocator reference */
+ gst_object_ref (allocator);
+ }
+
+ group->mems_allocated++;
+ }
+
+ gst_v4l2_allocator_clear_userptr (allocator, group);
+
+ return group;
+}
+
+gboolean
gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
- gint dmabuf_fd[VIDEO_MAX_PLANES])
+ GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
{
- /* TODO */
- return NULL;
+ GstV4l2Memory *mem;
+ gint i;
+
+ g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
+
+ if (group->n_mem != n_mem)
+ goto n_mem_missmatch;
+
+ for (i = 0; i < group->n_mem; i++) {
+ gint dmafd;
+ gsize size, offset, maxsize;
+
+ if (!gst_is_dmabuf_memory (dma_mem[i]))
+ goto not_dmabuf;
+
+ size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
+
+ if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0)
+ goto dup_failed;
+
+ GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i);
+
+ mem = (GstV4l2Memory *) group->mem[i];
+
+ /* Update memory */
+ mem->mem.maxsize = maxsize;
+ mem->mem.offset = offset;
+ mem->mem.size = size;
+ mem->dmafd = dmafd;
+
+ /* Update v4l2 structure */
+ group->planes[i].length = maxsize;
+ group->planes[i].bytesused = size;
+ group->planes[i].m.fd = dmafd;
+ group->planes[i].data_offset = offset;
+ }
+
+ /* Copy into buffer structure if not using planes */
+ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
+ group->buffer.bytesused = group->planes[0].bytesused;
+ group->buffer.length = group->planes[0].length;
+ group->buffer.m.fd = group->planes[0].m.userptr;
+ } else {
+ group->buffer.length = group->n_mem;
+ }
+
+ return TRUE;
+
+n_mem_missmatch:
+ {
+ GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
+ group->n_mem);
+ return FALSE;
+ }
+not_dmabuf:
+ {
+ GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
+ return FALSE;
+ }
+dup_failed:
+ {
+ GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
+ g_strerror (errno));
+ return FALSE;
+ }
}
-GstV4l2MemoryGroup *
+void
+gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup * group)
+{
+ GstV4l2Memory *mem;
+ gint i;
+
+ g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
+
+ for (i = 0; i < group->n_mem; i++) {
+
+ mem = (GstV4l2Memory *) group->mem[i];
+
+ GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d",
+ mem->dmafd, i);
+
+ if (mem->dmafd >= 0)
+ close (mem->dmafd);
+
+ /* Update memory */
+ mem->mem.maxsize = 0;
+ mem->mem.offset = 0;
+ mem->mem.size = 0;
+ mem->dmafd = -1;
+
+ /* Update v4l2 structure */
+ group->planes[i].length = 0;
+ group->planes[i].bytesused = 0;
+ group->planes[i].m.fd = -1;
+ group->planes[i].data_offset = 0;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
+ group->buffer.bytesused = 0;
+ group->buffer.length = 0;
+ group->buffer.m.fd = -1;
+ }
+}
+
+
+gboolean
gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
- gpointer data[VIDEO_MAX_PLANES], gint stride[VIDEO_MAX_PLANES],
- gint offset[VIDEO_MAX_PLANES])
+ GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
+ gpointer * data, gsize * offset)
{
- /* TODO */
- return NULL;
+ GstV4l2Memory *mem;
+ gint i;
+
+ g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
+
+ /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */
+ if (n_planes != group->n_mem)
+ goto n_mem_missmatch;
+
+ for (i = 0; i < group->n_mem; i++) {
+ gsize size, maxsize;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
+ struct v4l2_pix_format_mplane *pix = &allocator->format.fmt.pix_mp;
+ maxsize = pix->plane_fmt[i].sizeimage;
+ } else {
+ maxsize = allocator->format.fmt.pix.sizeimage;
+ }
+
+ if ((i + 1) == n_planes) {
+ size = img_size - offset[i];
+ } else {
+ size = offset[i + 1] - offset[i];
+ }
+
+ g_assert (size <= img_size);
+
+ GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
+ G_GSIZE_FORMAT, data[i], i, size);
+
+ mem = (GstV4l2Memory *) group->mem[i];
+
+ mem->mem.maxsize = maxsize;
+ mem->mem.size = size;
+ mem->data = data[i];
+
+ group->planes[i].length = maxsize;
+ group->planes[i].bytesused = size;
+ group->planes[i].m.userptr = (unsigned long) data[i];
+ group->planes[i].data_offset = 0;
+ }
+
+ /* Copy into buffer structure if not using planes */
+ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
+ group->buffer.bytesused = group->planes[0].bytesused;
+ group->buffer.length = group->planes[0].length;
+ group->buffer.m.userptr = group->planes[0].m.userptr;
+ } else {
+ group->buffer.length = group->n_mem;
+ }
+
+ return TRUE;
+
+n_mem_missmatch:
+ {
+ GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
+ n_planes, group->n_mem);
+ return FALSE;
+ }
+}
+
+void
+gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup * group)
+{
+ GstV4l2Memory *mem;
+ gint i;
+
+ g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
+
+ for (i = 0; i < group->n_mem; i++) {
+ mem = (GstV4l2Memory *) group->mem[i];
+
+ GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %"
+ G_GSIZE_FORMAT, mem->data, i, mem->mem.size);
+
+ mem->mem.maxsize = 0;
+ mem->mem.size = 0;
+ mem->data = NULL;
+
+ group->planes[i].length = 0;
+ group->planes[i].bytesused = 0;
+ group->planes[i].m.userptr = 0;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
+ group->buffer.bytesused = 0;
+ group->buffer.length = 0;
+ group->buffer.m.userptr = 0;
+ }
}
-#endif
void
gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
GstV4l2MemoryGroup* gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
GstAllocator * dmabuf_allocator);
+GstV4l2MemoryGroup * gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator);
+
+GstV4l2MemoryGroup * gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator);
+
+gboolean gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup *group,
+ gint n_mem, GstMemory ** dma_mem);
+
+void gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup *group);
+
+
+gboolean gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup *group,
+ gsize img_size, int n_planes,
+ gpointer * data, gsize * offset);
+
+void gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
+ GstV4l2MemoryGroup *group);
+
void gst_v4l2_allocator_flush (GstV4l2Allocator * allocator);
gboolean gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
#include <gst/glib-compat-private.h>
GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
+GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
#define GST_CAT_DEFAULT v4l2_debug
+#define GST_V4L2_IMPORT_QUARK gst_v4l2_buffer_pool_import_quark ()
+
+
/*
* GstV4l2BufferPool:
*/
}
static GstFlowReturn
+gst_v4l2_buffer_pool_copy_buffer (GstV4l2BufferPool * pool, GstBuffer * dest,
+ GstBuffer * src)
+{
+ const GstVideoFormatInfo *finfo = pool->obj->info.finfo;
+
+ GST_LOG_OBJECT (pool, "copying buffer");
+
+ if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
+ finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
+ GstVideoFrame src_frame, dest_frame;
+
+ GST_DEBUG_OBJECT (pool, "copy video frame");
+
+ /* we have raw video, use videoframe copy to get strides right */
+ if (!gst_video_frame_map (&src_frame, &pool->obj->info, src, GST_MAP_READ))
+ goto invalid_buffer;
+
+ if (!gst_video_frame_map (&dest_frame, &pool->obj->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 {
+ GstMapInfo map;
+
+ GST_DEBUG_OBJECT (pool, "copy raw bytes");
+
+ if (!gst_buffer_map (src, &map, GST_MAP_READ))
+ goto invalid_buffer;
+
+ gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
+
+ gst_buffer_unmap (src, &map);
+ gst_buffer_resize (dest, 0, gst_buffer_get_size (src));
+ }
+
+ GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, pool, "slow copy into buffer %p",
+ dest);
+
+ return GST_FLOW_OK;
+
+invalid_buffer:
+ {
+ GST_ERROR_OBJECT (pool, "could not map buffer");
+ return GST_FLOW_ERROR;
+ }
+}
+
+struct UserPtrData
+{
+ GstBuffer *buffer;
+ gboolean is_frame;
+ GstVideoFrame frame;
+ GstMapInfo map;
+};
+
+static GQuark
+gst_v4l2_buffer_pool_import_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (quark == 0)
+ quark = g_quark_from_string ("GstV4l2BufferPoolUsePtrData");
+
+ return quark;
+}
+
+static void
+_unmap_userptr_frame (struct UserPtrData *data)
+{
+ if (data->is_frame)
+ gst_video_frame_unmap (&data->frame);
+ else
+ gst_buffer_unmap (data->buffer, &data->map);
+
+ if (data->buffer)
+ gst_buffer_unref (data->buffer);
+
+ g_slice_free (struct UserPtrData, data);
+}
+
+static GstFlowReturn
+gst_v4l2_buffer_pool_import_userptr (GstV4l2BufferPool * pool,
+ GstBuffer * dest, GstBuffer * src)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstV4l2MemoryGroup *group = NULL;
+ GstMapFlags flags;
+ const GstVideoFormatInfo *finfo = pool->obj->info.finfo;
+ struct UserPtrData *data = NULL;
+
+ GST_LOG_OBJECT (pool, "importing userptr");
+
+ /* get the group */
+ if (!gst_v4l2_is_buffer_valid (dest, &group))
+ goto not_our_buffer;
+
+ /* ensure we have a src */
+ if (src == NULL) {
+ g_return_val_if_fail (pool->other_pool != NULL, GST_FLOW_ERROR);
+ ret = gst_buffer_pool_acquire_buffer (pool->other_pool, &src, NULL);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type))
+ flags = GST_MAP_READ;
+ else
+ flags = GST_MAP_WRITE;
+
+ data = g_slice_new0 (struct UserPtrData);
+
+ if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
+ finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
+ data->is_frame = TRUE;
+
+ if (!gst_video_frame_map (&data->frame, &pool->obj->info, src, flags))
+ goto invalid_buffer;
+
+ if (!gst_v4l2_allocator_import_userptr (pool->vallocator, group,
+ data->frame.info.size, finfo->n_planes, data->frame.data,
+ data->frame.info.offset))
+ goto import_failed;
+ } else {
+ gsize offset[1] = { 0 };
+ gpointer ptr[1];
+
+ data->is_frame = FALSE;
+
+ if (!gst_buffer_map (src, &data->map, flags))
+ goto invalid_buffer;
+
+ ptr[0] = data->map.data;
+
+ if (!gst_v4l2_allocator_import_userptr (pool->vallocator, group,
+ data->map.size, 1, ptr, offset))
+ goto import_failed;
+ }
+
+ data->buffer = gst_buffer_ref (src);
+
+ gst_mini_object_set_qdata (GST_MINI_OBJECT (dest), GST_V4L2_IMPORT_QUARK,
+ data, (GDestroyNotify) _unmap_userptr_frame);
+
+done:
+ return ret;
+
+not_our_buffer:
+ {
+ GST_ERROR_OBJECT (pool, "destination buffer invalid or not from our pool");
+ return GST_FLOW_ERROR;
+ }
+invalid_buffer:
+ {
+ GST_ERROR_OBJECT (pool, "could not map buffer");
+ g_slice_free (struct UserPtrData, data);
+ return GST_FLOW_ERROR;
+ }
+import_failed:
+ {
+ GST_ERROR_OBJECT (pool, "failed to import data");
+ _unmap_userptr_frame (data);
+ return GST_FLOW_ERROR;
+ }
+}
+
+static GstFlowReturn
+gst_v4l2_buffer_pool_import_dmabuf (GstV4l2BufferPool * pool,
+ GstBuffer * dest, GstBuffer * src)
+{
+ GstV4l2MemoryGroup *group = NULL;
+ GstMemory *dma_mem[GST_VIDEO_MAX_PLANES] = { 0 };
+ guint n_mem = gst_buffer_n_memory (src);
+ gint i;
+
+ GST_LOG_OBJECT (pool, "importing dmabuf");
+
+ if (!gst_v4l2_is_buffer_valid (dest, &group))
+ goto not_our_buffer;
+
+ if (n_mem > GST_VIDEO_MAX_PLANES)
+ goto too_many_mems;
+
+ for (i = 0; i < n_mem; i++)
+ dma_mem[i] = gst_buffer_peek_memory (src, i);
+
+ if (!gst_v4l2_allocator_import_dmabuf (pool->vallocator, group, n_mem,
+ dma_mem))
+ goto import_failed;
+
+ gst_mini_object_set_qdata (GST_MINI_OBJECT (dest), GST_V4L2_IMPORT_QUARK,
+ gst_buffer_ref (src), (GDestroyNotify) gst_buffer_unref);
+
+ return GST_FLOW_OK;
+
+not_our_buffer:
+ {
+ GST_ERROR_OBJECT (pool, "destination buffer invalid or not from our pool");
+ return GST_FLOW_ERROR;
+ }
+too_many_mems:
+ {
+ GST_ERROR_OBJECT (pool, "could not map buffer");
+ return GST_FLOW_ERROR;
+ }
+import_failed:
+ {
+ GST_ERROR_OBJECT (pool, "failed to import dmabuf");
+ return GST_FLOW_ERROR;
+ }
+}
+
+static GstFlowReturn
+gst_v4l2_buffer_pool_prepare_buffer (GstV4l2BufferPool * pool,
+ GstBuffer * dest, GstBuffer * src)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (src == NULL) {
+ if (pool->other_pool == NULL) {
+ GST_ERROR_OBJECT (pool, "can't prepare buffer, source buffer missing");
+ return GST_FLOW_ERROR;
+ }
+
+ ret = gst_buffer_pool_acquire_buffer (pool->other_pool, &src, NULL);
+ if (ret != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (pool, "failed to acquire buffer from downstream pool");
+ goto done;
+ }
+ }
+
+ switch (pool->obj->mode) {
+ case GST_V4L2_IO_MMAP:
+ case GST_V4L2_IO_DMABUF:
+ ret = gst_v4l2_buffer_pool_copy_buffer (pool, dest, src);
+ break;
+ case GST_V4L2_IO_USERPTR:
+ ret = gst_v4l2_buffer_pool_import_userptr (pool, dest, src);
+ break;
+ case GST_V4L2_IO_DMABUF_IMPORT:
+ ret = gst_v4l2_buffer_pool_import_dmabuf (pool, dest, src);
+ break;
+ default:
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static GstFlowReturn
gst_v4l2_buffer_pool_alloc_buffer (GstBufferPool * bpool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params)
{
pool->allocator);
break;
case GST_V4L2_IO_USERPTR:
+ group = gst_v4l2_allocator_alloc_userptr (pool->vallocator);
+ break;
case GST_V4L2_IO_DMABUF_IMPORT:
+ group = gst_v4l2_allocator_alloc_dmabufin (pool->vallocator);
+ break;
default:
newbuf = NULL;
g_assert_not_reached ();
/* ERRORS */
allocation_failed:
{
- GST_WARNING ("Failed to allocated buffer");
- return GST_FLOW_EOS;
+ GST_ERROR_OBJECT (pool, "failed to allocate buffer");
+ return FALSE;
}
}
can_allocate =
GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, USERPTR);
break;
+ case GST_V4L2_IO_DMABUF_IMPORT:
+ can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, DMABUF);
+ break;
case GST_V4L2_IO_RW:
pool->allocator = g_object_ref (allocator);
pool->params = params;
/* No need to change the configuration */
goto done;
break;
- case GST_V4L2_IO_DMABUF_IMPORT:
default:
g_assert_not_reached ();
break;
break;
}
case GST_V4L2_IO_USERPTR:
+ {
+ guint count;
+
+ if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, USERPTR)) {
+ num_buffers = min_buffers;
+ } else {
+ num_buffers = max_buffers;
+ }
+
+ GST_DEBUG_OBJECT (pool, "requesting %d USERPTR buffers", num_buffers);
+
+ count = gst_v4l2_allocator_start (pool->vallocator, num_buffers,
+ V4L2_MEMORY_USERPTR);
+
+ /* There is no rational to not get what we asked */
+ if (count < num_buffers) {
+ num_buffers = count;
+ goto no_buffers;
+ }
+
+ break;
+ }
case GST_V4L2_IO_DMABUF_IMPORT:
+ {
+ guint count;
+
+ if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, DMABUF)) {
+ num_buffers = min_buffers;
+ } else {
+ num_buffers = max_buffers;
+ }
+
+ GST_DEBUG_OBJECT (pool, "requesting %d DMABUF buffers", num_buffers);
+
+ count = gst_v4l2_allocator_start (pool->vallocator, num_buffers,
+ V4L2_MEMORY_DMABUF);
+
+ /* There is no rational to not get what we asked */
+ if (count < num_buffers) {
+ num_buffers = count;
+ goto no_buffers;
+ }
+
+ break;
+ }
default:
num_buffers = 0;
copy_threshold = 0;
GST_BUFFER_POOL_CLASS (parent_class)->set_config (bpool, config);
gst_structure_free (config);
+ if (pool->other_pool)
+ if (!gst_buffer_pool_set_active (pool->other_pool, TRUE))
+ goto other_pool_failed;
+
/* now, allocate the buffers: */
if (!GST_BUFFER_POOL_CLASS (parent_class)->start (bpool))
goto start_failed;
GST_ERROR_OBJECT (pool, "failed to start streaming");
return FALSE;
}
+other_pool_failed:
+ {
+ GST_ERROR_OBJECT (pool, "failed to active the other pool %"
+ GST_PTR_FORMAT, pool->other_pool);
+ return FALSE;
+ }
}
pool->buffers[i] = NULL;
pool->num_queued--;
+ /* Remove qdata, this will unmap any map data in userptr */
+ gst_mini_object_set_qdata (GST_MINI_OBJECT (buffer),
+ GST_V4L2_IMPORT_QUARK, NULL, NULL);
+
if (V4L2_TYPE_IS_OUTPUT (obj->type))
gst_buffer_unref (buffer);
else
pool->streaming = FALSE;
+ if (pool->other_pool)
+ gst_buffer_pool_set_active (pool->other_pool, FALSE);
+
return TRUE;
/* ERRORS */
if (GST_BUFFER_POOL_IS_FLUSHING (bpool))
goto flushing;
+ /* If this is being called to resurect a lost buffer */
+ if (params && params->flags & GST_V4L2_POOL_ACQUIRE_FLAG_RESURECT) {
+ ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer,
+ params);
+ goto done;
+ }
+
switch (obj->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
/* 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_DMABUF:
case GST_V4L2_IO_MMAP:
- /* If this is being called to resurect a lost buffer */
- if (params && params->flags & GST_V4L2_POOL_ACQUIRE_FLAG_RESURECT) {
- ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
- buffer, params);
- break;
- }
-
+ {
/* just dequeue a buffer, we basically use the queue of v4l2 as the
* storage for our buffers. This function does poll first so we can
* interrupt it fine. */
*buffer = copy;
}
break;
-
+ }
case GST_V4L2_IO_USERPTR:
+ {
+ struct UserPtrData *data;
+
+ /* dequeue filled userptr */
+ ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto done;
+
+ data = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buffer),
+ GST_V4L2_IMPORT_QUARK);
+
+ /* and requeue so that we can continue capturing */
+ gst_v4l2_buffer_pool_prepare_buffer (pool, *buffer, NULL);
+ ret = gst_v4l2_buffer_pool_qbuf (pool, *buffer);
+ *buffer = data->buffer;
+
+ data->buffer = NULL;
+ _unmap_userptr_frame (data);
+ break;
+ }
case GST_V4L2_IO_DMABUF_IMPORT:
+ {
+ GstBuffer *tmp;
+
+ /* dequeue filled dmabuf */
+ ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto done;
+
+ tmp = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buffer),
+ GST_V4L2_IMPORT_QUARK);
+
+ /* and requeue so that we can continue capturing */
+ gst_v4l2_buffer_pool_prepare_buffer (pool, *buffer, NULL);
+ ret = gst_v4l2_buffer_pool_qbuf (pool, *buffer);
+ *buffer = tmp;
+ break;
+ }
default:
ret = GST_FLOW_ERROR;
g_assert_not_reached ();
}
break;
+
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
/* playback, This function should return an empty buffer */
case GST_V4L2_IO_MMAP:
case GST_V4L2_IO_DMABUF:
+ case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF_IMPORT:
/* get a free unqueued buffer */
ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool,
buffer, params);
break;
- case GST_V4L2_IO_USERPTR:
- case GST_V4L2_IO_DMABUF_IMPORT:
default:
ret = GST_FLOW_ERROR;
g_assert_not_reached ();
case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
+ case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF_IMPORT:
{
if (gst_v4l2_is_buffer_valid (buffer, NULL)) {
/* queue back in the device */
+ if (pool->other_pool)
+ gst_v4l2_buffer_pool_prepare_buffer (pool, buffer, NULL);
gst_v4l2_buffer_pool_qbuf (pool, buffer);
} else {
/* Simply release invalide/modified buffer, the allocator will
}
break;
}
- case GST_V4L2_IO_USERPTR:
- case GST_V4L2_IO_DMABUF_IMPORT:
default:
g_assert_not_reached ();
break;
case GST_V4L2_IO_MMAP:
case GST_V4L2_IO_DMABUF:
+ case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF_IMPORT:
{
GstV4l2MemoryGroup *group;
guint index;
break;
}
- case GST_V4L2_IO_USERPTR:
- case GST_V4L2_IO_DMABUF_IMPORT:
default:
g_assert_not_reached ();
break;
if (pool->video_fd >= 0)
v4l2_close (pool->video_fd);
+
if (pool->vallocator)
gst_object_unref (pool->vallocator);
+
if (pool->allocator)
gst_object_unref (pool->allocator);
+ if (pool->other_pool)
+ gst_object_unref (pool->other_pool);
+
/* FIXME Is this required to keep around ? */
gst_object_unref (pool->obj->element);
goto eos;
}
- if (!gst_v4l2_object_copy (obj, buf, tmp))
+ if (gst_v4l2_buffer_pool_copy_buffer (pool, buf, tmp) != GST_FLOW_OK)
goto copy_failed;
/* an queue the buffer again after the copy */
case GST_V4L2_IO_USERPTR:
case GST_V4L2_IO_DMABUF_IMPORT:
+ {
+ /* Nothing to do buffer is from the other pool */
+ break;
+ }
+
default:
g_assert_not_reached ();
break;
/* FIXME, do write() */
GST_WARNING_OBJECT (pool, "implement write()");
break;
+
+ case GST_V4L2_IO_USERPTR:
+ case GST_V4L2_IO_DMABUF_IMPORT:
case GST_V4L2_IO_DMABUF:
case GST_V4L2_IO_MMAP:
{
if (ret != GST_FLOW_OK)
goto acquire_failed;
- /* copy into it and queue */
- if (!gst_v4l2_object_copy (obj, to_queue, buf))
- goto copy_failed;
+ ret = gst_v4l2_buffer_pool_prepare_buffer (pool, to_queue, buf);
+ if (ret != GST_FLOW_OK)
+ goto prepare_failed;
}
if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK)
gst_buffer_unref (to_queue);
break;
}
-
- case GST_V4L2_IO_USERPTR:
- case GST_V4L2_IO_DMABUF_IMPORT:
default:
g_assert_not_reached ();
break;
gst_flow_get_name (ret));
return ret;
}
-copy_failed:
+prepare_failed:
{
- GST_ERROR_OBJECT (obj->element, "failed to copy data");
- return GST_FLOW_ERROR;
+ GST_ERROR_OBJECT (obj->element, "failed to prepare data");
+ return ret;
}
start_failed:
{
GST_ERROR_OBJECT (obj->element, "failed to start streaming");
return GST_FLOW_ERROR;
}
+copy_failed:
+ {
+ GST_ERROR_OBJECT (obj->element, "failed to copy buffer");
+ return GST_FLOW_ERROR;
+ }
eos:
{
GST_DEBUG_OBJECT (obj->element, "end of stream reached");
return FALSE;
}
}
+
+void
+gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool,
+ GstBufferPool * other_pool)
+{
+ g_return_if_fail (!gst_buffer_pool_is_active (GST_BUFFER_POOL (pool)));
+
+ if (pool->other_pool)
+ gst_object_unref (pool->other_pool);
+ pool->other_pool = gst_object_ref (other_pool);
+}
GstV4l2Allocator *vallocator;
GstAllocator *allocator;
GstAllocationParams params;
+ GstBufferPool *other_pool;
guint size;
gboolean add_videometa;
GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * bpool, GstBuffer * buf);
-gboolean gst_v4l2_buffer_pool_flush (GstV4l2BufferPool * pool);
+gboolean gst_v4l2_buffer_pool_flush (GstV4l2BufferPool * pool);
+
+void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool,
+ GstBufferPool * other_pool);
G_END_DECLS
#include <gst/video/video.h>
GST_DEBUG_CATEGORY_EXTERN (v4l2_debug);
-GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
#define GST_CAT_DEFAULT v4l2_debug
#define DEFAULT_PROP_DEVICE_NAME NULL
return TRUE;
}
-gboolean
-gst_v4l2_object_copy (GstV4l2Object * v4l2object, GstBuffer * dest,
- GstBuffer * src)
-{
- const GstVideoFormatInfo *finfo = v4l2object->info.finfo;
-
- if (finfo && (finfo->format != GST_VIDEO_FORMAT_UNKNOWN &&
- finfo->format != GST_VIDEO_FORMAT_ENCODED)) {
- GstVideoFrame src_frame, dest_frame;
-
- GST_DEBUG_OBJECT (v4l2object->element, "copy video frame");
-
- /* FIXME This won't work if cropping apply */
-
- /* 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 {
- GstMapInfo map;
-
- GST_DEBUG_OBJECT (v4l2object->element, "copy raw bytes");
- gst_buffer_map (src, &map, GST_MAP_READ);
- gst_buffer_fill (dest, 0, map.data, gst_buffer_get_size (src));
- gst_buffer_unmap (src, &map);
- gst_buffer_resize (dest, 0, gst_buffer_get_size (src));
- }
- 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;
- }
-}
-
GstCaps *
gst_v4l2_object_get_caps (GstV4l2Object * v4l2object, GstCaps * filter)
{
gst_v4l2_object_decide_allocation (GstV4l2Object * obj, GstQuery * query)
{
GstCaps *caps;
- GstBufferPool *pool;
+ GstBufferPool *pool, *other_pool = NULL;
GstStructure *config;
guint size, min, max, extra = 0;
gboolean update;
gboolean has_video_meta, has_crop_meta;
- gboolean can_use_own_pool;
+ gboolean can_share_own_pool, pushing_from_our_pool = FALSE;
struct v4l2_control ctl = { 0, };
GST_DEBUG_OBJECT (obj->element, "decide allocation");
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 */
+ * buffer extra to capture while the other buffers are downstream */
min += 1;
} else {
min = 2;
gst_query_find_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE,
NULL);
- can_use_own_pool = ((has_crop_meta || !obj->need_crop_meta) &&
+ can_share_own_pool = ((has_crop_meta || !obj->need_crop_meta) &&
(has_video_meta || !obj->need_video_meta));
/* select a pool */
* other size than what the hardware gives us but for downstream pools
* we can try */
size = MAX (size, obj->sizeimage);
- } else if (can_use_own_pool) {
+ } else if (can_share_own_pool) {
/* no downstream pool, use our own then */
GST_DEBUG_OBJECT (obj->element,
"read/write mode: no downstream pool, using our own");
pool = gst_object_ref (obj->pool);
size = obj->sizeimage;
+ pushing_from_our_pool = TRUE;
}
break;
- case GST_V4L2_IO_MMAP:
- case GST_V4L2_IO_DMABUF:
- /* FIXME in these case we actually prefer/need a downstream pool */
+
case GST_V4L2_IO_USERPTR:
case GST_V4L2_IO_DMABUF_IMPORT:
+ /* in importing mode, prefer our own pool, and pass the other pool to
+ * our own, so it can serve itself */
+ if (pool == NULL)
+ goto no_downstream_pool;
+ gst_v4l2_buffer_pool_set_other_pool (GST_V4L2_BUFFER_POOL (obj->pool),
+ pool);
+ other_pool = pool;
+ gst_object_unref (pool);
+ pool = gst_object_ref (obj->pool);
+ size = obj->sizeimage;
+ break;
+
+ case GST_V4L2_IO_MMAP:
+ case GST_V4L2_IO_DMABUF:
/* in streaming mode, prefer our own pool */
/* Check if we can use it ... */
- if (can_use_own_pool) {
+ if (can_share_own_pool) {
if (pool)
gst_object_unref (pool);
pool = gst_object_ref (obj->pool);
size = obj->sizeimage;
GST_DEBUG_OBJECT (obj->element,
"streaming mode: using our own pool %" GST_PTR_FORMAT, pool);
+ pushing_from_our_pool = TRUE;
} else if (pool) {
GST_DEBUG_OBJECT (obj->element,
"streaming mode: copying to downstream pool %" GST_PTR_FORMAT,
GST_V4L2_BUFFER_POOL_OPTION_CROP_META);
}
- gst_buffer_pool_config_set_params (config, caps, size, min + extra, 0);
+ /* If pushing from our own pool, configure it with queried minimum,
+ * otherwise use the minimum required */
+ if (pushing_from_our_pool)
+ extra += min;
+ else
+ extra += GST_V4L2_MIN_BUFFERS;
+
+ gst_buffer_pool_config_set_params (config, caps, size, extra, 0);
GST_DEBUG_OBJECT (pool, "setting config %" GST_PTR_FORMAT, config);
}
setup_other_pool:
+
/* Now configure the other pool if different */
- if (pool && obj->pool != pool) {
+ if (obj->pool != pool)
+ other_pool = pool;
+
+ if (other_pool) {
if (gst_buffer_pool_is_active (obj->pool))
goto done;
gst_object_unref (pool);
return FALSE;
}
+no_downstream_pool:
+ {
+ GST_ELEMENT_ERROR (obj->element, RESOURCE, SETTINGS,
+ (_("No downstream pool to import from.")),
+ ("When importing DMABUF or USERPTR, we need a pool to import from"));
+ return FALSE;
+ }
}
gboolean
gboolean gst_v4l2_object_stop (GstV4l2Object * v4l2object);
-
-gboolean gst_v4l2_object_copy (GstV4l2Object * v4l2object,
- GstBuffer * dest, GstBuffer * src);
-
GstCaps * gst_v4l2_object_get_caps (GstV4l2Object * v4l2object,
GstCaps * filter);