v4l2allocator: Workaround driver that don't support REQBUFS(0)
[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 #include "ext/videodev2.h"
25 #include "gstv4l2allocator.h"
26 #include "v4l2_calls.h"
27
28 #include <gst/allocators/gstdmabuf.h>
29
30 #include <fcntl.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/mman.h>
35
36 #define GST_V4L2_MEMORY_TYPE "V4l2Memory"
37
38 #define gst_v4l2_allocator_parent_class parent_class
39 G_DEFINE_TYPE (GstV4l2Allocator, gst_v4l2_allocator, GST_TYPE_ALLOCATOR);
40
41 GST_DEBUG_CATEGORY_STATIC (v4l2allocator_debug);
42 #define GST_CAT_DEFAULT v4l2allocator_debug
43
44 #define UNSET_QUEUED(buffer) \
45     ((buffer).flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
46
47 #define SET_QUEUED(buffer) ((buffer).flags |= V4L2_BUF_FLAG_QUEUED)
48
49 #define IS_QUEUED(buffer) \
50     ((buffer).flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
51
52 enum
53 {
54   GROUP_RELEASED,
55   LAST_SIGNAL
56 };
57
58 static guint gst_v4l2_allocator_signals[LAST_SIGNAL] = { 0 };
59
60 static void gst_v4l2_allocator_release (GstV4l2Allocator * allocator,
61     GstV4l2Memory * mem);
62
63 static const gchar *
64 memory_type_to_str (guint32 memory)
65 {
66   switch (memory) {
67     case V4L2_MEMORY_MMAP:
68       return "mmap";
69     case V4L2_MEMORY_USERPTR:
70       return "userptr";
71     case V4L2_MEMORY_DMABUF:
72       return "dmabuf";
73     default:
74       return "unknown";
75   }
76 }
77
78 /*************************************/
79 /* GstV4lMemory implementation */
80 /*************************************/
81
82 static gpointer
83 _v4l2mem_map (GstV4l2Memory * mem, gsize maxsize, GstMapFlags flags)
84 {
85   gpointer data = NULL;
86
87   switch (mem->group->buffer.memory) {
88     case V4L2_MEMORY_MMAP:
89     case V4L2_MEMORY_USERPTR:
90       data = mem->data;
91       break;
92     case V4L2_MEMORY_DMABUF:
93       /* v4l2 dmabuf memory are not shared with downstream */
94       g_assert_not_reached ();
95       break;
96     default:
97       GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
98       break;
99   }
100   return data;
101 }
102
103 static gboolean
104 _v4l2mem_unmap (GstV4l2Memory * mem)
105 {
106   gboolean ret = FALSE;
107
108   switch (mem->group->buffer.memory) {
109     case V4L2_MEMORY_MMAP:
110     case V4L2_MEMORY_USERPTR:
111       ret = TRUE;
112       break;
113     case V4L2_MEMORY_DMABUF:
114       /* v4l2 dmabuf memory are not share with downstream */
115       g_assert_not_reached ();
116       break;
117     default:
118       GST_WARNING ("Unknown memory type %i", mem->group->buffer.memory);
119       break;
120   }
121   return ret;
122 }
123
124 static gboolean
125 _v4l2mem_dispose (GstV4l2Memory * mem)
126 {
127   GstV4l2Allocator *allocator = (GstV4l2Allocator *) mem->mem.allocator;
128   GstV4l2MemoryGroup *group = mem->group;
129   gboolean ret;
130
131   if (group->mem[mem->plane]) {
132     /* We may have a dmabuf, replace it with returned original memory */
133     group->mem[mem->plane] = gst_memory_ref ((GstMemory *) mem);
134     gst_v4l2_allocator_release (allocator, mem);
135     ret = FALSE;
136   } else {
137     gst_object_ref (allocator);
138     ret = TRUE;
139   }
140
141   return ret;
142 }
143
144 static void
145 _v4l2mem_free (GstV4l2Memory * mem)
146 {
147   if (mem->dmafd >= 0)
148     close (mem->dmafd);
149   g_slice_free (GstV4l2Memory, mem);
150 }
151
152 static inline GstV4l2Memory *
153 _v4l2mem_new (GstMemoryFlags flags, GstAllocator * allocator,
154     GstMemory * parent, gsize maxsize, gsize align, gsize offset, gsize size,
155     gint plane, gpointer data, int dmafd, GstV4l2MemoryGroup * group)
156 {
157   GstV4l2Memory *mem;
158
159   mem = g_slice_new0 (GstV4l2Memory);
160   gst_memory_init (GST_MEMORY_CAST (mem),
161       flags, allocator, parent, maxsize, align, offset, size);
162
163   if (parent == NULL)
164     mem->mem.mini_object.dispose =
165         (GstMiniObjectDisposeFunction) _v4l2mem_dispose;
166
167   mem->plane = plane;
168   mem->data = data;
169   mem->dmafd = dmafd;
170   mem->group = group;
171
172   return mem;
173 }
174
175 static GstV4l2Memory *
176 _v4l2mem_share (GstV4l2Memory * mem, gssize offset, gsize size)
177 {
178   GstV4l2Memory *sub;
179   GstMemory *parent;
180
181   /* find the real parent */
182   if ((parent = mem->mem.parent) == NULL)
183     parent = (GstMemory *) mem;
184
185   if (size == -1)
186     size = mem->mem.size - offset;
187
188   /* the shared memory is always readonly */
189   sub = _v4l2mem_new (GST_MINI_OBJECT_FLAGS (parent) |
190       GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->mem.allocator, parent,
191       mem->mem.maxsize, mem->mem.align, offset, size, mem->plane, mem->data,
192       -1, mem->group);
193
194   return sub;
195 }
196
197 static gboolean
198 _v4l2mem_is_span (GstV4l2Memory * mem1, GstV4l2Memory * mem2, gsize * offset)
199 {
200   if (offset)
201     *offset = mem1->mem.offset - mem1->mem.parent->offset;
202
203   /* and memory is contiguous */
204   return mem1->mem.offset + mem1->mem.size == mem2->mem.offset;
205 }
206
207 gboolean
208 gst_is_v4l2_memory (GstMemory * mem)
209 {
210   return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE);
211 }
212
213 GQuark
214 gst_v4l2_memory_quark (void)
215 {
216   static GQuark quark = 0;
217
218   if (quark == 0)
219     quark = g_quark_from_string ("GstV4l2Memory");
220
221   return quark;
222 }
223
224
225 /*************************************/
226 /* GstV4l2MemoryGroup implementation */
227 /*************************************/
228
229 static void
230 gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
231 {
232   gint i;
233
234   for (i = 0; i < group->n_mem; i++) {
235     GstMemory *mem = group->mem[i];
236     group->mem[i] = NULL;
237     if (mem)
238       gst_memory_unref (mem);
239   }
240
241   g_slice_free (GstV4l2MemoryGroup, group);
242 }
243
244 static GstV4l2MemoryGroup *
245 gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
246 {
247   gint video_fd = allocator->video_fd;
248   guint32 memory = allocator->memory;
249   struct v4l2_format *format = &allocator->format;
250   GstV4l2MemoryGroup *group;
251   gsize img_size, buf_size;
252
253   group = g_slice_new0 (GstV4l2MemoryGroup);
254
255   group->buffer.type = format->type;
256   group->buffer.index = index;
257   group->buffer.memory = memory;
258
259   if (V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
260     group->n_mem = group->buffer.length = format->fmt.pix_mp.num_planes;
261     group->buffer.m.planes = group->planes;
262   } else {
263     group->n_mem = 1;
264   }
265
266   if (v4l2_ioctl (video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0)
267     goto querybuf_failed;
268
269   /* Check that provided size matches the format we have negotiation. Failing
270    * there usually means a driver of libv4l bug. */
271   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
272     gint i;
273
274     for (i = 0; i < group->n_mem; i++) {
275       img_size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage;
276       buf_size = group->planes[i].length;
277       if (buf_size < img_size)
278         goto buffer_too_short;
279     }
280   } else {
281     img_size = allocator->format.fmt.pix.sizeimage;
282     buf_size = group->buffer.length;
283     if (buf_size < img_size)
284       goto buffer_too_short;
285   }
286
287   /* We save non planar buffer information into the multi-planar plane array
288    * to avoid duplicating the code later */
289   if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
290     group->planes[0].bytesused = group->buffer.bytesused;
291     group->planes[0].length = group->buffer.length;
292     g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
293     memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
294   }
295
296   GST_LOG_OBJECT (allocator, "Got %s buffer", memory_type_to_str (memory));
297   GST_LOG_OBJECT (allocator, "  index:     %u", group->buffer.index);
298   GST_LOG_OBJECT (allocator, "  type:      %d", group->buffer.type);
299   GST_LOG_OBJECT (allocator, "  flags:     %08x", group->buffer.flags);
300   GST_LOG_OBJECT (allocator, "  field:     %d", group->buffer.field);
301   GST_LOG_OBJECT (allocator, "  memory:    %d", group->buffer.memory);
302   GST_LOG_OBJECT (allocator, "  planes:    %d", group->n_mem);
303
304 #ifndef GST_DISABLE_GST_DEBUG
305   if (memory == V4L2_MEMORY_MMAP) {
306     gint i;
307     for (i = 0; i < group->n_mem; i++) {
308       GST_LOG_OBJECT (allocator, "  [%u] bytesused: %u, length: %u", i,
309           group->planes[i].bytesused, group->planes[i].length);
310       GST_LOG_OBJECT (allocator, "  [%u] MMAP offset:  %u", i,
311           group->planes[i].m.mem_offset);
312     }
313   }
314 #endif
315
316   return group;
317
318 querybuf_failed:
319   {
320     GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno));
321     goto failed;
322   }
323 buffer_too_short:
324   {
325     GST_ERROR ("buffer size %" G_GSIZE_FORMAT
326         " is smaller then negotiated size %" G_GSIZE_FORMAT
327         ", this is usually the result of a bug in the v4l2 driver or libv4l.",
328         buf_size, img_size);
329     goto failed;
330   }
331 failed:
332   gst_v4l2_memory_group_free (group);
333   return NULL;
334 }
335
336
337 /*************************************/
338 /* GstV4lAllocator implementation    */
339 /*************************************/
340
341 static void
342 gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
343 {
344   GstV4l2MemoryGroup *group = mem->group;
345
346   GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
347       mem->plane, group->buffer.index);
348
349   switch (allocator->memory) {
350     case V4L2_MEMORY_DMABUF:
351       close (mem->dmafd);
352       mem->dmafd = -1;
353       break;
354     case V4L2_MEMORY_USERPTR:
355       mem->data = NULL;
356       break;
357     default:
358       break;
359   }
360
361   /* When all memory are back, put the group back in the free queue */
362   if (g_atomic_int_dec_and_test (&group->mems_allocated)) {
363     GST_LOG_OBJECT (allocator, "buffer %u released", group->buffer.index);
364     gst_atomic_queue_push (allocator->free_queue, group);
365     g_signal_emit (allocator, gst_v4l2_allocator_signals[GROUP_RELEASED], 0);
366   }
367
368   /* Keep last, allocator may be freed after this call */
369   g_object_unref (allocator);
370 }
371
372 static void
373 gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
374 {
375   GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator;
376   GstV4l2Memory *mem = (GstV4l2Memory *) gmem;
377   GstV4l2MemoryGroup *group = mem->group;
378
379   GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u",
380       mem->plane, group->buffer.index);
381
382   switch (allocator->memory) {
383     case V4L2_MEMORY_MMAP:
384       if (mem->data) {
385         v4l2_munmap (mem->data, group->planes[mem->plane].length);
386       } else if (group->planes[mem->plane].m.fd > 0) {
387         close (group->planes[mem->plane].m.fd);
388       }
389       break;
390     default:
391       /* Nothing to do */
392       break;
393   }
394
395   _v4l2mem_free (mem);
396 }
397
398 static void
399 gst_v4l2_allocator_dispose (GObject * obj)
400 {
401   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
402   gint i;
403
404   GST_LOG_OBJECT (obj, "called");
405
406   for (i = 0; i < allocator->count; i++) {
407     GstV4l2MemoryGroup *group = allocator->groups[i];
408     allocator->groups[i] = NULL;
409     if (group)
410       gst_v4l2_memory_group_free (group);
411   }
412
413   G_OBJECT_CLASS (parent_class)->dispose (obj);
414 }
415
416 static void
417 gst_v4l2_allocator_finalize (GObject * obj)
418 {
419   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
420
421   GST_LOG_OBJECT (obj, "called");
422
423   v4l2_close (allocator->video_fd);
424   gst_atomic_queue_unref (allocator->free_queue);
425
426   G_OBJECT_CLASS (parent_class)->finalize (obj);
427 }
428
429 static void
430 gst_v4l2_allocator_class_init (GstV4l2AllocatorClass * klass)
431 {
432   GObjectClass *object_class;
433   GstAllocatorClass *allocator_class;
434
435   allocator_class = (GstAllocatorClass *) klass;
436   object_class = (GObjectClass *) klass;
437
438   allocator_class->alloc = NULL;
439   allocator_class->free = gst_v4l2_allocator_free;
440
441   object_class->dispose = gst_v4l2_allocator_dispose;
442   object_class->finalize = gst_v4l2_allocator_finalize;
443
444   gst_v4l2_allocator_signals[GROUP_RELEASED] = g_signal_new ("group-released",
445       G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
446       G_TYPE_NONE, 0);
447
448   GST_DEBUG_CATEGORY_INIT (v4l2allocator_debug, "v4l2allocator", 0,
449       "V4L2 Allocator");
450 }
451
452 static void
453 gst_v4l2_allocator_init (GstV4l2Allocator * allocator)
454 {
455   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
456
457   alloc->mem_type = GST_V4L2_MEMORY_TYPE;
458   alloc->mem_map = (GstMemoryMapFunction) _v4l2mem_map;
459   alloc->mem_unmap = (GstMemoryUnmapFunction) _v4l2mem_unmap;
460   alloc->mem_share = (GstMemoryShareFunction) _v4l2mem_share;
461   alloc->mem_is_span = (GstMemoryIsSpanFunction) _v4l2mem_is_span;
462   /* Use the default, fallback copy function */
463
464   allocator->free_queue = gst_atomic_queue_new (VIDEO_MAX_FRAME);
465
466   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
467 }
468
469 #define GST_V4L2_ALLOCATOR_PROBE(obj,type) \
470     gst_v4l2_allocator_probe ((obj), V4L2_MEMORY_ ## type, \
471         GST_V4L2_ALLOCATOR_FLAG_ ## type ## _REQBUFS, \
472         GST_V4L2_ALLOCATOR_FLAG_ ## type ## _CREATE_BUFS)
473 static guint32
474 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
475     guint32 breq_flag, guint32 bcreate_flag)
476 {
477   struct v4l2_requestbuffers breq = { 0 };
478   guint32 flags = 0;
479
480   breq.type = allocator->type;
481   breq.count = 0;
482   breq.memory = memory;
483
484   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) == 0) {
485     struct v4l2_create_buffers bcreate = { 0 };
486
487     flags |= breq_flag;
488
489     bcreate.memory = V4L2_MEMORY_MMAP;
490     bcreate.format = allocator->format;
491
492     if ((v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0))
493       flags |= bcreate_flag;
494   }
495
496   return flags;
497 }
498
499 static GstV4l2MemoryGroup *
500 gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
501 {
502   struct v4l2_create_buffers bcreate = { 0 };
503   GstV4l2MemoryGroup *group = NULL;
504
505   GST_OBJECT_LOCK (allocator);
506
507   if (!g_atomic_int_get (&allocator->active))
508     goto done;
509
510   bcreate.memory = allocator->memory;
511   bcreate.format = allocator->format;
512   bcreate.count = 1;
513
514   if (!allocator->can_allocate)
515     goto done;
516
517   if (v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0)
518     goto create_bufs_failed;
519
520   group = gst_v4l2_memory_group_new (allocator, bcreate.index);
521
522   if (group) {
523     allocator->groups[bcreate.index] = group;
524     allocator->count++;
525   }
526
527 done:
528   GST_OBJECT_UNLOCK (allocator);
529   return group;
530
531 create_bufs_failed:
532   {
533     GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s",
534         g_strerror (errno));
535     goto done;
536   }
537 }
538
539 static GstV4l2MemoryGroup *
540 gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator)
541 {
542   GstV4l2MemoryGroup *group;
543
544   if (!g_atomic_int_get (&allocator->active))
545     return NULL;
546
547   group = gst_atomic_queue_pop (allocator->free_queue);
548
549   if (group == NULL) {
550     if (allocator->can_allocate) {
551       group = gst_v4l2_allocator_create_buf (allocator);
552
553       /* Don't hammer on CREATE_BUFS */
554       if (group == NULL)
555         allocator->can_allocate = FALSE;
556     }
557   }
558
559   return group;
560 }
561
562 static void
563 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
564     GstV4l2MemoryGroup * group)
565 {
566   gsize size;
567   gboolean imported = FALSE;
568
569   switch (allocator->memory) {
570     case V4L2_MEMORY_USERPTR:
571     case V4L2_MEMORY_DMABUF:
572       imported = TRUE;
573       break;
574   }
575
576   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
577     gint i;
578
579     for (i = 0; i < group->n_mem; i++) {
580       size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage;
581
582       if (imported)
583         group->mem[i]->maxsize = size;
584
585       gst_memory_resize (group->mem[i], 0, size);
586     }
587
588   } else {
589     size = allocator->format.fmt.pix.sizeimage;
590
591     if (imported)
592       group->mem[0]->maxsize = size;
593
594     gst_memory_resize (group->mem[0], 0, size);
595   }
596 }
597
598 static void
599 _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
600 {
601   if (group->mems_allocated > 0) {
602     gint i;
603     /* If one or more mmap worked, we need to unref the memory, otherwise
604      * they will keep a ref on the allocator and leak it. This will put back
605      * the group into the free_queue */
606     for (i = 0; i < group->n_mem; i++)
607       gst_memory_unref (group->mem[i]);
608   } else {
609     /* Otherwise, group has to be on free queue for _stop() to work */
610     gst_atomic_queue_push (allocator->free_queue, group);
611   }
612 }
613
614
615
616 GstV4l2Allocator *
617 gst_v4l2_allocator_new (GstObject * parent, gint video_fd,
618     struct v4l2_format *format)
619 {
620   GstV4l2Allocator *allocator;
621   guint32 flags = 0;
622   gchar *name, *parent_name;
623
624   parent_name = gst_object_get_name (parent);
625   name = g_strconcat (parent_name, ":allocator", NULL);
626   g_free (parent_name);
627
628   allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL);
629   g_free (name);
630
631   /* Save everything */
632   allocator->video_fd = v4l2_dup (video_fd);
633   allocator->type = format->type;
634   allocator->format = *format;
635
636   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP);
637   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR);
638   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF);
639
640
641   if (flags == 0) {
642     /* Drivers not ported from videobuf to videbuf2 don't allow freeing buffers
643      * using REQBUFS(0). This is a workaround to still support these drivers,
644      * which are known to have MMAP support. */
645     GST_WARNING_OBJECT (allocator, "Could not probe supported memory type, "
646         "assuming MMAP is supported, this is expected for older drivers not "
647         " yet ported to videobuf2 framework");
648     flags = GST_V4L2_ALLOCATOR_FLAG_MMAP_REQBUFS;
649   }
650
651   GST_OBJECT_FLAG_SET (allocator, flags);
652
653   return allocator;
654 }
655
656 guint
657 gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
658     guint32 memory)
659 {
660   struct v4l2_requestbuffers breq = { count, allocator->type, memory };
661   gboolean can_allocate;
662   gint i;
663
664   g_return_val_if_fail (count != 0, 0);
665
666   GST_OBJECT_LOCK (allocator);
667
668   if (g_atomic_int_get (&allocator->active))
669     goto already_active;
670
671   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0)
672     goto reqbufs_failed;
673
674   if (breq.count < 1)
675     goto out_of_memory;
676
677   switch (memory) {
678     case V4L2_MEMORY_MMAP:
679       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, MMAP);
680       break;
681     case V4L2_MEMORY_USERPTR:
682       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, USERPTR);
683       break;
684     case V4L2_MEMORY_DMABUF:
685       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, DMABUF);
686       break;
687     default:
688       can_allocate = FALSE;
689       break;
690   }
691
692   GST_DEBUG_OBJECT (allocator, "allocated %u %s buffers out of %u requested",
693       breq.count, memory_type_to_str (memory), count);
694
695   allocator->can_allocate = can_allocate;
696   allocator->count = breq.count;
697   allocator->memory = memory;
698
699   /* Create memory groups */
700   for (i = 0; i < allocator->count; i++) {
701     allocator->groups[i] = gst_v4l2_memory_group_new (allocator, i);
702     if (allocator->groups[i] == NULL)
703       goto error;
704
705     gst_atomic_queue_push (allocator->free_queue, allocator->groups[i]);
706   }
707
708   g_atomic_int_set (&allocator->active, TRUE);
709
710 done:
711   GST_OBJECT_UNLOCK (allocator);
712   return breq.count;
713
714 already_active:
715   {
716     GST_ERROR_OBJECT (allocator,
717         "error requesting %d buffers: %s", count, g_strerror (errno));
718     goto error;
719   }
720 reqbufs_failed:
721   {
722     GST_ERROR_OBJECT (allocator,
723         "error requesting %d buffers: %s", count, g_strerror (errno));
724     goto error;
725   }
726 out_of_memory:
727   {
728     GST_ERROR_OBJECT (allocator, "Not enough memory to allocate buffers");
729     goto error;
730   }
731 error:
732   {
733     breq.count = 0;
734     goto done;
735   }
736 }
737
738 GstV4l2Return
739 gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
740 {
741   struct v4l2_requestbuffers breq = { 0, allocator->type, allocator->memory };
742   gint i = 0;
743   GstV4l2Return ret = GST_V4L2_OK;
744
745   GST_DEBUG_OBJECT (allocator, "stop allocator");
746
747   GST_OBJECT_LOCK (allocator);
748
749   if (!g_atomic_int_get (&allocator->active))
750     goto done;
751
752   if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) {
753     GST_DEBUG_OBJECT (allocator, "allocator is still in use");
754     ret = GST_V4L2_BUSY;
755     goto done;
756   }
757
758   while (gst_atomic_queue_pop (allocator->free_queue)) {
759     /* nothing */
760   };
761
762   for (i = 0; i < allocator->count; i++) {
763     GstV4l2MemoryGroup *group = allocator->groups[i];
764     allocator->groups[i] = NULL;
765     if (group)
766       gst_v4l2_memory_group_free (group);
767   }
768
769   /* Not all drivers support rebufs(0), so warn only */
770   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0)
771     GST_WARNING_OBJECT (allocator,
772         "error releasing buffers buffers: %s", g_strerror (errno));
773
774   allocator->count = 0;
775
776   g_atomic_int_set (&allocator->active, FALSE);
777
778 done:
779   GST_OBJECT_UNLOCK (allocator);
780   return ret;
781 }
782
783 GstV4l2MemoryGroup *
784 gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
785 {
786   GstV4l2MemoryGroup *group;
787   gint i;
788
789   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
790
791   group = gst_v4l2_allocator_alloc (allocator);
792
793   if (group == NULL)
794     return NULL;
795
796   for (i = 0; i < group->n_mem; i++) {
797     if (group->mem[i] == NULL) {
798       gpointer data;
799       data = v4l2_mmap (NULL, group->planes[i].length, PROT_READ | PROT_WRITE,
800           MAP_SHARED, allocator->video_fd, group->planes[i].m.mem_offset);
801
802       if (data == MAP_FAILED)
803         goto mmap_failed;
804
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);
808
809       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
810           NULL, group->planes[i].length, 0, 0, group->planes[i].length, i,
811           data, -1, group);
812     } else {
813       /* Take back the allocator reference */
814       gst_object_ref (allocator);
815     }
816
817     group->mems_allocated++;
818   }
819
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
823    * simplicity */
824   gst_v4l2_allocator_reset_size (allocator, group);
825
826   return group;
827
828 mmap_failed:
829   {
830     GST_ERROR_OBJECT (allocator, "Failed to mmap buffer: %s",
831         g_strerror (errno));
832     _cleanup_failed_alloc (allocator, group);
833     return NULL;
834   }
835 }
836
837 GstV4l2MemoryGroup *
838 gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
839     GstAllocator * dmabuf_allocator)
840 {
841   GstV4l2MemoryGroup *group;
842   gint i;
843
844   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL);
845
846   group = gst_v4l2_allocator_alloc (allocator);
847
848   if (group == NULL)
849     return NULL;
850
851   for (i = 0; i < group->n_mem; i++) {
852     GstV4l2Memory *mem;
853     GstMemory *dma_mem;
854     gint dmafd;
855
856     if (group->mem[i] == NULL) {
857       struct v4l2_exportbuffer expbuf = { 0 };
858
859       expbuf.type = allocator->type;
860       expbuf.index = group->buffer.index;
861       expbuf.plane = i;
862       expbuf.flags = O_CLOEXEC | O_RDWR;
863
864       if (v4l2_ioctl (allocator->video_fd, VIDIOC_EXPBUF, &expbuf) < 0)
865         goto expbuf_failed;
866
867       GST_LOG_OBJECT (allocator, "exported DMABUF as fd %i plane %d",
868           expbuf.fd, i);
869
870       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
871           NULL, group->planes[i].length, 0, 0, group->planes[i].length, i,
872           NULL, expbuf.fd, group);
873     } else {
874       /* Take back the allocator reference */
875       gst_object_ref (allocator);
876     }
877
878     g_assert (gst_is_v4l2_memory (group->mem[i]));
879     mem = (GstV4l2Memory *) group->mem[i];
880
881     if ((dmafd = dup (mem->dmafd)) < 0)
882       goto dup_failed;
883
884     dma_mem = gst_dmabuf_allocator_alloc (dmabuf_allocator, dmafd,
885         mem->mem.maxsize);
886
887     gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem),
888         GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref);
889
890     group->mem[i] = dma_mem;
891     group->mems_allocated++;
892   }
893
894   gst_v4l2_allocator_reset_size (allocator, group);
895
896   return group;
897
898 expbuf_failed:
899   {
900     GST_ERROR_OBJECT (allocator, "Failed to export DMABUF: %s",
901         g_strerror (errno));
902     goto cleanup;
903   }
904 dup_failed:
905   {
906     GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
907         g_strerror (errno));
908     goto cleanup;
909   }
910 cleanup:
911   {
912     _cleanup_failed_alloc (allocator, group);
913     return NULL;
914   }
915 }
916
917 static void
918 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
919     GstV4l2MemoryGroup * group)
920 {
921   GstV4l2Memory *mem;
922   gint i;
923
924   g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
925
926   for (i = 0; i < group->n_mem; i++) {
927
928     mem = (GstV4l2Memory *) group->mem[i];
929
930     GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d",
931         mem->dmafd, i);
932
933     if (mem->dmafd >= 0)
934       close (mem->dmafd);
935
936     /* Update memory */
937     mem->mem.maxsize = 0;
938     mem->mem.offset = 0;
939     mem->mem.size = 0;
940     mem->dmafd = -1;
941
942     /* Update v4l2 structure */
943     group->planes[i].length = 0;
944     group->planes[i].bytesused = 0;
945     group->planes[i].m.fd = -1;
946     group->planes[i].data_offset = 0;
947   }
948
949   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
950     group->buffer.bytesused = 0;
951     group->buffer.length = 0;
952     group->buffer.m.fd = -1;
953   }
954 }
955
956 GstV4l2MemoryGroup *
957 gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
958 {
959   GstV4l2MemoryGroup *group;
960   gint i;
961
962   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
963
964   group = gst_v4l2_allocator_alloc (allocator);
965
966   if (group == NULL)
967     return NULL;
968
969   GST_LOG_OBJECT (allocator, "allocating empty DMABUF import group");
970
971   for (i = 0; i < group->n_mem; i++) {
972     if (group->mem[i] == NULL) {
973       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
974           NULL, 0, 0, 0, 0, i, NULL, -1, group);
975     } else {
976       /* Take back the allocator reference */
977       gst_object_ref (allocator);
978     }
979
980     group->mems_allocated++;
981   }
982
983   gst_v4l2_allocator_clear_dmabufin (allocator, group);
984
985   return group;
986 }
987
988 static void
989 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
990     GstV4l2MemoryGroup * group)
991 {
992   GstV4l2Memory *mem;
993   gint i;
994
995   g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
996
997   for (i = 0; i < group->n_mem; i++) {
998     mem = (GstV4l2Memory *) group->mem[i];
999
1000     GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %"
1001         G_GSIZE_FORMAT, mem->data, i, mem->mem.size);
1002
1003     mem->mem.maxsize = 0;
1004     mem->mem.size = 0;
1005     mem->data = NULL;
1006
1007     group->planes[i].length = 0;
1008     group->planes[i].bytesused = 0;
1009     group->planes[i].m.userptr = 0;
1010   }
1011
1012   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1013     group->buffer.bytesused = 0;
1014     group->buffer.length = 0;
1015     group->buffer.m.userptr = 0;
1016   }
1017 }
1018
1019 GstV4l2MemoryGroup *
1020 gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
1021 {
1022   GstV4l2MemoryGroup *group;
1023   gint i;
1024
1025   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
1026
1027   group = gst_v4l2_allocator_alloc (allocator);
1028
1029   if (group == NULL)
1030     return NULL;
1031
1032   GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
1033
1034   for (i = 0; i < group->n_mem; i++) {
1035
1036     if (group->mem[i] == NULL) {
1037       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
1038           NULL, 0, 0, 0, 0, i, NULL, -1, group);
1039     } else {
1040       /* Take back the allocator reference */
1041       gst_object_ref (allocator);
1042     }
1043
1044     group->mems_allocated++;
1045   }
1046
1047   gst_v4l2_allocator_clear_userptr (allocator, group);
1048
1049   return group;
1050 }
1051
1052 gboolean
1053 gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
1054     GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
1055 {
1056   GstV4l2Memory *mem;
1057   gint i;
1058
1059   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
1060
1061   if (group->n_mem != n_mem)
1062     goto n_mem_missmatch;
1063
1064   for (i = 0; i < group->n_mem; i++) {
1065     gint dmafd;
1066     gsize size, offset, maxsize;
1067
1068     if (!gst_is_dmabuf_memory (dma_mem[i]))
1069       goto not_dmabuf;
1070
1071     size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
1072
1073     if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0)
1074       goto dup_failed;
1075
1076     GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i);
1077
1078     mem = (GstV4l2Memory *) group->mem[i];
1079
1080     /* Update memory */
1081     mem->mem.maxsize = maxsize;
1082     mem->mem.offset = offset;
1083     mem->mem.size = size;
1084     mem->dmafd = dmafd;
1085
1086     /* Update v4l2 structure */
1087     group->planes[i].length = maxsize;
1088     group->planes[i].bytesused = size;
1089     group->planes[i].m.fd = dmafd;
1090     group->planes[i].data_offset = offset;
1091   }
1092
1093   /* Copy into buffer structure if not using planes */
1094   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1095     group->buffer.bytesused = group->planes[0].bytesused;
1096     group->buffer.length = group->planes[0].length;
1097     group->buffer.m.fd = group->planes[0].m.userptr;
1098   } else {
1099     group->buffer.length = group->n_mem;
1100   }
1101
1102   return TRUE;
1103
1104 n_mem_missmatch:
1105   {
1106     GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
1107         group->n_mem);
1108     return FALSE;
1109   }
1110 not_dmabuf:
1111   {
1112     GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
1113     return FALSE;
1114   }
1115 dup_failed:
1116   {
1117     GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
1118         g_strerror (errno));
1119     return FALSE;
1120   }
1121 }
1122
1123 gboolean
1124 gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
1125     GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
1126     gpointer * data, gsize * offset)
1127 {
1128   GstV4l2Memory *mem;
1129   gint i;
1130
1131   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
1132
1133   /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */
1134   if (n_planes != group->n_mem)
1135     goto n_mem_missmatch;
1136
1137   for (i = 0; i < group->n_mem; i++) {
1138     gsize size, maxsize;
1139
1140     if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1141       struct v4l2_pix_format_mplane *pix = &allocator->format.fmt.pix_mp;
1142       maxsize = pix->plane_fmt[i].sizeimage;
1143     } else {
1144       maxsize = allocator->format.fmt.pix.sizeimage;
1145     }
1146
1147     if ((i + 1) == n_planes) {
1148       size = img_size - offset[i];
1149     } else {
1150       size = offset[i + 1] - offset[i];
1151     }
1152
1153     g_assert (size <= img_size);
1154
1155     GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
1156         G_GSIZE_FORMAT, data[i], i, size);
1157
1158     mem = (GstV4l2Memory *) group->mem[i];
1159
1160     mem->mem.maxsize = maxsize;
1161     mem->mem.size = size;
1162     mem->data = data[i];
1163
1164     group->planes[i].length = maxsize;
1165     group->planes[i].bytesused = size;
1166     group->planes[i].m.userptr = (unsigned long) data[i];
1167     group->planes[i].data_offset = 0;
1168   }
1169
1170   /* Copy into buffer structure if not using planes */
1171   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1172     group->buffer.bytesused = group->planes[0].bytesused;
1173     group->buffer.length = group->planes[0].length;
1174     group->buffer.m.userptr = group->planes[0].m.userptr;
1175   } else {
1176     group->buffer.length = group->n_mem;
1177   }
1178
1179   return TRUE;
1180
1181 n_mem_missmatch:
1182   {
1183     GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
1184         n_planes, group->n_mem);
1185     return FALSE;
1186   }
1187 }
1188
1189 void
1190 gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
1191 {
1192   gint i;
1193
1194   GST_OBJECT_LOCK (allocator);
1195
1196   if (!g_atomic_int_get (&allocator->active))
1197     goto done;
1198
1199   for (i = 0; i < allocator->count; i++) {
1200     GstV4l2MemoryGroup *group = allocator->groups[i];
1201     gint n;
1202
1203     if (IS_QUEUED (group->buffer)) {
1204       UNSET_QUEUED (group->buffer);
1205
1206       gst_v4l2_allocator_reset_group (allocator, group);
1207
1208       for (n = 0; n < group->n_mem; n++)
1209         gst_memory_unref (group->mem[n]);
1210     }
1211   }
1212
1213 done:
1214   GST_OBJECT_UNLOCK (allocator);
1215 }
1216
1217 gboolean
1218 gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
1219     GstV4l2MemoryGroup * group)
1220 {
1221   gboolean ret = TRUE;
1222   gint i;
1223
1224   g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE);
1225
1226   /* update sizes */
1227   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1228     for (i = 0; i < group->n_mem; i++)
1229       group->planes[i].bytesused =
1230           gst_memory_get_sizes (group->mem[i], NULL, NULL);
1231   } else {
1232     group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL);
1233   }
1234
1235   if (v4l2_ioctl (allocator->video_fd, VIDIOC_QBUF, &group->buffer) < 0) {
1236     GST_ERROR_OBJECT (allocator, "failed queing buffer %i: %s",
1237         group->buffer.index, g_strerror (errno));
1238     ret = FALSE;
1239     if (IS_QUEUED (group->buffer)) {
1240       GST_DEBUG_OBJECT (allocator,
1241           "driver pretends buffer is queued even if queue failed");
1242       UNSET_QUEUED (group->buffer);
1243     }
1244     goto done;
1245   }
1246
1247   GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)",
1248       group->buffer.index, group->buffer.flags);
1249
1250   if (!IS_QUEUED (group->buffer)) {
1251     GST_DEBUG_OBJECT (allocator,
1252         "driver pretends buffer is not queued even if queue succeeded");
1253     SET_QUEUED (group->buffer);
1254   }
1255
1256   /* Ensure the memory will stay around and is RO */
1257   for (i = 0; i < group->n_mem; i++)
1258     gst_memory_ref (group->mem[i]);
1259
1260 done:
1261   return ret;
1262 }
1263
1264 GstV4l2MemoryGroup *
1265 gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator)
1266 {
1267   struct v4l2_buffer buffer = { 0 };
1268   struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} };
1269   gint i;
1270
1271   GstV4l2MemoryGroup *group = NULL;
1272
1273   g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE);
1274
1275   buffer.type = allocator->type;
1276   buffer.memory = allocator->memory;
1277
1278   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1279     buffer.length = allocator->format.fmt.pix_mp.num_planes;
1280     buffer.m.planes = planes;
1281   }
1282
1283   if (v4l2_ioctl (allocator->video_fd, VIDIOC_DQBUF, &buffer) < 0)
1284     goto error;
1285
1286   group = allocator->groups[buffer.index];
1287   group->buffer = buffer;
1288
1289   GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index,
1290       buffer.flags);
1291
1292   if (IS_QUEUED (group->buffer)) {
1293     GST_DEBUG_OBJECT (allocator,
1294         "driver pretends buffer is queued even if dequeue succeeded");
1295     UNSET_QUEUED (group->buffer);
1296   }
1297
1298   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1299     group->buffer.m.planes = group->planes;
1300     memcpy (group->planes, buffer.m.planes, sizeof (planes));
1301   } else {
1302     group->planes[0].bytesused = group->buffer.bytesused;
1303     group->planes[0].length = group->buffer.length;
1304     g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
1305     memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
1306   }
1307
1308   /* And update memory size */
1309   if (V4L2_TYPE_IS_OUTPUT (allocator->type)) {
1310     gst_v4l2_allocator_reset_size (allocator, group);
1311   } else {
1312     /* for capture, simply read the size */
1313     for (i = 0; i < group->n_mem; i++) {
1314       gst_memory_resize (group->mem[i], 0, group->planes[i].bytesused);
1315     }
1316   }
1317
1318   /* Release the memory, possibly making it RW again */
1319   for (i = 0; i < group->n_mem; i++)
1320     gst_memory_unref (group->mem[i]);
1321
1322   return group;
1323
1324 error:
1325   GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
1326       memory_type_to_str (allocator->memory), g_strerror (errno));
1327
1328   switch (errno) {
1329     case EAGAIN:
1330       GST_WARNING_OBJECT (allocator,
1331           "Non-blocking I/O has been selected using O_NONBLOCK and"
1332           " no buffer was in the outgoing queue.");
1333       break;
1334     case EINVAL:
1335       GST_ERROR_OBJECT (allocator,
1336           "The buffer type is not supported, or the index is out of bounds, "
1337           "or no buffers have been allocated yet, or the userptr "
1338           "or length are invalid.");
1339       break;
1340     case ENOMEM:
1341       GST_ERROR_OBJECT (allocator,
1342           "insufficient memory to enqueue a user pointer buffer");
1343       break;
1344     case EIO:
1345       GST_INFO_OBJECT (allocator,
1346           "VIDIOC_DQBUF failed due to an internal error."
1347           " Can also indicate temporary problems like signal loss."
1348           " Note the driver might dequeue an (empty) buffer despite"
1349           " returning an error, or even stop capturing.");
1350       /* have we de-queued a buffer ? */
1351       if (!IS_QUEUED (buffer)) {
1352         GST_DEBUG_OBJECT (allocator, "reenqueing buffer");
1353         /* FIXME ... should we do something here? */
1354       }
1355       break;
1356     case EINTR:
1357       GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device");
1358       break;
1359     default:
1360       GST_WARNING_OBJECT (allocator,
1361           "Grabbing frame got interrupted unexpectedly. %d: %s.", errno,
1362           g_strerror (errno));
1363       break;
1364   }
1365
1366   return NULL;
1367 }
1368
1369 void
1370 gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
1371     GstV4l2MemoryGroup * group)
1372 {
1373   switch (allocator->memory) {
1374     case V4L2_MEMORY_USERPTR:
1375       gst_v4l2_allocator_clear_userptr (allocator, group);
1376       break;
1377     case V4L2_MEMORY_DMABUF:
1378       gst_v4l2_allocator_clear_dmabufin (allocator, group);
1379       break;
1380     case V4L2_MEMORY_MMAP:
1381       break;
1382     default:
1383       g_assert_not_reached ();
1384       break;
1385   }
1386
1387   gst_v4l2_allocator_reset_size (allocator, group);
1388 }
1389
1390 gsize
1391 gst_v4l2_allocator_num_allocated (GstV4l2Allocator * allocator)
1392 {
1393   gsize num_allocated;
1394
1395   GST_OBJECT_LOCK (allocator);
1396
1397   num_allocated = allocator->count;
1398
1399   GST_OBJECT_UNLOCK (allocator);
1400
1401   return num_allocated;
1402 }