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>
41 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
42 #include <tbm_surface.h>
43 #include <tbm_surface_internal.h>
44 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
46 #define GST_V4L2_MEMORY_TYPE "V4l2Memory"
48 #define gst_v4l2_allocator_parent_class parent_class
49 G_DEFINE_TYPE (GstV4l2Allocator, gst_v4l2_allocator, GST_TYPE_ALLOCATOR);
51 GST_DEBUG_CATEGORY_STATIC (v4l2allocator_debug);
52 #define GST_CAT_DEFAULT v4l2allocator_debug
54 #define UNSET_QUEUED(buffer) \
55 ((buffer).flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
57 #define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED)
59 #define IS_QUEUED(buffer) \
60 ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
68 static guint gst_v4l2_allocator_signals[LAST_SIGNAL] = { 0 };
70 static void gst_v4l2_allocator_release (GstV4l2Allocator * allocator,
74 memory_type_to_str (guint32 memory)
77 case V4L2_MEMORY_MMAP:
79 case V4L2_MEMORY_USERPTR:
81 case V4L2_MEMORY_DMABUF:
88 /*************************************/
89 /* GstV4lMemory implementation */
90 /*************************************/
93 _v4l2mem_map (GstV4l2Memory * mem, gsize maxsize, GstMapFlags flags)
97 switch (mem->group->buffer.memory) {
98 case V4L2_MEMORY_MMAP:
99 case V4L2_MEMORY_USERPTR:
102 case V4L2_MEMORY_DMABUF:
103 /* v4l2 dmabuf memory are not shared with downstream */
104 g_assert_not_reached ();
107 GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
114 _v4l2mem_unmap (GstV4l2Memory * mem)
116 gboolean ret = FALSE;
118 switch (mem->group->buffer.memory) {
119 case V4L2_MEMORY_MMAP:
120 case V4L2_MEMORY_USERPTR:
123 case V4L2_MEMORY_DMABUF:
124 /* v4l2 dmabuf memory are not share with downstream */
125 g_assert_not_reached ();
128 GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
135 _v4l2mem_dispose (GstV4l2Memory * mem)
137 GstV4l2Allocator *allocator = (GstV4l2Allocator *) mem->mem.allocator;
138 GstV4l2MemoryGroup *group = mem->group;
141 if (group->mem[mem->plane]) {
142 /* We may have a dmabuf, replace it with returned original memory */
143 group->mem[mem->plane] = gst_memory_ref ((GstMemory *) mem);
144 gst_v4l2_allocator_release (allocator, mem);
147 gst_object_ref (allocator);
154 static inline GstV4l2Memory *
155 _v4l2mem_new (GstMemoryFlags flags, GstAllocator * allocator,
156 GstMemory * parent, gsize maxsize, gsize align, gsize offset, gsize size,
157 gint plane, gpointer data, int dmafd, GstV4l2MemoryGroup * group)
161 mem = g_slice_new0 (GstV4l2Memory);
162 gst_memory_init (GST_MEMORY_CAST (mem),
163 flags, allocator, parent, maxsize, align, offset, size);
166 mem->mem.mini_object.dispose =
167 (GstMiniObjectDisposeFunction) _v4l2mem_dispose;
177 static GstV4l2Memory *
178 _v4l2mem_share (GstV4l2Memory * mem, gssize offset, gsize size)
183 /* find the real parent */
184 if ((parent = mem->mem.parent) == NULL)
185 parent = (GstMemory *) mem;
188 size = mem->mem.size - offset;
190 /* the shared memory is always readonly */
191 sub = _v4l2mem_new (GST_MINI_OBJECT_FLAGS (parent) |
192 GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->mem.allocator, parent,
193 mem->mem.maxsize, mem->mem.align, offset, size, mem->plane, mem->data,
200 _v4l2mem_is_span (GstV4l2Memory * mem1, GstV4l2Memory * mem2, gsize * offset)
203 *offset = mem1->mem.offset - mem1->mem.parent->offset;
205 /* and memory is contiguous */
206 return mem1->mem.offset + mem1->mem.size == mem2->mem.offset;
210 gst_is_v4l2_memory (GstMemory * mem)
212 return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE);
216 gst_v4l2_memory_quark (void)
218 static GQuark quark = 0;
221 quark = g_quark_from_string ("GstV4l2Memory");
227 /*************************************/
228 /* GstV4l2MemoryGroup implementation */
229 /*************************************/
232 gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
236 for (i = 0; i < group->n_mem; i++) {
237 GstMemory *mem = group->mem[i];
238 group->mem[i] = NULL;
240 gst_memory_unref (mem);
242 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
243 if (group->surface) {
244 GST_INFO ("unref surface[%p]", group->surface);
245 tbm_surface_destroy (group->surface);
246 group->surface = NULL;
248 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
250 g_slice_free (GstV4l2MemoryGroup, group);
253 static GstV4l2MemoryGroup *
254 gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
256 GstV4l2Object *obj = allocator->obj;
257 guint32 memory = allocator->memory;
258 struct v4l2_format *format = &obj->format;
259 GstV4l2MemoryGroup *group;
260 gsize img_size, buf_size;
262 group = g_slice_new0 (GstV4l2MemoryGroup);
264 group->buffer.type = format->type;
265 group->buffer.index = index;
266 group->buffer.memory = memory;
268 if (V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
269 group->n_mem = group->buffer.length = format->fmt.pix_mp.num_planes;
270 group->buffer.m.planes = group->planes;
275 if (obj->ioctl (obj->video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0)
276 goto querybuf_failed;
278 if (group->buffer.index != index) {
279 GST_ERROR_OBJECT (allocator, "Buffer index returned by VIDIOC_QUERYBUF "
280 "didn't match, this indicate the presence of a bug in your driver or "
282 g_slice_free (GstV4l2MemoryGroup, group);
286 /* Check that provided size matches the format we have negotiation. Failing
287 * there usually means a driver of libv4l bug. */
288 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
291 for (i = 0; i < group->n_mem; i++) {
292 img_size = obj->format.fmt.pix_mp.plane_fmt[i].sizeimage;
293 buf_size = group->planes[i].length;
294 if (buf_size < img_size)
295 goto buffer_too_short;
298 img_size = obj->format.fmt.pix.sizeimage;
299 buf_size = group->buffer.length;
300 if (buf_size < img_size)
301 goto buffer_too_short;
304 /* We save non planar buffer information into the multi-planar plane array
305 * to avoid duplicating the code later */
306 if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
307 group->planes[0].bytesused = group->buffer.bytesused;
308 group->planes[0].length = group->buffer.length;
309 group->planes[0].data_offset = 0;
310 g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
311 memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
314 GST_LOG_OBJECT (allocator, "Got %s buffer", memory_type_to_str (memory));
315 GST_LOG_OBJECT (allocator, " index: %u", group->buffer.index);
316 GST_LOG_OBJECT (allocator, " type: %d", group->buffer.type);
317 GST_LOG_OBJECT (allocator, " flags: %08x", group->buffer.flags);
318 GST_LOG_OBJECT (allocator, " field: %d", group->buffer.field);
319 GST_LOG_OBJECT (allocator, " memory: %d", group->buffer.memory);
320 GST_LOG_OBJECT (allocator, " planes: %d", group->n_mem);
322 #ifndef GST_DISABLE_GST_DEBUG
323 if (memory == V4L2_MEMORY_MMAP) {
325 for (i = 0; i < group->n_mem; i++) {
326 GST_LOG_OBJECT (allocator,
327 " [%u] bytesused: %u, length: %u, offset: %u", i,
328 group->planes[i].bytesused, group->planes[i].length,
329 group->planes[i].data_offset);
330 GST_LOG_OBJECT (allocator, " [%u] MMAP offset: %u", i,
331 group->planes[i].m.mem_offset);
340 GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno));
345 GST_ERROR ("buffer size %" G_GSIZE_FORMAT
346 " is smaller then negotiated size %" G_GSIZE_FORMAT
347 ", this is usually the result of a bug in the v4l2 driver or libv4l.",
352 gst_v4l2_memory_group_free (group);
357 /*************************************/
358 /* GstV4lAllocator implementation */
359 /*************************************/
362 gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
364 GstV4l2MemoryGroup *group = mem->group;
366 GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
367 mem->plane, group->buffer.index);
369 switch (allocator->memory) {
370 case V4L2_MEMORY_DMABUF:
374 case V4L2_MEMORY_USERPTR:
381 /* When all memory are back, put the group back in the free queue */
382 if (g_atomic_int_dec_and_test (&group->mems_allocated)) {
383 GST_LOG_OBJECT (allocator, "buffer %u released", group->buffer.index);
384 gst_atomic_queue_push (allocator->free_queue, group);
385 g_signal_emit (allocator, gst_v4l2_allocator_signals[GROUP_RELEASED], 0);
388 /* Keep last, allocator may be freed after this call */
389 g_object_unref (allocator);
393 gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
395 GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator;
396 GstV4l2Object *obj = allocator->obj;
397 GstV4l2Memory *mem = (GstV4l2Memory *) gmem;
398 GstV4l2MemoryGroup *group = mem->group;
400 /* Only free unparented memory */
401 if (mem->mem.parent == NULL) {
402 GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u",
403 mem->plane, group->buffer.index);
405 if (allocator->memory == V4L2_MEMORY_MMAP) {
407 obj->munmap (mem->data, group->planes[mem->plane].length);
410 /* This apply for both mmap with expbuf, and dmabuf imported memory */
415 g_slice_free (GstV4l2Memory, mem);
419 gst_v4l2_allocator_dispose (GObject * obj)
421 GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
424 GST_LOG_OBJECT (obj, "called");
426 for (i = 0; i < allocator->count; i++) {
427 GstV4l2MemoryGroup *group = allocator->groups[i];
428 allocator->groups[i] = NULL;
430 gst_v4l2_memory_group_free (group);
433 G_OBJECT_CLASS (parent_class)->dispose (obj);
437 gst_v4l2_allocator_finalize (GObject * obj)
439 GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
441 GST_LOG_OBJECT (obj, "called");
442 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
443 if (allocator->bufmgr) {
444 GST_INFO_OBJECT (obj, "deinit tbm bufmgr %p", allocator->bufmgr);
445 tbm_bufmgr_deinit (allocator->bufmgr);
446 allocator->bufmgr = NULL;
448 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
450 gst_atomic_queue_unref (allocator->free_queue);
451 gst_object_unref (allocator->obj->element);
453 G_OBJECT_CLASS (parent_class)->finalize (obj);
457 gst_v4l2_allocator_class_init (GstV4l2AllocatorClass * klass)
459 GObjectClass *object_class;
460 GstAllocatorClass *allocator_class;
462 allocator_class = (GstAllocatorClass *) klass;
463 object_class = (GObjectClass *) klass;
465 allocator_class->alloc = NULL;
466 allocator_class->free = gst_v4l2_allocator_free;
468 object_class->dispose = gst_v4l2_allocator_dispose;
469 object_class->finalize = gst_v4l2_allocator_finalize;
471 gst_v4l2_allocator_signals[GROUP_RELEASED] = g_signal_new ("group-released",
472 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
475 GST_DEBUG_CATEGORY_INIT (v4l2allocator_debug, "v4l2allocator", 0,
480 gst_v4l2_allocator_init (GstV4l2Allocator * allocator)
482 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
484 alloc->mem_type = GST_V4L2_MEMORY_TYPE;
485 alloc->mem_map = (GstMemoryMapFunction) _v4l2mem_map;
486 alloc->mem_unmap = (GstMemoryUnmapFunction) _v4l2mem_unmap;
487 alloc->mem_share = (GstMemoryShareFunction) _v4l2mem_share;
488 alloc->mem_is_span = (GstMemoryIsSpanFunction) _v4l2mem_is_span;
489 /* Use the default, fallback copy function */
491 allocator->free_queue = gst_atomic_queue_new (VIDEO_MAX_FRAME);
493 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
496 #define GST_V4L2_ALLOCATOR_PROBE(obj,type) \
497 gst_v4l2_allocator_probe ((obj), V4L2_MEMORY_ ## type, \
498 GST_V4L2_ALLOCATOR_FLAG_ ## type ## _REQBUFS, \
499 GST_V4L2_ALLOCATOR_FLAG_ ## type ## _CREATE_BUFS)
501 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
502 guint32 breq_flag, guint32 bcreate_flag)
504 GstV4l2Object *obj = allocator->obj;
505 struct v4l2_requestbuffers breq = { 0 };
508 breq.type = obj->type;
510 breq.memory = memory;
512 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) == 0) {
513 struct v4l2_create_buffers bcreate = { 0 };
517 bcreate.memory = memory;
518 bcreate.format = obj->format;
520 if ((obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0))
521 flags |= bcreate_flag;
524 if (breq.capabilities & V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS)
525 flags |= GST_V4L2_ALLOCATOR_FLAG_SUPPORTS_ORPHANED_BUFS;
530 static GstV4l2MemoryGroup *
531 gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
533 GstV4l2Object *obj = allocator->obj;
534 struct v4l2_create_buffers bcreate = { 0 };
535 GstV4l2MemoryGroup *group = NULL;
537 GST_OBJECT_LOCK (allocator);
539 if (!g_atomic_int_get (&allocator->active))
542 if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator))
545 bcreate.memory = allocator->memory;
546 bcreate.format = obj->format;
549 if (!allocator->can_allocate)
552 if (obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0)
553 goto create_bufs_failed;
555 if (allocator->groups[bcreate.index] != NULL)
556 goto create_bufs_bug;
558 group = gst_v4l2_memory_group_new (allocator, bcreate.index);
561 allocator->groups[bcreate.index] = group;
566 GST_OBJECT_UNLOCK (allocator);
571 GST_ERROR_OBJECT (allocator, "allocator was orphaned, "
572 "not creating new buffers");
577 GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s",
583 GST_ERROR_OBJECT (allocator, "created buffer has already used buffer "
584 "index %i, this means there is an bug in your driver or libv4l2",
590 static GstV4l2MemoryGroup *
591 gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator)
593 GstV4l2MemoryGroup *group;
595 if (!g_atomic_int_get (&allocator->active))
598 group = gst_atomic_queue_pop (allocator->free_queue);
601 if (allocator->can_allocate) {
602 group = gst_v4l2_allocator_create_buf (allocator);
604 /* Don't hammer on CREATE_BUFS */
606 allocator->can_allocate = FALSE;
614 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
615 GstV4l2MemoryGroup * group)
618 for (i = 0; i < group->n_mem; i++) {
619 group->mem[i]->maxsize = group->planes[i].length;
620 group->mem[i]->offset = 0;
621 group->mem[i]->size = group->planes[i].length;
626 _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
628 if (group->mems_allocated > 0) {
630 /* If one or more mmap worked, we need to unref the memory, otherwise
631 * they will keep a ref on the allocator and leak it. This will put back
632 * the group into the free_queue */
633 for (i = 0; i < group->n_mem; i++)
634 gst_memory_unref (group->mem[i]);
636 /* Otherwise, group has to be on free queue for _stop() to work */
637 gst_atomic_queue_push (allocator->free_queue, group);
641 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
642 static tbm_format __get_tbm_format (GstVideoFormat gst_format)
644 switch (gst_format) {
645 case GST_VIDEO_FORMAT_NV12:
646 case GST_VIDEO_FORMAT_SN12:
647 return TBM_FORMAT_NV12;
648 case GST_VIDEO_FORMAT_I420:
649 case GST_VIDEO_FORMAT_S420:
651 return TBM_FORMAT_YUV420;
654 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
657 gst_v4l2_allocator_new (GstObject * parent, GstV4l2Object * v4l2object)
659 GstV4l2Allocator *allocator;
661 gchar *name, *parent_name;
663 parent_name = gst_object_get_name (parent);
664 name = g_strconcat (parent_name, ":allocator", NULL);
665 g_free (parent_name);
667 allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL);
668 gst_object_ref_sink (allocator);
671 /* Save everything */
672 allocator->obj = v4l2object;
674 /* Keep a ref on the elemnt so obj does not disapear */
675 gst_object_ref (allocator->obj->element);
677 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP);
678 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR);
679 flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF);
683 /* Drivers not ported from videobuf to videbuf2 don't allow freeing buffers
684 * using REQBUFS(0). This is a workaround to still support these drivers,
685 * which are known to have MMAP support. */
686 GST_WARNING_OBJECT (allocator, "Could not probe supported memory type, "
687 "assuming MMAP is supported, this is expected for older drivers not "
688 " yet ported to videobuf2 framework");
689 flags = GST_V4L2_ALLOCATOR_FLAG_MMAP_REQBUFS;
692 GST_OBJECT_FLAG_SET (allocator, flags);
694 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
695 if (v4l2object->tbm_output &&
696 !V4L2_TYPE_IS_OUTPUT (v4l2object->type) &&
697 v4l2object->mode == GST_V4L2_IO_DMABUF) {
698 tbm_surface_h tmp_surface = NULL;
699 int width = GST_VIDEO_INFO_WIDTH (&v4l2object->info);
700 int height = GST_VIDEO_INFO_HEIGHT (&v4l2object->info);
702 tmp_surface = tbm_surface_create (width, height,
703 __get_tbm_format (GST_VIDEO_INFO_FORMAT (&v4l2object->info)));
705 tbm_surface_get_info (tmp_surface, &allocator->s_info);
706 GST_INFO_OBJECT (allocator, "[%dx%d] -> tbm surface info[%dx%d]",
707 width, height, allocator->s_info.width, allocator->s_info.height);
708 tbm_surface_destroy (tmp_surface);
710 GST_ERROR_OBJECT (allocator, "[%dx%d] surface failed", width, height);
713 allocator->bufmgr = tbm_bufmgr_init (-1);
714 if (!allocator->bufmgr) {
715 GST_ERROR_OBJECT (allocator, "tbm bufmgr failed");
716 gst_object_unref (allocator);
720 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
725 gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
728 GstV4l2Object *obj = allocator->obj;
729 struct v4l2_requestbuffers breq = { count, obj->type, memory };
730 gboolean can_allocate;
733 g_return_val_if_fail (count != 0, 0);
735 GST_OBJECT_LOCK (allocator);
737 if (g_atomic_int_get (&allocator->active))
740 if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator))
743 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
750 case V4L2_MEMORY_MMAP:
751 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, MMAP);
753 case V4L2_MEMORY_USERPTR:
754 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, USERPTR);
756 case V4L2_MEMORY_DMABUF:
757 can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, DMABUF);
760 can_allocate = FALSE;
764 GST_DEBUG_OBJECT (allocator, "allocated %u %s buffers out of %u requested",
765 breq.count, memory_type_to_str (memory), count);
767 allocator->can_allocate = can_allocate;
768 allocator->count = breq.count;
769 allocator->memory = memory;
771 /* Create memory groups */
772 for (i = 0; i < allocator->count; i++) {
773 allocator->groups[i] = gst_v4l2_memory_group_new (allocator, i);
774 if (allocator->groups[i] == NULL)
777 gst_atomic_queue_push (allocator->free_queue, allocator->groups[i]);
780 g_atomic_int_set (&allocator->active, TRUE);
783 GST_OBJECT_UNLOCK (allocator);
788 GST_ERROR_OBJECT (allocator, "allocator already active");
793 GST_ERROR_OBJECT (allocator, "allocator was orphaned");
798 GST_ERROR_OBJECT (allocator,
799 "error requesting %d buffers: %s", count, g_strerror (errno));
804 GST_ERROR_OBJECT (allocator, "Not enough memory to allocate buffers");
815 gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
817 GstV4l2Object *obj = allocator->obj;
818 struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory };
820 GstV4l2Return ret = GST_V4L2_OK;
822 GST_DEBUG_OBJECT (allocator, "stop allocator");
824 GST_OBJECT_LOCK (allocator);
826 if (!g_atomic_int_get (&allocator->active))
829 if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) {
830 GST_DEBUG_OBJECT (allocator, "allocator is still in use");
835 while (gst_atomic_queue_pop (allocator->free_queue)) {
839 for (i = 0; i < allocator->count; i++) {
840 GstV4l2MemoryGroup *group = allocator->groups[i];
841 allocator->groups[i] = NULL;
843 gst_v4l2_memory_group_free (group);
846 if (!GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator)) {
847 /* Not all drivers support rebufs(0), so warn only */
848 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
849 GST_WARNING_OBJECT (allocator,
850 "error releasing buffers buffers: %s", g_strerror (errno));
853 allocator->count = 0;
855 g_atomic_int_set (&allocator->active, FALSE);
858 GST_OBJECT_UNLOCK (allocator);
863 gst_v4l2_allocator_orphan (GstV4l2Allocator * allocator)
865 GstV4l2Object *obj = allocator->obj;
866 struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory };
868 if (!GST_V4L2_ALLOCATOR_CAN_ORPHAN_BUFS (allocator))
871 GST_OBJECT_FLAG_SET (allocator, GST_V4L2_ALLOCATOR_FLAG_ORPHANED);
873 if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0) {
874 GST_ERROR_OBJECT (allocator,
875 "error orphaning buffers buffers: %s", g_strerror (errno));
883 gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
885 GstV4l2Object *obj = allocator->obj;
886 GstV4l2MemoryGroup *group;
889 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
891 group = gst_v4l2_allocator_alloc (allocator);
896 for (i = 0; i < group->n_mem; i++) {
897 if (group->mem[i] == NULL) {
899 data = obj->mmap (NULL, group->planes[i].length, PROT_READ | PROT_WRITE,
900 MAP_SHARED, obj->video_fd, group->planes[i].m.mem_offset);
902 if (data == MAP_FAILED)
905 GST_LOG_OBJECT (allocator,
906 "mmap buffer length %d, data offset %d, plane %d",
907 group->planes[i].length, group->planes[i].data_offset, i);
909 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
910 NULL, group->planes[i].length, 0, 0, group->planes[i].length, i, data,
913 /* Take back the allocator reference */
914 gst_object_ref (allocator);
917 group->mems_allocated++;
920 /* Ensure group size. Unlike GST, v4l2 have size (bytesused) initially set
921 * to 0. As length might be bigger then the expected size exposed in the
922 * format, we simply set bytesused initially and reset it here for
924 gst_v4l2_allocator_reset_size (allocator, group);
930 GST_ERROR_OBJECT (allocator, "Failed to mmap buffer: %s",
932 _cleanup_failed_alloc (allocator, group);
938 gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
939 GstAllocator * dmabuf_allocator)
941 GstV4l2Object *obj = allocator->obj;
942 GstV4l2MemoryGroup *group;
944 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
945 tbm_bo bos[VIDEO_MAX_PLANES] = {NULL, };
946 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
948 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
950 group = gst_v4l2_allocator_alloc (allocator);
955 for (i = 0; i < group->n_mem; i++) {
959 if (group->mem[i] == NULL) {
960 struct v4l2_exportbuffer expbuf = { 0 };
962 expbuf.type = obj->type;
963 expbuf.index = group->buffer.index;
965 expbuf.flags = O_CLOEXEC | O_RDWR;
967 if (obj->ioctl (obj->video_fd, VIDIOC_EXPBUF, &expbuf) < 0)
970 GST_LOG_OBJECT (allocator, "exported DMABUF as fd %i plane %d",
973 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
974 NULL, group->planes[i].length, 0, group->planes[i].data_offset,
975 group->planes[i].length - group->planes[i].data_offset, i, NULL,
977 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
978 if (obj->tbm_output) {
979 bos[i] = tbm_bo_import_fd (allocator->bufmgr, expbuf.fd);
980 GST_INFO_OBJECT (allocator, "obj[%p,i:%d]: fd[%d] -> bo[%p]",
981 obj, expbuf.index, expbuf.fd, bos[i]);
983 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
985 /* Take back the allocator reference */
986 gst_object_ref (allocator);
989 group->mems_allocated++;
991 g_assert (gst_is_v4l2_memory (group->mem[i]));
992 mem = (GstV4l2Memory *) group->mem[i];
994 dma_mem = gst_fd_allocator_alloc (dmabuf_allocator, mem->dmafd,
995 group->planes[i].length, GST_FD_MEMORY_FLAG_DONT_CLOSE);
996 gst_memory_resize (dma_mem, group->planes[i].data_offset,
997 group->planes[i].length - group->planes[i].data_offset);
999 gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem),
1000 GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref);
1002 group->mem[i] = dma_mem;
1005 #ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT
1006 if (obj->tbm_output && !group->surface) {
1007 group->surface = tbm_surface_internal_create_with_bos (&allocator->s_info, bos, group->n_mem);
1008 GST_INFO_OBJECT (allocator, "new surface[%p] in memory group[%p]", group->surface, group);
1010 /* release bos - they will be kept in surface. */
1011 for (i = 0 ; i < VIDEO_MAX_PLANES && bos[i] ; i++)
1012 tbm_bo_unref (bos[i]);
1013 #endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */
1015 gst_v4l2_allocator_reset_size (allocator, group);
1021 GST_ERROR_OBJECT (allocator, "Failed to export DMABUF: %s",
1022 g_strerror (errno));
1027 _cleanup_failed_alloc (allocator, group);
1033 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
1034 GstV4l2MemoryGroup * group)
1036 GstV4l2Object *obj = allocator->obj;
1040 g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
1042 for (i = 0; i < group->n_mem; i++) {
1044 mem = (GstV4l2Memory *) group->mem[i];
1046 GST_LOG_OBJECT (allocator, "[%i] clearing DMABUF import, fd %i plane %d",
1047 group->buffer.index, mem->dmafd, i);
1050 mem->mem.maxsize = 0;
1051 mem->mem.offset = 0;
1055 /* Update v4l2 structure */
1056 group->planes[i].length = 0;
1057 group->planes[i].bytesused = 0;
1058 group->planes[i].m.fd = -1;
1059 group->planes[i].data_offset = 0;
1062 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1063 group->buffer.bytesused = 0;
1064 group->buffer.length = 0;
1065 group->buffer.m.fd = -1;
1069 GstV4l2MemoryGroup *
1070 gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
1072 GstV4l2MemoryGroup *group;
1075 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
1077 group = gst_v4l2_allocator_alloc (allocator);
1082 GST_LOG_OBJECT (allocator, "allocating empty DMABUF import group");
1084 for (i = 0; i < group->n_mem; i++) {
1085 if (group->mem[i] == NULL) {
1086 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
1087 NULL, 0, 0, 0, 0, i, NULL, -1, group);
1089 /* Take back the allocator reference */
1090 gst_object_ref (allocator);
1093 group->mems_allocated++;
1096 gst_v4l2_allocator_clear_dmabufin (allocator, group);
1102 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
1103 GstV4l2MemoryGroup * group)
1105 GstV4l2Object *obj = allocator->obj;
1109 g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
1111 for (i = 0; i < group->n_mem; i++) {
1112 mem = (GstV4l2Memory *) group->mem[i];
1114 GST_LOG_OBJECT (allocator, "[%i] clearing USERPTR %p plane %d size %"
1115 G_GSIZE_FORMAT, group->buffer.index, mem->data, i, mem->mem.size);
1117 mem->mem.maxsize = 0;
1121 group->planes[i].length = 0;
1122 group->planes[i].bytesused = 0;
1123 group->planes[i].m.userptr = 0;
1126 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1127 group->buffer.bytesused = 0;
1128 group->buffer.length = 0;
1129 group->buffer.m.userptr = 0;
1133 GstV4l2MemoryGroup *
1134 gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
1136 GstV4l2MemoryGroup *group;
1139 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
1141 group = gst_v4l2_allocator_alloc (allocator);
1146 GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
1148 for (i = 0; i < group->n_mem; i++) {
1150 if (group->mem[i] == NULL) {
1151 group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
1152 NULL, 0, 0, 0, 0, i, NULL, -1, group);
1154 /* Take back the allocator reference */
1155 gst_object_ref (allocator);
1158 group->mems_allocated++;
1161 gst_v4l2_allocator_clear_userptr (allocator, group);
1167 gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
1168 GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
1170 GstV4l2Object *obj = allocator->obj;
1174 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
1176 if (group->n_mem != n_mem)
1177 goto n_mem_missmatch;
1179 for (i = 0; i < group->n_mem; i++) {
1181 gsize size, offset, maxsize;
1183 if (!gst_is_dmabuf_memory (dma_mem[i]))
1186 size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
1188 dmafd = gst_dmabuf_memory_get_fd (dma_mem[i]);
1190 GST_LOG_OBJECT (allocator, "[%i] imported DMABUF as fd %i plane %d",
1191 group->buffer.index, dmafd, i);
1193 mem = (GstV4l2Memory *) group->mem[i];
1196 mem->mem.maxsize = maxsize;
1197 mem->mem.offset = offset;
1198 mem->mem.size = size;
1201 /* Update v4l2 structure */
1202 group->planes[i].length = maxsize;
1203 group->planes[i].bytesused = size + offset;
1204 group->planes[i].m.fd = dmafd;
1205 group->planes[i].data_offset = offset;
1208 /* Copy into buffer structure if not using planes */
1209 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1210 group->buffer.bytesused = group->planes[0].bytesused;
1211 group->buffer.length = group->planes[0].length;
1212 group->buffer.m.fd = group->planes[0].m.userptr;
1214 /* FIXME Check if data_offset > 0 and fail for non-multi-planar */
1215 g_assert (group->planes[0].data_offset == 0);
1217 group->buffer.length = group->n_mem;
1224 GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
1230 GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
1236 gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
1237 GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
1238 gpointer * data, gsize * size)
1240 GstV4l2Object *obj = allocator->obj;
1244 g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
1246 /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */
1247 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type) && n_planes != group->n_mem)
1248 goto n_mem_missmatch;
1250 for (i = 0; i < group->n_mem; i++) {
1251 gsize maxsize, psize;
1253 /* TODO request used size and maxsize seperatly */
1254 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type))
1255 maxsize = psize = size[i];
1257 maxsize = psize = img_size;
1259 g_assert (psize <= img_size);
1261 GST_LOG_OBJECT (allocator, "[%i] imported USERPTR %p plane %d size %"
1262 G_GSIZE_FORMAT, group->buffer.index, data[i], i, psize);
1264 mem = (GstV4l2Memory *) group->mem[i];
1266 mem->mem.maxsize = maxsize;
1267 mem->mem.size = psize;
1268 mem->data = data[i];
1270 group->planes[i].length = maxsize;
1271 group->planes[i].bytesused = psize;
1272 group->planes[i].m.userptr = (unsigned long) data[i];
1273 group->planes[i].data_offset = 0;
1276 /* Copy into buffer structure if not using planes */
1277 if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1278 group->buffer.bytesused = group->planes[0].bytesused;
1279 group->buffer.length = group->planes[0].length;
1280 group->buffer.m.userptr = group->planes[0].m.userptr;
1282 group->buffer.length = group->n_mem;
1289 GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
1290 n_planes, group->n_mem);
1296 gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
1300 GST_OBJECT_LOCK (allocator);
1302 if (!g_atomic_int_get (&allocator->active))
1305 for (i = 0; i < allocator->count; i++) {
1306 GstV4l2MemoryGroup *group = allocator->groups[i];
1309 if (IS_QUEUED (group->buffer)) {
1310 UNSET_QUEUED (group->buffer);
1312 gst_v4l2_allocator_reset_group (allocator, group);
1314 for (n = 0; n < group->n_mem; n++)
1315 gst_memory_unref (group->mem[n]);
1320 GST_OBJECT_UNLOCK (allocator);
1324 gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
1325 GstV4l2MemoryGroup * group)
1327 GstV4l2Object *obj = allocator->obj;
1328 gboolean ret = TRUE;
1331 g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE);
1334 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1335 for (i = 0; i < group->n_mem; i++)
1336 group->planes[i].bytesused =
1337 gst_memory_get_sizes (group->mem[i], NULL, NULL);
1339 group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL);
1342 /* Ensure the memory will stay around and is RO */
1343 for (i = 0; i < group->n_mem; i++)
1344 gst_memory_ref (group->mem[i]);
1346 if (obj->ioctl (obj->video_fd, VIDIOC_QBUF, &group->buffer) < 0) {
1347 GST_ERROR_OBJECT (allocator, "failed queueing buffer %i: %s",
1348 group->buffer.index, g_strerror (errno));
1350 /* Release the memory, possibly making it RW again */
1351 for (i = 0; i < group->n_mem; i++)
1352 gst_memory_unref (group->mem[i]);
1355 if (IS_QUEUED (group->buffer)) {
1356 GST_DEBUG_OBJECT (allocator,
1357 "driver pretends buffer is queued even if queue failed");
1358 UNSET_QUEUED (group->buffer);
1363 GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)",
1364 group->buffer.index, group->buffer.flags);
1366 if (!IS_QUEUED (group->buffer)) {
1367 GST_DEBUG_OBJECT (allocator,
1368 "driver pretends buffer is not queued even if queue succeeded");
1369 SET_QUEUED (group->buffer);
1377 gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator,
1378 GstV4l2MemoryGroup ** group_out)
1380 GstV4l2Object *obj = allocator->obj;
1381 struct v4l2_buffer buffer = { 0 };
1382 struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} };
1385 GstV4l2MemoryGroup *group = NULL;
1387 g_return_val_if_fail (g_atomic_int_get (&allocator->active), GST_FLOW_ERROR);
1389 buffer.type = obj->type;
1390 buffer.memory = allocator->memory;
1392 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1393 buffer.length = obj->format.fmt.pix_mp.num_planes;
1394 buffer.m.planes = planes;
1397 if (obj->ioctl (obj->video_fd, VIDIOC_DQBUF, &buffer) < 0)
1400 group = allocator->groups[buffer.index];
1402 if (!IS_QUEUED (group->buffer)) {
1403 GST_ERROR_OBJECT (allocator,
1404 "buffer %i was not queued, this indicate a driver bug.", buffer.index);
1405 return GST_FLOW_ERROR;
1408 group->buffer = buffer;
1410 GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index,
1413 if (IS_QUEUED (group->buffer)) {
1414 GST_DEBUG_OBJECT (allocator,
1415 "driver pretends buffer is queued even if dequeue succeeded");
1416 UNSET_QUEUED (group->buffer);
1419 if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1420 group->buffer.m.planes = group->planes;
1421 memcpy (group->planes, buffer.m.planes, sizeof (planes));
1423 group->planes[0].bytesused = group->buffer.bytesused;
1424 group->planes[0].length = group->buffer.length;
1425 g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
1426 memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
1429 /* And update memory size */
1430 if (V4L2_TYPE_IS_OUTPUT (obj->type)) {
1431 gst_v4l2_allocator_reset_size (allocator, group);
1433 /* for capture, simply read the size */
1434 for (i = 0; i < group->n_mem; i++) {
1437 GST_LOG_OBJECT (allocator,
1438 "Dequeued capture buffer, length: %u bytesused: %u data_offset: %u",
1439 group->planes[i].length, group->planes[i].bytesused,
1440 group->planes[i].data_offset);
1442 offset = group->planes[i].data_offset;
1444 if (group->planes[i].bytesused > group->planes[i].data_offset) {
1445 size = group->planes[i].bytesused - group->planes[i].data_offset;
1447 GST_WARNING_OBJECT (allocator, "V4L2 provided buffer has bytesused %"
1448 G_GUINT32_FORMAT " which is too small to include data_offset %"
1449 G_GUINT32_FORMAT, group->planes[i].bytesused,
1450 group->planes[i].data_offset);
1451 size = group->planes[i].bytesused;
1454 if (G_LIKELY (size + offset <= group->mem[i]->maxsize))
1455 gst_memory_resize (group->mem[i], offset, size);
1457 GST_WARNING_OBJECT (allocator,
1458 "v4l2 provided buffer that is too big for the memory it was "
1459 "writing into. v4l2 claims %" G_GSIZE_FORMAT " bytes used but "
1460 "memory is only %" G_GSIZE_FORMAT "B. This is probably a driver "
1461 "bug.", size, group->mem[i]->maxsize);
1462 gst_memory_resize (group->mem[i], 0, group->mem[i]->maxsize);
1467 /* Release the memory, possibly making it RW again */
1468 for (i = 0; i < group->n_mem; i++)
1469 gst_memory_unref (group->mem[i]);
1475 if (errno == EPIPE) {
1476 GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer");
1477 return GST_FLOW_EOS;
1480 GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
1481 memory_type_to_str (allocator->memory), g_strerror (errno));
1485 GST_WARNING_OBJECT (allocator,
1486 "Non-blocking I/O has been selected using O_NONBLOCK and"
1487 " no buffer was in the outgoing queue.");
1490 GST_ERROR_OBJECT (allocator,
1491 "The buffer type is not supported, or the index is out of bounds, "
1492 "or no buffers have been allocated yet, or the userptr "
1493 "or length are invalid.");
1496 GST_ERROR_OBJECT (allocator,
1497 "insufficient memory to enqueue a user pointer buffer");
1500 GST_INFO_OBJECT (allocator,
1501 "VIDIOC_DQBUF failed due to an internal error."
1502 " Can also indicate temporary problems like signal loss."
1503 " Note the driver might dequeue an (empty) buffer despite"
1504 " returning an error, or even stop capturing.");
1505 /* have we de-queued a buffer ? */
1506 if (!IS_QUEUED (buffer)) {
1507 GST_DEBUG_OBJECT (allocator, "reenqueueing buffer");
1508 /* FIXME ... should we do something here? */
1512 GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device");
1515 GST_WARNING_OBJECT (allocator,
1516 "Grabbing frame got interrupted unexpectedly. %d: %s.", errno,
1517 g_strerror (errno));
1521 return GST_FLOW_ERROR;
1525 gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
1526 GstV4l2MemoryGroup * group)
1528 switch (allocator->memory) {
1529 case V4L2_MEMORY_USERPTR:
1530 gst_v4l2_allocator_clear_userptr (allocator, group);
1532 case V4L2_MEMORY_DMABUF:
1533 gst_v4l2_allocator_clear_dmabufin (allocator, group);
1535 case V4L2_MEMORY_MMAP:
1538 g_assert_not_reached ();
1542 gst_v4l2_allocator_reset_size (allocator, group);