2 * Copyright (C) 2014 Collabora Ltd.
3 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 # define _GNU_SOURCE /* O_CLOEXEC */
28 #include "ext/videodev2.h"
30 #include "gstv4l2object.h"
31 #include "gstv4l2allocator.h"
33 #include <gst/allocators/gstdmabuf.h>
38 #include <sys/types.h>
42 #define GST_V4L2_MEMORY_TYPE "V4l2Memory"
44 #define gst_v4l2_allocator_parent_class parent_class
45 G_DEFINE_TYPE (GstV4l2Allocator, gst_v4l2_allocator, GST_TYPE_ALLOCATOR);
47 GST_DEBUG_CATEGORY_STATIC (v4l2allocator_debug);
48 #define GST_CAT_DEFAULT v4l2allocator_debug
50 #define UNSET_QUEUED(buffer) \
51 ((buffer).flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
53 #define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED)
55 #define IS_QUEUED(buffer) \
56 ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
64 static guint gst_v4l2_allocator_signals[LAST_SIGNAL] = { 0 };
66 static void gst_v4l2_allocator_release (GstV4l2Allocator * allocator,
70 memory_type_to_str (guint32 memory)
73 case V4L2_MEMORY_MMAP:
75 case V4L2_MEMORY_USERPTR:
77 case V4L2_MEMORY_DMABUF:
84 /*************************************/
85 /* GstV4lMemory implementation */
86 /*************************************/
89 _v4l2mem_map (GstV4l2Memory * mem, gsize maxsize, GstMapFlags flags)
93 switch (mem->group->buffer.memory) {
94 case V4L2_MEMORY_MMAP:
95 case V4L2_MEMORY_USERPTR:
98 case V4L2_MEMORY_DMABUF:
99 /* v4l2 dmabuf memory are not shared with downstream */
100 g_assert_not_reached ();
103 GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
110 _v4l2mem_unmap (GstV4l2Memory * mem)
112 gboolean ret = FALSE;
114 switch (mem->group->buffer.memory) {
115 case V4L2_MEMORY_MMAP:
116 case V4L2_MEMORY_USERPTR:
119 case V4L2_MEMORY_DMABUF:
120 /* v4l2 dmabuf memory are not share with downstream */
121 g_assert_not_reached ();
124 GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
131 _v4l2mem_dispose (GstV4l2Memory * mem)
133 GstV4l2Allocator *allocator = (GstV4l2Allocator *) mem->mem.allocator;
134 GstV4l2MemoryGroup *group = mem->group;
137 if (group->mem[mem->plane]) {
138 /* We may have a dmabuf, replace it with returned original memory */
139 group->mem[mem->plane] = gst_memory_ref ((GstMemory *) mem);
140 gst_v4l2_allocator_release (allocator, mem);
143 gst_object_ref (allocator);
150 static inline GstV4l2Memory *
151 _v4l2mem_new (GstMemoryFlags flags, GstAllocator * allocator,
152 GstMemory * parent, gsize maxsize, gsize align, gsize offset, gsize size,
153 gint plane, gpointer data, int dmafd, GstV4l2MemoryGroup * group)
157 mem = g_slice_new0 (GstV4l2Memory);
158 gst_memory_init (GST_MEMORY_CAST (mem),
159 flags, allocator, parent, maxsize, align, offset, size);
162 mem->mem.mini_object.dispose =
163 (GstMiniObjectDisposeFunction) _v4l2mem_dispose;
173 static GstV4l2Memory *
174 _v4l2mem_share (GstV4l2Memory * mem, gssize offset, gsize size)
179 /* find the real parent */
180 if ((parent = mem->mem.parent) == NULL)
181 parent = (GstMemory *) mem;
184 size = mem->mem.size - offset;
186 /* the shared memory is always readonly */
187 sub = _v4l2mem_new (GST_MINI_OBJECT_FLAGS (parent) |
188 GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->mem.allocator, parent,
189 mem->mem.maxsize, mem->mem.align, offset, size, mem->plane, mem->data,
196 _v4l2mem_is_span (GstV4l2Memory * mem1, GstV4l2Memory * mem2, gsize * offset)
199 *offset = mem1->mem.offset - mem1->mem.parent->offset;
201 /* and memory is contiguous */
202 return mem1->mem.offset + mem1->mem.size == mem2->mem.offset;
206 gst_is_v4l2_memory (GstMemory * mem)
208 return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE);
212 gst_v4l2_memory_quark (void)
214 static GQuark quark = 0;
217 quark = g_quark_from_string ("GstV4l2Memory");
223 /*************************************/
224 /* GstV4l2MemoryGroup implementation */
225 /*************************************/
228 gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
232 for (i = 0; i < group->n_mem; i++) {
233 GstMemory *mem = group->mem[i];
234 group->mem[i] = NULL;
236 gst_memory_unref (mem);
239 g_slice_free (GstV4l2MemoryGroup, group);
242 static GstV4l2MemoryGroup *
243 gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
245 GstV4l2Object *obj = allocator->obj;
246 guint32 memory = allocator->memory;
247 struct v4l2_format *format = &obj->format;
248 GstV4l2MemoryGroup *group;
249 gsize img_size, buf_size;
251 group = g_slice_new0 (GstV4l2MemoryGroup);
253 group->buffer.type = format->type;
254 group->buffer.index = index;
255 group->buffer.memory = memory;
257 if (V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
258 group->n_mem = group->buffer.length = format->fmt.pix_mp.num_planes;
259 group->buffer.m.planes = group->planes;
264 if (obj->ioctl (obj->video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0)
265 goto querybuf_failed;
267 if (group->buffer.index != index) {
268 GST_ERROR_OBJECT (allocator, "Buffer index returned by VIDIOC_QUERYBUF "
269 "didn't match, this indicate the presence of a bug in your driver or "
271 g_slice_free (GstV4l2MemoryGroup, group);
275 /* Check that provided size matches the format we have negotiation. Failing
276 * there usually means a driver of libv4l bug. */
277 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
280 for (i = 0; i < group->n_mem; i++) {
281 img_size = obj->format.fmt.pix_mp.plane_fmt[i].sizeimage;
282 buf_size = group->planes[i].length;
283 if (buf_size < img_size)
284 goto buffer_too_short;
287 img_size = obj->format.fmt.pix.sizeimage;
288 buf_size = group->buffer.length;
289 if (buf_size < img_size)
290 goto buffer_too_short;
293 /* We save non planar buffer information into the multi-planar plane array
294 * to avoid duplicating the code later */
295 if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
296 group->planes[0].bytesused = group->buffer.bytesused;
297 group->planes[0].length = group->buffer.length;
298 group->planes[0].data_offset = 0;
299 g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
300 memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
303 GST_LOG_OBJECT (allocator, "Got %s buffer", memory_type_to_str (memory));
304 GST_LOG_OBJECT (allocator, " index: %u", group->buffer.index);
305 GST_LOG_OBJECT (allocator, " type: %d", group->buffer.type);
306 GST_LOG_OBJECT (allocator, " flags: %08x", group->buffer.flags);
307 GST_LOG_OBJECT (allocator, " field: %d", group->buffer.field);
308 GST_LOG_OBJECT (allocator, " memory: %d", group->buffer.memory);
309 GST_LOG_OBJECT (allocator, " planes: %d", group->n_mem);
311 #ifndef GST_DISABLE_GST_DEBUG
312 if (memory == V4L2_MEMORY_MMAP) {
314 for (i = 0; i < group->n_mem; i++) {
315 GST_LOG_OBJECT (allocator,
316 " [%u] bytesused: %u, length: %u, offset: %u", i,
317 group->planes[i].bytesused, group->planes[i].length,
318 group->planes[i].data_offset);
319 GST_LOG_OBJECT (allocator, " [%u] MMAP offset: %u", i,
320 group->planes[i].m.mem_offset);
329 GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno));
334 GST_ERROR ("buffer size %" G_GSIZE_FORMAT
335 " is smaller then negotiated size %" G_GSIZE_FORMAT
336 ", this is usually the result of a bug in the v4l2 driver or libv4l.",
341 gst_v4l2_memory_group_free (group);
346 /*************************************/
347 /* GstV4lAllocator implementation */
348 /*************************************/
351 gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
353 GstV4l2MemoryGroup *group = mem->group;
355 GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
356 mem->plane, group->buffer.index);
358 switch (allocator->memory) {
359 case V4L2_MEMORY_DMABUF:
363 case V4L2_MEMORY_USERPTR:
370 /* When all memory are back, put the group back in the free queue */
371 if (g_atomic_int_dec_and_test (&group->mems_allocated)) {
372 GST_LOG_OBJECT (allocator, "buffer %u released", group->buffer.index);
373 gst_atomic_queue_push (allocator->free_queue, group);
374 g_signal_emit (allocator, gst_v4l2_allocator_signals[GROUP_RELEASED], 0);
377 /* Keep last, allocator may be freed after this call */
378 g_object_unref (allocator);
382 gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
384 GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator;
385 GstV4l2Object *obj = allocator->obj;
386 GstV4l2Memory *mem = (GstV4l2Memory *) gmem;
387 GstV4l2MemoryGroup *group = mem->group;
389 /* Only free unparented memory */
390 if (mem->mem.parent == NULL) {
391 GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u",
392 mem->plane, group->buffer.index);
394 if (allocator->memory == V4L2_MEMORY_MMAP) {
396 obj->munmap (mem->data, group->planes[mem->plane].length);
399 /* This apply for both mmap with expbuf, and dmabuf imported memory */
404 g_slice_free (GstV4l2Memory, mem);
408 gst_v4l2_allocator_dispose (GObject * obj)
410 GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
413 GST_LOG_OBJECT (obj, "called");
415 for (i = 0; i < allocator->count; i++) {
416 GstV4l2MemoryGroup *group = allocator->groups[i];
417 allocator->groups[i] = NULL;
419 gst_v4l2_memory_group_free (group);
422 G_OBJECT_CLASS (parent_class)->dispose (obj);
426 gst_v4l2_allocator_finalize (GObject * obj)
428 GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
430 GST_LOG_OBJECT (obj, "called");
432 gst_atomic_queue_unref (allocator->free_queue);
433 gst_object_unref (allocator->obj->element);
435 G_OBJECT_CLASS (parent_class)->finalize (obj);
439 gst_v4l2_allocator_class_init (GstV4l2AllocatorClass * klass)
441 GObjectClass *object_class;
442 GstAllocatorClass *allocator_class;
444 allocator_class = (GstAllocatorClass *) klass;
445 object_class = (GObjectClass *) klass;
447 allocator_class->alloc = NULL;
448 allocator_class->free = gst_v4l2_allocator_free;
450 object_class->dispose = gst_v4l2_allocator_dispose;
451 object_class->finalize = gst_v4l2_allocator_finalize;
453 gst_v4l2_allocator_signals[GROUP_RELEASED] = g_signal_new ("group-released",
454 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
457 GST_DEBUG_CATEGORY_INIT (v4l2allocator_debug, "v4l2allocator", 0,
462 gst_v4l2_allocator_init (GstV4l2Allocator * allocator)
464 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
466 alloc->mem_type = GST_V4L2_MEMORY_TYPE;
467 alloc->mem_map = (GstMemoryMapFunction) _v4l2mem_map;
468 alloc->mem_unmap = (GstMemoryUnmapFunction) _v4l2mem_unmap;
469 alloc->mem_share = (GstMemoryShareFunction) _v4l2mem_share;
470 alloc->mem_is_span = (GstMemoryIsSpanFunction) _v4l2mem_is_span;
471 /* Use the default, fallback copy function */
473 allocator->free_queue = gst_atomic_queue_new (VIDEO_MAX_FRAME);
475 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
478 #define GST_V4L2_ALLOCATOR_PROBE(obj,type) \
479 gst_v4l2_allocator_probe ((obj), V4L2_MEMORY_ ## type, \
480 GST_V4L2_ALLOCATOR_FLAG_ ## type ## _REQBUFS, \
481 GST_V4L2_ALLOCATOR_FLAG_ ## type ## _CREATE_BUFS)
483 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
484 guint32 breq_flag, guint32 bcreate_flag)
486 GstV4l2Object *obj = allocator->obj;
487 struct v4l2_requestbuffers breq = { 0 };
490 breq.type = obj->type;
492 breq.memory = memory;
494 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) == 0) {
495 struct v4l2_create_buffers bcreate = { 0 };
499 bcreate.memory = memory;
500 bcreate.format = obj->format;
502 if ((obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0))
503 flags |= bcreate_flag;
509 static GstV4l2MemoryGroup *
510 gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
512 GstV4l2Object *obj = allocator->obj;
513 struct v4l2_create_buffers bcreate = { 0 };
514 GstV4l2MemoryGroup *group = NULL;
516 GST_OBJECT_LOCK (allocator);
518 if (!g_atomic_int_get (&allocator->active))
521 bcreate.memory = allocator->memory;
522 bcreate.format = obj->format;
525 if (!allocator->can_allocate)
528 if (obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0)
529 goto create_bufs_failed;
531 if (allocator->groups[bcreate.index] != NULL)
532 goto create_bufs_bug;
534 group = gst_v4l2_memory_group_new (allocator, bcreate.index);
537 allocator->groups[bcreate.index] = group;
542 GST_OBJECT_UNLOCK (allocator);
547 GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s",
553 GST_ERROR_OBJECT (allocator, "created buffer has already used buffer "
554 "index %i, this means there is an bug in your driver or libv4l2",
560 static GstV4l2MemoryGroup *
561 gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator)
563 GstV4l2MemoryGroup *group;
565 if (!g_atomic_int_get (&allocator->active))
568 group = gst_atomic_queue_pop (allocator->free_queue);
571 if (allocator->can_allocate) {
572 group = gst_v4l2_allocator_create_buf (allocator);
574 /* Don't hammer on CREATE_BUFS */
576 allocator->can_allocate = FALSE;
584 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
585 GstV4l2MemoryGroup * group)
588 for (i = 0; i < group->n_mem; i++) {
589 group->mem[i]->maxsize = group->planes[i].length;
590 group->mem[i]->offset = 0;
591 group->mem[i]->size = group->planes[i].length;
596 _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
598 if (group->mems_allocated > 0) {
600 /* If one or more mmap worked, we need to unref the memory, otherwise
601 * they will keep a ref on the allocator and leak it. This will put back
602 * the group into the free_queue */
603 for (i = 0; i < group->n_mem; i++)
604 gst_memory_unref (group->mem[i]);
606 /* Otherwise, group has to be on free queue for _stop() to work */
607 gst_atomic_queue_push (allocator->free_queue, group);
614 gst_v4l2_allocator_new (GstObject * parent, GstV4l2Object * v4l2object)
616 GstV4l2Allocator *allocator;
618 gchar *name, *parent_name;
620 parent_name = gst_object_get_name (parent);
621 name = g_strconcat (parent_name, ":allocator", NULL);
622 g_free (parent_name);
624 allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL);
625 gst_object_ref_sink (allocator);
628 /* Save everything */
629 allocator->obj = v4l2object;
631 /* Keep a ref on the elemnt so obj does not disapear */
632 gst_object_ref (allocator->obj->element);
634 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP);
635 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR);
636 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF);
640 /* Drivers not ported from videobuf to videbuf2 don't allow freeing buffers
641 * using REQBUFS(0). This is a workaround to still support these drivers,
642 * which are known to have MMAP support. */
643 GST_WARNING_OBJECT (allocator, "Could not probe supported memory type, "
644 "assuming MMAP is supported, this is expected for older drivers not "
645 " yet ported to videobuf2 framework");
646 flags = GST_V4L2_ALLOCATOR_FLAG_MMAP_REQBUFS;
649 GST_OBJECT_FLAG_SET (allocator, flags);
655 gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
658 GstV4l2Object *obj = allocator->obj;
659 struct v4l2_requestbuffers breq = { count, obj->type, memory };
660 gboolean can_allocate;
663 g_return_val_if_fail (count != 0, 0);
665 GST_OBJECT_LOCK (allocator);
667 if (g_atomic_int_get (&allocator->active))
670 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
677 case V4L2_MEMORY_MMAP:
678 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, MMAP);
680 case V4L2_MEMORY_USERPTR:
681 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, USERPTR);
683 case V4L2_MEMORY_DMABUF:
684 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, DMABUF);
687 can_allocate = FALSE;
691 GST_DEBUG_OBJECT (allocator, "allocated %u %s buffers out of %u requested",
692 breq.count, memory_type_to_str (memory), count);
694 allocator->can_allocate = can_allocate;
695 allocator->count = breq.count;
696 allocator->memory = memory;
698 /* Create memory groups */
699 for (i = 0; i < allocator->count; i++) {
700 allocator->groups[i] = gst_v4l2_memory_group_new (allocator, i);
701 if (allocator->groups[i] == NULL)
704 gst_atomic_queue_push (allocator->free_queue, allocator->groups[i]);
707 g_atomic_int_set (&allocator->active, TRUE);
710 GST_OBJECT_UNLOCK (allocator);
715 GST_ERROR_OBJECT (allocator, "allocator already active");
720 GST_ERROR_OBJECT (allocator,
721 "error requesting %d buffers: %s", count, g_strerror (errno));
726 GST_ERROR_OBJECT (allocator, "Not enough memory to allocate buffers");
737 gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
739 GstV4l2Object *obj = allocator->obj;
740 struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory };
742 GstV4l2Return ret = GST_V4L2_OK;
744 GST_DEBUG_OBJECT (allocator, "stop allocator");
746 GST_OBJECT_LOCK (allocator);
748 if (!g_atomic_int_get (&allocator->active))
751 if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) {
752 GST_DEBUG_OBJECT (allocator, "allocator is still in use");
757 while (gst_atomic_queue_pop (allocator->free_queue)) {
761 for (i = 0; i < allocator->count; i++) {
762 GstV4l2MemoryGroup *group = allocator->groups[i];
763 allocator->groups[i] = NULL;
765 gst_v4l2_memory_group_free (group);
768 /* Not all drivers support rebufs(0), so warn only */
769 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
770 GST_WARNING_OBJECT (allocator,
771 "error releasing buffers buffers: %s", g_strerror (errno));
773 allocator->count = 0;
775 g_atomic_int_set (&allocator->active, FALSE);
778 GST_OBJECT_UNLOCK (allocator);
783 gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
785 GstV4l2Object *obj = allocator->obj;
786 GstV4l2MemoryGroup *group;
789 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
791 group = gst_v4l2_allocator_alloc (allocator);
796 for (i = 0; i < group->n_mem; i++) {
797 if (group->mem[i] == NULL) {
799 data = obj->mmap (NULL, group->planes[i].length, PROT_READ | PROT_WRITE,
800 MAP_SHARED, obj->video_fd, group->planes[i].m.mem_offset);
802 if (data == MAP_FAILED)
805 GST_LOG_OBJECT (allocator,
806 "mmap buffer length %d, data offset %d, plane %d",
807 group->planes[i].length, group->planes[i].data_offset, i);
809 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
810 NULL, group->planes[i].length, 0, 0, group->planes[i].length, i, data,
813 /* Take back the allocator reference */
814 gst_object_ref (allocator);
817 group->mems_allocated++;
820 /* Ensure group size. Unlike GST, v4l2 have size (bytesused) initially set
821 * to 0. As length might be bigger then the expected size exposed in the
822 * format, we simply set bytesused initially and reset it here for
824 gst_v4l2_allocator_reset_size (allocator, group);
830 GST_ERROR_OBJECT (allocator, "Failed to mmap buffer: %s",
832 _cleanup_failed_alloc (allocator, group);
838 gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
839 GstAllocator * dmabuf_allocator)
841 GstV4l2Object *obj = allocator->obj;
842 GstV4l2MemoryGroup *group;
845 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
847 group = gst_v4l2_allocator_alloc (allocator);
852 for (i = 0; i < group->n_mem; i++) {
857 if (group->mem[i] == NULL) {
858 struct v4l2_exportbuffer expbuf = { 0 };
860 expbuf.type = obj->type;
861 expbuf.index = group->buffer.index;
863 expbuf.flags = O_CLOEXEC | O_RDWR;
865 if (obj->ioctl (obj->video_fd, VIDIOC_EXPBUF, &expbuf) < 0)
868 GST_LOG_OBJECT (allocator, "exported DMABUF as fd %i plane %d",
871 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
872 NULL, group->planes[i].length, 0, group->planes[i].data_offset,
873 group->planes[i].length - group->planes[i].data_offset, i, NULL,
876 /* Take back the allocator reference */
877 gst_object_ref (allocator);
880 group->mems_allocated++;
882 g_assert (gst_is_v4l2_memory (group->mem[i]));
883 mem = (GstV4l2Memory *) group->mem[i];
885 if ((dmafd = dup (mem->dmafd)) < 0)
888 dma_mem = gst_dmabuf_allocator_alloc (dmabuf_allocator, dmafd,
889 group->planes[i].length);
890 gst_memory_resize (dma_mem, group->planes[i].data_offset,
891 group->planes[i].length - group->planes[i].data_offset);
893 gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem),
894 GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref);
896 group->mem[i] = dma_mem;
899 gst_v4l2_allocator_reset_size (allocator, group);
905 GST_ERROR_OBJECT (allocator, "Failed to export DMABUF: %s",
911 GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
917 _cleanup_failed_alloc (allocator, group);
923 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
924 GstV4l2MemoryGroup * group)
926 GstV4l2Object *obj = allocator->obj;
930 g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
932 for (i = 0; i < group->n_mem; i++) {
934 mem = (GstV4l2Memory *) group->mem[i];
936 GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d",
943 mem->mem.maxsize = 0;
948 /* Update v4l2 structure */
949 group->planes[i].length = 0;
950 group->planes[i].bytesused = 0;
951 group->planes[i].m.fd = -1;
952 group->planes[i].data_offset = 0;
955 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
956 group->buffer.bytesused = 0;
957 group->buffer.length = 0;
958 group->buffer.m.fd = -1;
963 gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
965 GstV4l2MemoryGroup *group;
968 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
970 group = gst_v4l2_allocator_alloc (allocator);
975 GST_LOG_OBJECT (allocator, "allocating empty DMABUF import group");
977 for (i = 0; i < group->n_mem; i++) {
978 if (group->mem[i] == NULL) {
979 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
980 NULL, 0, 0, 0, 0, i, NULL, -1, group);
982 /* Take back the allocator reference */
983 gst_object_ref (allocator);
986 group->mems_allocated++;
989 gst_v4l2_allocator_clear_dmabufin (allocator, group);
995 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
996 GstV4l2MemoryGroup * group)
998 GstV4l2Object *obj = allocator->obj;
1002 g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
1004 for (i = 0; i < group->n_mem; i++) {
1005 mem = (GstV4l2Memory *) group->mem[i];
1007 GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %"
1008 G_GSIZE_FORMAT, mem->data, i, mem->mem.size);
1010 mem->mem.maxsize = 0;
1014 group->planes[i].length = 0;
1015 group->planes[i].bytesused = 0;
1016 group->planes[i].m.userptr = 0;
1019 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1020 group->buffer.bytesused = 0;
1021 group->buffer.length = 0;
1022 group->buffer.m.userptr = 0;
1026 GstV4l2MemoryGroup *
1027 gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
1029 GstV4l2MemoryGroup *group;
1032 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
1034 group = gst_v4l2_allocator_alloc (allocator);
1039 GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
1041 for (i = 0; i < group->n_mem; i++) {
1043 if (group->mem[i] == NULL) {
1044 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
1045 NULL, 0, 0, 0, 0, i, NULL, -1, group);
1047 /* Take back the allocator reference */
1048 gst_object_ref (allocator);
1051 group->mems_allocated++;
1054 gst_v4l2_allocator_clear_userptr (allocator, group);
1060 gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
1061 GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
1063 GstV4l2Object *obj = allocator->obj;
1067 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
1069 if (group->n_mem != n_mem)
1070 goto n_mem_missmatch;
1072 for (i = 0; i < group->n_mem; i++) {
1074 gsize size, offset, maxsize;
1076 if (!gst_is_dmabuf_memory (dma_mem[i]))
1079 size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
1081 if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0)
1084 GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i);
1086 mem = (GstV4l2Memory *) group->mem[i];
1089 mem->mem.maxsize = maxsize;
1090 mem->mem.offset = offset;
1091 mem->mem.size = size;
1094 /* Update v4l2 structure */
1095 group->planes[i].length = maxsize;
1096 group->planes[i].bytesused = size + offset;
1097 group->planes[i].m.fd = dmafd;
1098 group->planes[i].data_offset = offset;
1101 /* Copy into buffer structure if not using planes */
1102 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1103 group->buffer.bytesused = group->planes[0].bytesused;
1104 group->buffer.length = group->planes[0].length;
1105 group->buffer.m.fd = group->planes[0].m.userptr;
1107 /* FIXME Check if data_offset > 0 and fail for non-multi-planar */
1108 g_assert (group->planes[0].data_offset == 0);
1110 group->buffer.length = group->n_mem;
1117 GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
1123 GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
1128 GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
1129 g_strerror (errno));
1135 gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
1136 GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
1137 gpointer * data, gsize * size)
1139 GstV4l2Object *obj = allocator->obj;
1143 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
1145 /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */
1146 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type) && n_planes != group->n_mem)
1147 goto n_mem_missmatch;
1149 for (i = 0; i < group->n_mem; i++) {
1150 gsize maxsize, psize;
1152 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1153 maxsize = group->planes[i].length;
1156 maxsize = group->planes[i].length;
1160 g_assert (psize <= img_size);
1162 GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
1163 G_GSIZE_FORMAT, data[i], i, psize);
1165 mem = (GstV4l2Memory *) group->mem[i];
1167 mem->mem.maxsize = maxsize;
1168 mem->mem.size = psize;
1169 mem->data = data[i];
1171 group->planes[i].length = maxsize;
1172 group->planes[i].bytesused = psize;
1173 group->planes[i].m.userptr = (unsigned long) data[i];
1174 group->planes[i].data_offset = 0;
1177 /* Copy into buffer structure if not using planes */
1178 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1179 group->buffer.bytesused = group->planes[0].bytesused;
1180 group->buffer.length = group->planes[0].length;
1181 group->buffer.m.userptr = group->planes[0].m.userptr;
1183 group->buffer.length = group->n_mem;
1190 GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
1191 n_planes, group->n_mem);
1197 gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
1201 GST_OBJECT_LOCK (allocator);
1203 if (!g_atomic_int_get (&allocator->active))
1206 for (i = 0; i < allocator->count; i++) {
1207 GstV4l2MemoryGroup *group = allocator->groups[i];
1210 if (IS_QUEUED (group->buffer)) {
1211 UNSET_QUEUED (group->buffer);
1213 gst_v4l2_allocator_reset_group (allocator, group);
1215 for (n = 0; n < group->n_mem; n++)
1216 gst_memory_unref (group->mem[n]);
1221 GST_OBJECT_UNLOCK (allocator);
1225 gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
1226 GstV4l2MemoryGroup * group)
1228 GstV4l2Object *obj = allocator->obj;
1229 gboolean ret = TRUE;
1232 g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE);
1235 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1236 for (i = 0; i < group->n_mem; i++)
1237 group->planes[i].bytesused =
1238 gst_memory_get_sizes (group->mem[i], NULL, NULL);
1240 group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL);
1243 /* Ensure the memory will stay around and is RO */
1244 for (i = 0; i < group->n_mem; i++)
1245 gst_memory_ref (group->mem[i]);
1247 if (obj->ioctl (obj->video_fd, VIDIOC_QBUF, &group->buffer) < 0) {
1248 GST_ERROR_OBJECT (allocator, "failed queueing buffer %i: %s",
1249 group->buffer.index, g_strerror (errno));
1251 /* Release the memory, possibly making it RW again */
1252 for (i = 0; i < group->n_mem; i++)
1253 gst_memory_unref (group->mem[i]);
1256 if (IS_QUEUED (group->buffer)) {
1257 GST_DEBUG_OBJECT (allocator,
1258 "driver pretends buffer is queued even if queue failed");
1259 UNSET_QUEUED (group->buffer);
1264 GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)",
1265 group->buffer.index, group->buffer.flags);
1267 if (!IS_QUEUED (group->buffer)) {
1268 GST_DEBUG_OBJECT (allocator,
1269 "driver pretends buffer is not queued even if queue succeeded");
1270 SET_QUEUED (group->buffer);
1278 gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator,
1279 GstV4l2MemoryGroup ** group_out)
1281 GstV4l2Object *obj = allocator->obj;
1282 struct v4l2_buffer buffer = { 0 };
1283 struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} };
1286 GstV4l2MemoryGroup *group = NULL;
1288 g_return_val_if_fail (g_atomic_int_get (&allocator->active), GST_FLOW_ERROR);
1290 buffer.type = obj->type;
1291 buffer.memory = allocator->memory;
1293 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1294 buffer.length = obj->format.fmt.pix_mp.num_planes;
1295 buffer.m.planes = planes;
1298 if (obj->ioctl (obj->video_fd, VIDIOC_DQBUF, &buffer) < 0)
1301 group = allocator->groups[buffer.index];
1303 if (!IS_QUEUED (group->buffer)) {
1304 GST_ERROR_OBJECT (allocator,
1305 "buffer %i was not queued, this indicate a driver bug.", buffer.index);
1306 return GST_FLOW_ERROR;
1309 group->buffer = buffer;
1311 GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index,
1314 if (IS_QUEUED (group->buffer)) {
1315 GST_DEBUG_OBJECT (allocator,
1316 "driver pretends buffer is queued even if dequeue succeeded");
1317 UNSET_QUEUED (group->buffer);
1320 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1321 group->buffer.m.planes = group->planes;
1322 memcpy (group->planes, buffer.m.planes, sizeof (planes));
1324 group->planes[0].bytesused = group->buffer.bytesused;
1325 group->planes[0].length = group->buffer.length;
1326 g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
1327 memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
1330 /* And update memory size */
1331 if (V4L2_TYPE_IS_OUTPUT (obj->type)) {
1332 gst_v4l2_allocator_reset_size (allocator, group);
1334 /* for capture, simply read the size */
1335 for (i = 0; i < group->n_mem; i++) {
1338 GST_LOG_OBJECT (allocator,
1339 "Dequeued capture buffer, length: %u bytesused: %u data_offset: %u",
1340 group->planes[i].length, group->planes[i].bytesused,
1341 group->planes[i].data_offset);
1343 offset = group->planes[i].data_offset;
1345 if (group->planes[i].bytesused > group->planes[i].data_offset) {
1346 size = group->planes[i].bytesused - group->planes[i].data_offset;
1348 GST_WARNING_OBJECT (allocator, "V4L2 provided buffer has bytesused %"
1349 G_GUINT32_FORMAT " which is too small to include data_offset %"
1350 G_GUINT32_FORMAT, group->planes[i].bytesused,
1351 group->planes[i].data_offset);
1352 size = group->planes[i].bytesused;
1355 if (G_LIKELY (size + offset <= group->mem[i]->maxsize))
1356 gst_memory_resize (group->mem[i], offset, size);
1358 GST_WARNING_OBJECT (allocator,
1359 "v4l2 provided buffer that is too big for the memory it was "
1360 "writing into. v4l2 claims %" G_GSIZE_FORMAT " bytes used but "
1361 "memory is only %" G_GSIZE_FORMAT "B. This is probably a driver "
1362 "bug.", size, group->mem[i]->maxsize);
1363 gst_memory_resize (group->mem[i], 0, group->mem[i]->maxsize);
1368 /* Release the memory, possibly making it RW again */
1369 for (i = 0; i < group->n_mem; i++)
1370 gst_memory_unref (group->mem[i]);
1376 if (errno == EPIPE) {
1377 GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer");
1378 return GST_FLOW_EOS;
1381 GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
1382 memory_type_to_str (allocator->memory), g_strerror (errno));
1386 GST_WARNING_OBJECT (allocator,
1387 "Non-blocking I/O has been selected using O_NONBLOCK and"
1388 " no buffer was in the outgoing queue.");
1391 GST_ERROR_OBJECT (allocator,
1392 "The buffer type is not supported, or the index is out of bounds, "
1393 "or no buffers have been allocated yet, or the userptr "
1394 "or length are invalid.");
1397 GST_ERROR_OBJECT (allocator,
1398 "insufficient memory to enqueue a user pointer buffer");
1401 GST_INFO_OBJECT (allocator,
1402 "VIDIOC_DQBUF failed due to an internal error."
1403 " Can also indicate temporary problems like signal loss."
1404 " Note the driver might dequeue an (empty) buffer despite"
1405 " returning an error, or even stop capturing.");
1406 /* have we de-queued a buffer ? */
1407 if (!IS_QUEUED (buffer)) {
1408 GST_DEBUG_OBJECT (allocator, "reenqueueing buffer");
1409 /* FIXME ... should we do something here? */
1413 GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device");
1416 GST_WARNING_OBJECT (allocator,
1417 "Grabbing frame got interrupted unexpectedly. %d: %s.", errno,
1418 g_strerror (errno));
1422 return GST_FLOW_ERROR;
1426 gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
1427 GstV4l2MemoryGroup * group)
1429 switch (allocator->memory) {
1430 case V4L2_MEMORY_USERPTR:
1431 gst_v4l2_allocator_clear_userptr (allocator, group);
1433 case V4L2_MEMORY_DMABUF:
1434 gst_v4l2_allocator_clear_dmabufin (allocator, group);
1436 case V4L2_MEMORY_MMAP:
1439 g_assert_not_reached ();
1443 gst_v4l2_allocator_reset_size (allocator, group);