[v4l2videodecoder] Add new property to enable TBM output buffer
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2allocator.c
1 /*
2  * Copyright (C) 2014 Collabora Ltd.
3  *     Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
4  *
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.
9  *
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.
14  *
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.
19  *
20  */
21
22 #include "config.h"
23
24 #ifndef _GNU_SOURCE
25 # define _GNU_SOURCE            /* O_CLOEXEC */
26 #endif
27
28 #include "ext/videodev2.h"
29
30 #include "gstv4l2object.h"
31 #include "gstv4l2allocator.h"
32
33 #include <gst/allocators/gstdmabuf.h>
34
35 #include <fcntl.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/mman.h>
40 #include <unistd.h>
41 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
42 #include <tbm_surface.h>
43 #include <tbm_surface_internal.h>
44 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
45
46 #define GST_V4L2_MEMORY_TYPE "V4l2Memory"
47
48 #define gst_v4l2_allocator_parent_class parent_class
49 G_DEFINE_TYPE (GstV4l2Allocator, gst_v4l2_allocator, GST_TYPE_ALLOCATOR);
50
51 GST_DEBUG_CATEGORY_STATIC (v4l2allocator_debug);
52 #define GST_CAT_DEFAULT v4l2allocator_debug
53
54 #define UNSET_QUEUED(buffer) \
55     ((buffer).flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
56
57 #define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED)
58
59 #define IS_QUEUED(buffer) \
60     ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
61
62 enum
63 {
64   GROUP_RELEASED,
65   LAST_SIGNAL
66 };
67
68 static guint gst_v4l2_allocator_signals[LAST_SIGNAL] = { 0 };
69
70 static void gst_v4l2_allocator_release (GstV4l2Allocator * allocator,
71     GstV4l2Memory * mem);
72
73 static const gchar *
74 memory_type_to_str (guint32 memory)
75 {
76   switch (memory) {
77     case V4L2_MEMORY_MMAP:
78       return "mmap";
79     case V4L2_MEMORY_USERPTR:
80       return "userptr";
81     case V4L2_MEMORY_DMABUF:
82       return "dmabuf";
83     default:
84       return "unknown";
85   }
86 }
87
88 /*************************************/
89 /* GstV4lMemory implementation */
90 /*************************************/
91
92 static gpointer
93 _v4l2mem_map (GstV4l2Memory * mem, gsize maxsize, GstMapFlags flags)
94 {
95   gpointer data = NULL;
96
97   switch (mem->group->buffer.memory) {
98     case V4L2_MEMORY_MMAP:
99     case V4L2_MEMORY_USERPTR:
100       data = mem->data;
101       break;
102     case V4L2_MEMORY_DMABUF:
103       /* v4l2 dmabuf memory are not shared with downstream */
104       g_assert_not_reached ();
105       break;
106     default:
107       GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
108       break;
109   }
110   return data;
111 }
112
113 static gboolean
114 _v4l2mem_unmap (GstV4l2Memory * mem)
115 {
116   gboolean ret = FALSE;
117
118   switch (mem->group->buffer.memory) {
119     case V4L2_MEMORY_MMAP:
120     case V4L2_MEMORY_USERPTR:
121       ret = TRUE;
122       break;
123     case V4L2_MEMORY_DMABUF:
124       /* v4l2 dmabuf memory are not share with downstream */
125       g_assert_not_reached ();
126       break;
127     default:
128       GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
129       break;
130   }
131   return ret;
132 }
133
134 static gboolean
135 _v4l2mem_dispose (GstV4l2Memory * mem)
136 {
137   GstV4l2Allocator *allocator = (GstV4l2Allocator *) mem->mem.allocator;
138   GstV4l2MemoryGroup *group = mem->group;
139   gboolean ret;
140
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);
145     ret = FALSE;
146   } else {
147     gst_object_ref (allocator);
148     ret = TRUE;
149   }
150
151   return ret;
152 }
153
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)
158 {
159   GstV4l2Memory *mem;
160
161   mem = g_slice_new0 (GstV4l2Memory);
162   gst_memory_init (GST_MEMORY_CAST (mem),
163       flags, allocator, parent, maxsize, align, offset, size);
164
165   if (parent == NULL)
166     mem->mem.mini_object.dispose =
167         (GstMiniObjectDisposeFunction) _v4l2mem_dispose;
168
169   mem->plane = plane;
170   mem->data = data;
171   mem->dmafd = dmafd;
172   mem->group = group;
173
174   return mem;
175 }
176
177 static GstV4l2Memory *
178 _v4l2mem_share (GstV4l2Memory * mem, gssize offset, gsize size)
179 {
180   GstV4l2Memory *sub;
181   GstMemory *parent;
182
183   /* find the real parent */
184   if ((parent = mem->mem.parent) == NULL)
185     parent = (GstMemory *) mem;
186
187   if (size == -1)
188     size = mem->mem.size - offset;
189
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,
194       -1, mem->group);
195
196   return sub;
197 }
198
199 static gboolean
200 _v4l2mem_is_span (GstV4l2Memory * mem1, GstV4l2Memory * mem2, gsize * offset)
201 {
202   if (offset)
203     *offset = mem1->mem.offset - mem1->mem.parent->offset;
204
205   /* and memory is contiguous */
206   return mem1->mem.offset + mem1->mem.size == mem2->mem.offset;
207 }
208
209 gboolean
210 gst_is_v4l2_memory (GstMemory * mem)
211 {
212   return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE);
213 }
214
215 GQuark
216 gst_v4l2_memory_quark (void)
217 {
218   static GQuark quark = 0;
219
220   if (quark == 0)
221     quark = g_quark_from_string ("GstV4l2Memory");
222
223   return quark;
224 }
225
226
227 /*************************************/
228 /* GstV4l2MemoryGroup implementation */
229 /*************************************/
230
231 static void
232 gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
233 {
234   gint i;
235
236   for (i = 0; i < group->n_mem; i++) {
237     GstMemory *mem = group->mem[i];
238     group->mem[i] = NULL;
239     if (mem)
240       gst_memory_unref (mem);
241   }
242 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
243   if (group->surface) {
244     GST_INFO ("unref surface[%p]", group->surface);
245     tbm_surface_destroy (group->surface);
246     group->surface = NULL;
247   }
248 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
249
250   g_slice_free (GstV4l2MemoryGroup, group);
251 }
252
253 static GstV4l2MemoryGroup *
254 gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
255 {
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;
261
262   group = g_slice_new0 (GstV4l2MemoryGroup);
263
264   group->buffer.type = format->type;
265   group->buffer.index = index;
266   group->buffer.memory = memory;
267
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;
271   } else {
272     group->n_mem = 1;
273   }
274
275   if (obj->ioctl (obj->video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0)
276     goto querybuf_failed;
277
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 "
281         "libv4l2");
282     g_slice_free (GstV4l2MemoryGroup, group);
283     return NULL;
284   }
285
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)) {
289     gint i;
290
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;
296     }
297   } else {
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;
302   }
303
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));
312   }
313
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);
321
322 #ifndef GST_DISABLE_GST_DEBUG
323   if (memory == V4L2_MEMORY_MMAP) {
324     gint i;
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);
332     }
333   }
334 #endif
335
336   return group;
337
338 querybuf_failed:
339   {
340     GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno));
341     goto failed;
342   }
343 buffer_too_short:
344   {
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.",
348         buf_size, img_size);
349     goto failed;
350   }
351 failed:
352   gst_v4l2_memory_group_free (group);
353   return NULL;
354 }
355
356
357 /*************************************/
358 /* GstV4lAllocator implementation    */
359 /*************************************/
360
361 static void
362 gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
363 {
364   GstV4l2MemoryGroup *group = mem->group;
365
366   GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
367       mem->plane, group->buffer.index);
368
369   switch (allocator->memory) {
370     case V4L2_MEMORY_DMABUF:
371       close (mem->dmafd);
372       mem->dmafd = -1;
373       break;
374     case V4L2_MEMORY_USERPTR:
375       mem->data = NULL;
376       break;
377     default:
378       break;
379   }
380
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);
386   }
387
388   /* Keep last, allocator may be freed after this call */
389   g_object_unref (allocator);
390 }
391
392 static void
393 gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
394 {
395   GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator;
396   GstV4l2Object *obj = allocator->obj;
397   GstV4l2Memory *mem = (GstV4l2Memory *) gmem;
398   GstV4l2MemoryGroup *group = mem->group;
399
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);
404
405     if (allocator->memory == V4L2_MEMORY_MMAP) {
406       if (mem->data)
407         obj->munmap (mem->data, group->planes[mem->plane].length);
408     }
409
410     /* This apply for both mmap with expbuf, and dmabuf imported memory */
411     if (mem->dmafd >= 0)
412       close (mem->dmafd);
413   }
414
415   g_slice_free (GstV4l2Memory, mem);
416 }
417
418 static void
419 gst_v4l2_allocator_dispose (GObject * obj)
420 {
421   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
422   gint i;
423
424   GST_LOG_OBJECT (obj, "called");
425
426   for (i = 0; i < allocator->count; i++) {
427     GstV4l2MemoryGroup *group = allocator->groups[i];
428     allocator->groups[i] = NULL;
429     if (group)
430       gst_v4l2_memory_group_free (group);
431   }
432
433   G_OBJECT_CLASS (parent_class)->dispose (obj);
434 }
435
436 static void
437 gst_v4l2_allocator_finalize (GObject * obj)
438 {
439   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
440
441   GST_LOG_OBJECT (obj, "called");
442 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
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;
447   }
448 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
449
450   gst_atomic_queue_unref (allocator->free_queue);
451   gst_object_unref (allocator->obj->element);
452
453   G_OBJECT_CLASS (parent_class)->finalize (obj);
454 }
455
456 static void
457 gst_v4l2_allocator_class_init (GstV4l2AllocatorClass * klass)
458 {
459   GObjectClass *object_class;
460   GstAllocatorClass *allocator_class;
461
462   allocator_class = (GstAllocatorClass *) klass;
463   object_class = (GObjectClass *) klass;
464
465   allocator_class->alloc = NULL;
466   allocator_class->free = gst_v4l2_allocator_free;
467
468   object_class->dispose = gst_v4l2_allocator_dispose;
469   object_class->finalize = gst_v4l2_allocator_finalize;
470
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,
473       G_TYPE_NONE, 0);
474
475   GST_DEBUG_CATEGORY_INIT (v4l2allocator_debug, "v4l2allocator", 0,
476       "V4L2 Allocator");
477 }
478
479 static void
480 gst_v4l2_allocator_init (GstV4l2Allocator * allocator)
481 {
482   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
483
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 */
490
491   allocator->free_queue = gst_atomic_queue_new (VIDEO_MAX_FRAME);
492
493   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
494 }
495
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)
500 static guint32
501 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
502     guint32 breq_flag, guint32 bcreate_flag)
503 {
504   GstV4l2Object *obj = allocator->obj;
505   struct v4l2_requestbuffers breq = { 0 };
506   guint32 flags = 0;
507
508   breq.type = obj->type;
509   breq.count = 0;
510   breq.memory = memory;
511
512   if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) == 0) {
513     struct v4l2_create_buffers bcreate = { 0 };
514
515     flags |= breq_flag;
516
517     bcreate.memory = memory;
518     bcreate.format = obj->format;
519
520     if ((obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0))
521       flags |= bcreate_flag;
522   }
523
524   if (breq.capabilities & V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS)
525     flags |= GST_V4L2_ALLOCATOR_FLAG_SUPPORTS_ORPHANED_BUFS;
526
527   return flags;
528 }
529
530 static GstV4l2MemoryGroup *
531 gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
532 {
533   GstV4l2Object *obj = allocator->obj;
534   struct v4l2_create_buffers bcreate = { 0 };
535   GstV4l2MemoryGroup *group = NULL;
536
537   GST_OBJECT_LOCK (allocator);
538
539   if (!g_atomic_int_get (&allocator->active))
540     goto done;
541
542   if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator))
543     goto orphaned_bug;
544
545   bcreate.memory = allocator->memory;
546   bcreate.format = obj->format;
547   bcreate.count = 1;
548
549   if (!allocator->can_allocate)
550     goto done;
551
552   if (obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0)
553     goto create_bufs_failed;
554
555   if (allocator->groups[bcreate.index] != NULL)
556     goto create_bufs_bug;
557
558   group = gst_v4l2_memory_group_new (allocator, bcreate.index);
559
560   if (group) {
561     allocator->groups[bcreate.index] = group;
562     allocator->count++;
563   }
564
565 done:
566   GST_OBJECT_UNLOCK (allocator);
567   return group;
568
569 orphaned_bug:
570   {
571     GST_ERROR_OBJECT (allocator, "allocator was orphaned, "
572         "not creating new buffers");
573     goto done;
574   }
575 create_bufs_failed:
576   {
577     GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s",
578         g_strerror (errno));
579     goto done;
580   }
581 create_bufs_bug:
582   {
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",
585         bcreate.index);
586     goto done;
587   }
588 }
589
590 static GstV4l2MemoryGroup *
591 gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator)
592 {
593   GstV4l2MemoryGroup *group;
594
595   if (!g_atomic_int_get (&allocator->active))
596     return NULL;
597
598   group = gst_atomic_queue_pop (allocator->free_queue);
599
600   if (group == NULL) {
601     if (allocator->can_allocate) {
602       group = gst_v4l2_allocator_create_buf (allocator);
603
604       /* Don't hammer on CREATE_BUFS */
605       if (group == NULL)
606         allocator->can_allocate = FALSE;
607     }
608   }
609
610   return group;
611 }
612
613 static void
614 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
615     GstV4l2MemoryGroup * group)
616 {
617   gint i;
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;
622   }
623 }
624
625 static void
626 _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
627 {
628   if (group->mems_allocated > 0) {
629     gint i;
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]);
635   } else {
636     /* Otherwise, group has to be on free queue for _stop() to work */
637     gst_atomic_queue_push (allocator->free_queue, group);
638   }
639 }
640
641 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
642 static tbm_format __get_tbm_format (GstVideoFormat gst_format)
643 {
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:
650   default:
651     return TBM_FORMAT_YUV420;
652   }
653 }
654 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
655
656 GstV4l2Allocator *
657 gst_v4l2_allocator_new (GstObject * parent, GstV4l2Object * v4l2object)
658 {
659   GstV4l2Allocator *allocator;
660   guint32 flags = 0;
661   gchar *name, *parent_name;
662
663   parent_name = gst_object_get_name (parent);
664   name = g_strconcat (parent_name, ":allocator", NULL);
665   g_free (parent_name);
666
667   allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL);
668   gst_object_ref_sink (allocator);
669   g_free (name);
670
671   /* Save everything */
672   allocator->obj = v4l2object;
673
674   /* Keep a ref on the elemnt so obj does not disapear */
675   gst_object_ref (allocator->obj->element);
676
677   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP);
678   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR);
679   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF);
680
681
682   if (flags == 0) {
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;
690   }
691
692   GST_OBJECT_FLAG_SET (allocator, flags);
693
694 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
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);
701
702     tmp_surface = tbm_surface_create (width, height,
703         __get_tbm_format (GST_VIDEO_INFO_FORMAT (&v4l2object->info)));
704     if (tmp_surface) {
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);
709     } else {
710       GST_ERROR_OBJECT (allocator, "[%dx%d] surface failed", width, height);
711     }
712
713     allocator->bufmgr = tbm_bufmgr_init (-1);
714     if (!allocator->bufmgr) {
715       GST_ERROR_OBJECT (allocator, "tbm bufmgr failed");
716       gst_object_unref (allocator);
717       return NULL;
718     }
719   }
720 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
721   return allocator;
722 }
723
724 guint
725 gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
726     guint32 memory)
727 {
728   GstV4l2Object *obj = allocator->obj;
729   struct v4l2_requestbuffers breq = { count, obj->type, memory };
730   gboolean can_allocate;
731   gint i;
732
733   g_return_val_if_fail (count != 0, 0);
734
735   GST_OBJECT_LOCK (allocator);
736
737   if (g_atomic_int_get (&allocator->active))
738     goto already_active;
739
740   if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator))
741     goto orphaned;
742
743   if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
744     goto reqbufs_failed;
745
746   if (breq.count < 1)
747     goto out_of_memory;
748
749   switch (memory) {
750     case V4L2_MEMORY_MMAP:
751       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, MMAP);
752       break;
753     case V4L2_MEMORY_USERPTR:
754       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, USERPTR);
755       break;
756     case V4L2_MEMORY_DMABUF:
757       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, DMABUF);
758       break;
759     default:
760       can_allocate = FALSE;
761       break;
762   }
763
764   GST_DEBUG_OBJECT (allocator, "allocated %u %s buffers out of %u requested",
765       breq.count, memory_type_to_str (memory), count);
766
767   allocator->can_allocate = can_allocate;
768   allocator->count = breq.count;
769   allocator->memory = memory;
770
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)
775       goto error;
776
777     gst_atomic_queue_push (allocator->free_queue, allocator->groups[i]);
778   }
779
780   g_atomic_int_set (&allocator->active, TRUE);
781
782 done:
783   GST_OBJECT_UNLOCK (allocator);
784   return breq.count;
785
786 already_active:
787   {
788     GST_ERROR_OBJECT (allocator, "allocator already active");
789     goto error;
790   }
791 orphaned:
792   {
793     GST_ERROR_OBJECT (allocator, "allocator was orphaned");
794     goto error;
795   }
796 reqbufs_failed:
797   {
798     GST_ERROR_OBJECT (allocator,
799         "error requesting %d buffers: %s", count, g_strerror (errno));
800     goto error;
801   }
802 out_of_memory:
803   {
804     GST_ERROR_OBJECT (allocator, "Not enough memory to allocate buffers");
805     goto error;
806   }
807 error:
808   {
809     breq.count = 0;
810     goto done;
811   }
812 }
813
814 GstV4l2Return
815 gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
816 {
817   GstV4l2Object *obj = allocator->obj;
818   struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory };
819   gint i = 0;
820   GstV4l2Return ret = GST_V4L2_OK;
821
822   GST_DEBUG_OBJECT (allocator, "stop allocator");
823
824   GST_OBJECT_LOCK (allocator);
825
826   if (!g_atomic_int_get (&allocator->active))
827     goto done;
828
829   if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) {
830     GST_DEBUG_OBJECT (allocator, "allocator is still in use");
831     ret = GST_V4L2_BUSY;
832     goto done;
833   }
834
835   while (gst_atomic_queue_pop (allocator->free_queue)) {
836     /* nothing */
837   };
838
839   for (i = 0; i < allocator->count; i++) {
840     GstV4l2MemoryGroup *group = allocator->groups[i];
841     allocator->groups[i] = NULL;
842     if (group)
843       gst_v4l2_memory_group_free (group);
844   }
845
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));
851   }
852
853   allocator->count = 0;
854
855   g_atomic_int_set (&allocator->active, FALSE);
856
857 done:
858   GST_OBJECT_UNLOCK (allocator);
859   return ret;
860 }
861
862 gboolean
863 gst_v4l2_allocator_orphan (GstV4l2Allocator * allocator)
864 {
865   GstV4l2Object *obj = allocator->obj;
866   struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory };
867
868   if (!GST_V4L2_ALLOCATOR_CAN_ORPHAN_BUFS (allocator))
869     return FALSE;
870
871   GST_OBJECT_FLAG_SET (allocator, GST_V4L2_ALLOCATOR_FLAG_ORPHANED);
872
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));
876     return FALSE;
877   }
878
879   return TRUE;
880 }
881
882 GstV4l2MemoryGroup *
883 gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
884 {
885   GstV4l2Object *obj = allocator->obj;
886   GstV4l2MemoryGroup *group;
887   gint i;
888
889   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
890
891   group = gst_v4l2_allocator_alloc (allocator);
892
893   if (group == NULL)
894     return NULL;
895
896   for (i = 0; i < group->n_mem; i++) {
897     if (group->mem[i] == NULL) {
898       gpointer data;
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);
901
902       if (data == MAP_FAILED)
903         goto mmap_failed;
904
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);
908
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,
911           -1, group);
912     } else {
913       /* Take back the allocator reference */
914       gst_object_ref (allocator);
915     }
916
917     group->mems_allocated++;
918   }
919
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
923    * simplicity */
924   gst_v4l2_allocator_reset_size (allocator, group);
925
926   return group;
927
928 mmap_failed:
929   {
930     GST_ERROR_OBJECT (allocator, "Failed to mmap buffer: %s",
931         g_strerror (errno));
932     _cleanup_failed_alloc (allocator, group);
933     return NULL;
934   }
935 }
936
937 GstV4l2MemoryGroup *
938 gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
939     GstAllocator * dmabuf_allocator)
940 {
941   GstV4l2Object *obj = allocator->obj;
942   GstV4l2MemoryGroup *group;
943   gint i;
944 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
945   tbm_bo bos[VIDEO_MAX_PLANES] = {NULL, };
946 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
947
948   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
949
950   group = gst_v4l2_allocator_alloc (allocator);
951
952   if (group == NULL)
953     return NULL;
954
955   for (i = 0; i < group->n_mem; i++) {
956     GstV4l2Memory *mem;
957     GstMemory *dma_mem;
958
959     if (group->mem[i] == NULL) {
960       struct v4l2_exportbuffer expbuf = { 0 };
961
962       expbuf.type = obj->type;
963       expbuf.index = group->buffer.index;
964       expbuf.plane = i;
965       expbuf.flags = O_CLOEXEC | O_RDWR;
966
967       if (obj->ioctl (obj->video_fd, VIDIOC_EXPBUF, &expbuf) < 0)
968         goto expbuf_failed;
969
970       GST_LOG_OBJECT (allocator, "exported DMABUF as fd %i plane %d",
971           expbuf.fd, i);
972
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,
976           expbuf.fd, group);
977 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
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]);
982       }
983 #endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */
984     } else {
985       /* Take back the allocator reference */
986       gst_object_ref (allocator);
987     }
988
989     group->mems_allocated++;
990
991     g_assert (gst_is_v4l2_memory (group->mem[i]));
992     mem = (GstV4l2Memory *) group->mem[i];
993
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);
998
999     gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem),
1000         GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref);
1001
1002     group->mem[i] = dma_mem;
1003   }
1004
1005 #ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
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);
1009   }
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_TBM_SUPPORT_FOR_V4L2_DECODER */
1014
1015   gst_v4l2_allocator_reset_size (allocator, group);
1016
1017   return group;
1018
1019 expbuf_failed:
1020   {
1021     GST_ERROR_OBJECT (allocator, "Failed to export DMABUF: %s",
1022         g_strerror (errno));
1023     goto cleanup;
1024   }
1025 cleanup:
1026   {
1027     _cleanup_failed_alloc (allocator, group);
1028     return NULL;
1029   }
1030 }
1031
1032 static void
1033 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
1034     GstV4l2MemoryGroup * group)
1035 {
1036   GstV4l2Object *obj = allocator->obj;
1037   GstV4l2Memory *mem;
1038   gint i;
1039
1040   g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
1041
1042   for (i = 0; i < group->n_mem; i++) {
1043
1044     mem = (GstV4l2Memory *) group->mem[i];
1045
1046     GST_LOG_OBJECT (allocator, "[%i] clearing DMABUF import, fd %i plane %d",
1047         group->buffer.index, mem->dmafd, i);
1048
1049     /* Update memory */
1050     mem->mem.maxsize = 0;
1051     mem->mem.offset = 0;
1052     mem->mem.size = 0;
1053     mem->dmafd = -1;
1054
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;
1060   }
1061
1062   if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1063     group->buffer.bytesused = 0;
1064     group->buffer.length = 0;
1065     group->buffer.m.fd = -1;
1066   }
1067 }
1068
1069 GstV4l2MemoryGroup *
1070 gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
1071 {
1072   GstV4l2MemoryGroup *group;
1073   gint i;
1074
1075   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
1076
1077   group = gst_v4l2_allocator_alloc (allocator);
1078
1079   if (group == NULL)
1080     return NULL;
1081
1082   GST_LOG_OBJECT (allocator, "allocating empty DMABUF import group");
1083
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);
1088     } else {
1089       /* Take back the allocator reference */
1090       gst_object_ref (allocator);
1091     }
1092
1093     group->mems_allocated++;
1094   }
1095
1096   gst_v4l2_allocator_clear_dmabufin (allocator, group);
1097
1098   return group;
1099 }
1100
1101 static void
1102 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
1103     GstV4l2MemoryGroup * group)
1104 {
1105   GstV4l2Object *obj = allocator->obj;
1106   GstV4l2Memory *mem;
1107   gint i;
1108
1109   g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
1110
1111   for (i = 0; i < group->n_mem; i++) {
1112     mem = (GstV4l2Memory *) group->mem[i];
1113
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);
1116
1117     mem->mem.maxsize = 0;
1118     mem->mem.size = 0;
1119     mem->data = NULL;
1120
1121     group->planes[i].length = 0;
1122     group->planes[i].bytesused = 0;
1123     group->planes[i].m.userptr = 0;
1124   }
1125
1126   if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1127     group->buffer.bytesused = 0;
1128     group->buffer.length = 0;
1129     group->buffer.m.userptr = 0;
1130   }
1131 }
1132
1133 GstV4l2MemoryGroup *
1134 gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
1135 {
1136   GstV4l2MemoryGroup *group;
1137   gint i;
1138
1139   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
1140
1141   group = gst_v4l2_allocator_alloc (allocator);
1142
1143   if (group == NULL)
1144     return NULL;
1145
1146   GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
1147
1148   for (i = 0; i < group->n_mem; i++) {
1149
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);
1153     } else {
1154       /* Take back the allocator reference */
1155       gst_object_ref (allocator);
1156     }
1157
1158     group->mems_allocated++;
1159   }
1160
1161   gst_v4l2_allocator_clear_userptr (allocator, group);
1162
1163   return group;
1164 }
1165
1166 gboolean
1167 gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
1168     GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
1169 {
1170   GstV4l2Object *obj = allocator->obj;
1171   GstV4l2Memory *mem;
1172   gint i;
1173
1174   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
1175
1176   if (group->n_mem != n_mem)
1177     goto n_mem_missmatch;
1178
1179   for (i = 0; i < group->n_mem; i++) {
1180     gint dmafd;
1181     gsize size, offset, maxsize;
1182
1183     if (!gst_is_dmabuf_memory (dma_mem[i]))
1184       goto not_dmabuf;
1185
1186     size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
1187
1188     dmafd = gst_dmabuf_memory_get_fd (dma_mem[i]);
1189
1190     GST_LOG_OBJECT (allocator, "[%i] imported DMABUF as fd %i plane %d",
1191         group->buffer.index, dmafd, i);
1192
1193     mem = (GstV4l2Memory *) group->mem[i];
1194
1195     /* Update memory */
1196     mem->mem.maxsize = maxsize;
1197     mem->mem.offset = offset;
1198     mem->mem.size = size;
1199     mem->dmafd = dmafd;
1200
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;
1206   }
1207
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;
1213
1214     /* FIXME Check if data_offset > 0 and fail for non-multi-planar */
1215     g_assert (group->planes[0].data_offset == 0);
1216   } else {
1217     group->buffer.length = group->n_mem;
1218   }
1219
1220   return TRUE;
1221
1222 n_mem_missmatch:
1223   {
1224     GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
1225         group->n_mem);
1226     return FALSE;
1227   }
1228 not_dmabuf:
1229   {
1230     GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
1231     return FALSE;
1232   }
1233 }
1234
1235 gboolean
1236 gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
1237     GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
1238     gpointer * data, gsize * size)
1239 {
1240   GstV4l2Object *obj = allocator->obj;
1241   GstV4l2Memory *mem;
1242   gint i;
1243
1244   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
1245
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;
1249
1250   for (i = 0; i < group->n_mem; i++) {
1251     gsize maxsize, psize;
1252
1253     /* TODO request used size and maxsize seperatly */
1254     if (V4L2_TYPE_IS_MULTIPLANAR (obj->type))
1255       maxsize = psize = size[i];
1256     else
1257       maxsize = psize = img_size;
1258
1259     g_assert (psize <= img_size);
1260
1261     GST_LOG_OBJECT (allocator, "[%i] imported USERPTR %p plane %d size %"
1262         G_GSIZE_FORMAT, group->buffer.index, data[i], i, psize);
1263
1264     mem = (GstV4l2Memory *) group->mem[i];
1265
1266     mem->mem.maxsize = maxsize;
1267     mem->mem.size = psize;
1268     mem->data = data[i];
1269
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;
1274   }
1275
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;
1281   } else {
1282     group->buffer.length = group->n_mem;
1283   }
1284
1285   return TRUE;
1286
1287 n_mem_missmatch:
1288   {
1289     GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
1290         n_planes, group->n_mem);
1291     return FALSE;
1292   }
1293 }
1294
1295 void
1296 gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
1297 {
1298   gint i;
1299
1300   GST_OBJECT_LOCK (allocator);
1301
1302   if (!g_atomic_int_get (&allocator->active))
1303     goto done;
1304
1305   for (i = 0; i < allocator->count; i++) {
1306     GstV4l2MemoryGroup *group = allocator->groups[i];
1307     gint n;
1308
1309     if (IS_QUEUED (group->buffer)) {
1310       UNSET_QUEUED (group->buffer);
1311
1312       gst_v4l2_allocator_reset_group (allocator, group);
1313
1314       for (n = 0; n < group->n_mem; n++)
1315         gst_memory_unref (group->mem[n]);
1316     }
1317   }
1318
1319 done:
1320   GST_OBJECT_UNLOCK (allocator);
1321 }
1322
1323 gboolean
1324 gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
1325     GstV4l2MemoryGroup * group)
1326 {
1327   GstV4l2Object *obj = allocator->obj;
1328   gboolean ret = TRUE;
1329   gint i;
1330
1331   g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE);
1332
1333   /* update sizes */
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);
1338   } else {
1339     group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL);
1340   }
1341
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]);
1345
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));
1349
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]);
1353
1354     ret = FALSE;
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);
1359     }
1360     goto done;
1361   }
1362
1363   GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)",
1364       group->buffer.index, group->buffer.flags);
1365
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);
1370   }
1371
1372 done:
1373   return ret;
1374 }
1375
1376 GstFlowReturn
1377 gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator,
1378     GstV4l2MemoryGroup ** group_out)
1379 {
1380   GstV4l2Object *obj = allocator->obj;
1381   struct v4l2_buffer buffer = { 0 };
1382   struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} };
1383   gint i;
1384
1385   GstV4l2MemoryGroup *group = NULL;
1386
1387   g_return_val_if_fail (g_atomic_int_get (&allocator->active), GST_FLOW_ERROR);
1388
1389   buffer.type = obj->type;
1390   buffer.memory = allocator->memory;
1391
1392   if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1393     buffer.length = obj->format.fmt.pix_mp.num_planes;
1394     buffer.m.planes = planes;
1395   }
1396
1397   if (obj->ioctl (obj->video_fd, VIDIOC_DQBUF, &buffer) < 0)
1398     goto error;
1399
1400   group = allocator->groups[buffer.index];
1401
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;
1406   }
1407
1408   group->buffer = buffer;
1409
1410   GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index,
1411       buffer.flags);
1412
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);
1417   }
1418
1419   if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
1420     group->buffer.m.planes = group->planes;
1421     memcpy (group->planes, buffer.m.planes, sizeof (planes));
1422   } else {
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));
1427   }
1428
1429   /* And update memory size */
1430   if (V4L2_TYPE_IS_OUTPUT (obj->type)) {
1431     gst_v4l2_allocator_reset_size (allocator, group);
1432   } else {
1433     /* for capture, simply read the size */
1434     for (i = 0; i < group->n_mem; i++) {
1435       gsize size, offset;
1436
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);
1441
1442       offset = group->planes[i].data_offset;
1443
1444       if (group->planes[i].bytesused > group->planes[i].data_offset) {
1445         size = group->planes[i].bytesused - group->planes[i].data_offset;
1446       } else {
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;
1452       }
1453
1454       if (G_LIKELY (size + offset <= group->mem[i]->maxsize))
1455         gst_memory_resize (group->mem[i], offset, size);
1456       else {
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);
1463       }
1464     }
1465   }
1466
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]);
1470
1471   *group_out = group;
1472   return GST_FLOW_OK;
1473
1474 error:
1475   if (errno == EPIPE) {
1476     GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer");
1477     return GST_FLOW_EOS;
1478   }
1479
1480   GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
1481       memory_type_to_str (allocator->memory), g_strerror (errno));
1482
1483   switch (errno) {
1484     case EAGAIN:
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.");
1488       break;
1489     case EINVAL:
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.");
1494       break;
1495     case ENOMEM:
1496       GST_ERROR_OBJECT (allocator,
1497           "insufficient memory to enqueue a user pointer buffer");
1498       break;
1499     case EIO:
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? */
1509       }
1510       break;
1511     case EINTR:
1512       GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device");
1513       break;
1514     default:
1515       GST_WARNING_OBJECT (allocator,
1516           "Grabbing frame got interrupted unexpectedly. %d: %s.", errno,
1517           g_strerror (errno));
1518       break;
1519   }
1520
1521   return GST_FLOW_ERROR;
1522 }
1523
1524 void
1525 gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
1526     GstV4l2MemoryGroup * group)
1527 {
1528   switch (allocator->memory) {
1529     case V4L2_MEMORY_USERPTR:
1530       gst_v4l2_allocator_clear_userptr (allocator, group);
1531       break;
1532     case V4L2_MEMORY_DMABUF:
1533       gst_v4l2_allocator_clear_dmabufin (allocator, group);
1534       break;
1535     case V4L2_MEMORY_MMAP:
1536       break;
1537     default:
1538       g_assert_not_reached ();
1539       break;
1540   }
1541
1542   gst_v4l2_allocator_reset_size (allocator, group);
1543 }