ebc1f3154280acd7e39fa49337e400c1c7c9432f
[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 static void
208 _v4l2mem_parent_to_dmabuf (GstV4l2Memory * mem, GstMemory * dma_mem)
209 {
210   gst_memory_lock (&mem->mem, GST_LOCK_FLAG_EXCLUSIVE);
211   dma_mem->parent = gst_memory_ref (&mem->mem);
212 }
213
214 gboolean
215 gst_is_v4l2_memory (GstMemory * mem)
216 {
217   return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE);
218 }
219
220
221 /*************************************/
222 /* GstV4l2MemoryGroup implementation */
223 /*************************************/
224
225 static void
226 gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group)
227 {
228   gint i;
229
230   for (i = 0; i < group->n_mem; i++) {
231     GstMemory *mem = group->mem[i];
232     group->mem[i] = NULL;
233     if (mem)
234       gst_memory_unref (mem);
235   }
236
237   g_slice_free (GstV4l2MemoryGroup, group);
238 }
239
240 static GstV4l2MemoryGroup *
241 gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index)
242 {
243   gint video_fd = allocator->video_fd;
244   guint32 memory = allocator->memory;
245   struct v4l2_format *format = &allocator->format;
246   GstV4l2MemoryGroup *group;
247   gsize img_size, buf_size;
248
249   group = g_slice_new0 (GstV4l2MemoryGroup);
250
251   group->buffer.type = format->type;
252   group->buffer.index = index;
253   group->buffer.memory = memory;
254
255   if (V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
256     group->n_mem = group->buffer.length = format->fmt.pix_mp.num_planes;
257     group->buffer.m.planes = group->planes;
258   } else {
259     group->n_mem = 1;
260   }
261
262   if (v4l2_ioctl (video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0)
263     goto querybuf_failed;
264
265   /* Check that provided size matches the format we have negotiation. Failing
266    * there usually means a driver of libv4l bug. */
267   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
268     gint i;
269
270     for (i = 0; i < group->n_mem; i++) {
271       img_size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage;
272       buf_size = group->planes[i].length;
273       if (buf_size < img_size)
274         goto buffer_too_short;
275     }
276   } else {
277     img_size = allocator->format.fmt.pix.sizeimage;
278     buf_size = group->buffer.length;
279     if (buf_size < img_size)
280       goto buffer_too_short;
281   }
282
283   /* We save non planar buffer information into the multi-planar plane array
284    * to avoid duplicating the code later */
285   if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) {
286     group->planes[0].bytesused = group->buffer.bytesused;
287     group->planes[0].length = group->buffer.length;
288     g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
289     memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
290   }
291
292   GST_LOG_OBJECT (allocator, "Got %s buffer", memory_type_to_str (memory));
293   GST_LOG_OBJECT (allocator, "  index:     %u", group->buffer.index);
294   GST_LOG_OBJECT (allocator, "  type:      %d", group->buffer.type);
295   GST_LOG_OBJECT (allocator, "  flags:     %08x", group->buffer.flags);
296   GST_LOG_OBJECT (allocator, "  field:     %d", group->buffer.field);
297   GST_LOG_OBJECT (allocator, "  memory:    %d", group->buffer.memory);
298   GST_LOG_OBJECT (allocator, "  planes:    %d", group->n_mem);
299
300 #ifndef GST_DISABLE_GST_DEBUG
301   if (memory == V4L2_MEMORY_MMAP) {
302     gint i;
303     for (i = 0; i < group->n_mem; i++) {
304       GST_LOG_OBJECT (allocator, "  [%u] bytesused: %u, length: %u", i,
305           group->planes[i].bytesused, group->planes[i].length);
306       GST_LOG_OBJECT (allocator, "  [%u] MMAP offset:  %u", i,
307           group->planes[i].m.mem_offset);
308     }
309   }
310 #endif
311
312   return group;
313
314 querybuf_failed:
315   {
316     GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno));
317     goto failed;
318   }
319 buffer_too_short:
320   {
321     GST_ERROR ("buffer size %" G_GSIZE_FORMAT
322         " is smaller then negotiated size %" G_GSIZE_FORMAT
323         ", this is usually the result of a bug in the v4l2 driver or libv4l.",
324         buf_size, img_size);
325     goto failed;
326   }
327 failed:
328   gst_v4l2_memory_group_free (group);
329   return NULL;
330 }
331
332
333 /*************************************/
334 /* GstV4lAllocator implementation    */
335 /*************************************/
336
337 static void
338 gst_v4l2_allocator_release (GstV4l2Allocator * allocator, GstV4l2Memory * mem)
339 {
340   GstV4l2MemoryGroup *group = mem->group;
341
342   GST_LOG_OBJECT (allocator, "plane %i of buffer %u released",
343       mem->plane, group->buffer.index);
344
345   switch (allocator->memory) {
346     case V4L2_MEMORY_DMABUF:
347       close (mem->dmafd);
348       mem->dmafd = -1;
349       break;
350     case V4L2_MEMORY_USERPTR:
351       mem->data = NULL;
352       break;
353     default:
354       break;
355   }
356
357   /* When all memory are back, put the group back in the free queue */
358   if (g_atomic_int_dec_and_test (&group->mems_allocated)) {
359     GST_LOG_OBJECT (allocator, "buffer %u released", group->buffer.index);
360     gst_atomic_queue_push (allocator->free_queue, group);
361     g_signal_emit (allocator, gst_v4l2_allocator_signals[GROUP_RELEASED], 0);
362   }
363
364   /* Keep last, allocator may be freed after this call */
365   g_object_unref (allocator);
366 }
367
368 static void
369 gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem)
370 {
371   GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator;
372   GstV4l2Memory *mem = (GstV4l2Memory *) gmem;
373   GstV4l2MemoryGroup *group = mem->group;
374
375   GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u",
376       mem->plane, group->buffer.index);
377
378   switch (allocator->memory) {
379     case V4L2_MEMORY_MMAP:
380       if (mem->data) {
381         v4l2_munmap (mem->data, group->planes[mem->plane].length);
382       } else if (group->planes[mem->plane].m.fd > 0) {
383         close (group->planes[mem->plane].m.fd);
384       }
385       break;
386     default:
387       /* Nothing to do */
388       break;
389   }
390
391   _v4l2mem_free (mem);
392 }
393
394 static void
395 gst_v4l2_allocator_dispose (GObject * obj)
396 {
397   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
398   gint i;
399
400   GST_LOG_OBJECT (obj, "called");
401
402   for (i = 0; i < allocator->count; i++) {
403     GstV4l2MemoryGroup *group = allocator->groups[i];
404     allocator->groups[i] = NULL;
405     if (group)
406       gst_v4l2_memory_group_free (group);
407   }
408
409   G_OBJECT_CLASS (parent_class)->dispose (obj);
410 }
411
412 static void
413 gst_v4l2_allocator_finalize (GObject * obj)
414 {
415   GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj;
416
417   GST_LOG_OBJECT (obj, "called");
418
419   v4l2_close (allocator->video_fd);
420   gst_atomic_queue_unref (allocator->free_queue);
421
422   G_OBJECT_CLASS (parent_class)->finalize (obj);
423 }
424
425 static void
426 gst_v4l2_allocator_class_init (GstV4l2AllocatorClass * klass)
427 {
428   GObjectClass *object_class;
429   GstAllocatorClass *allocator_class;
430
431   allocator_class = (GstAllocatorClass *) klass;
432   object_class = (GObjectClass *) klass;
433
434   allocator_class->alloc = NULL;
435   allocator_class->free = gst_v4l2_allocator_free;
436
437   object_class->dispose = gst_v4l2_allocator_dispose;
438   object_class->finalize = gst_v4l2_allocator_finalize;
439
440   gst_v4l2_allocator_signals[GROUP_RELEASED] = g_signal_new ("group-released",
441       G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
442       G_TYPE_NONE, 0);
443
444   GST_DEBUG_CATEGORY_INIT (v4l2allocator_debug, "v4l2allocator", 0,
445       "V4L2 Allocator");
446 }
447
448 static void
449 gst_v4l2_allocator_init (GstV4l2Allocator * allocator)
450 {
451   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
452
453   alloc->mem_type = GST_V4L2_MEMORY_TYPE;
454   alloc->mem_map = (GstMemoryMapFunction) _v4l2mem_map;
455   alloc->mem_unmap = (GstMemoryUnmapFunction) _v4l2mem_unmap;
456   alloc->mem_share = (GstMemoryShareFunction) _v4l2mem_share;
457   alloc->mem_is_span = (GstMemoryIsSpanFunction) _v4l2mem_is_span;
458   /* Use the default, fallback copy function */
459
460   allocator->free_queue = gst_atomic_queue_new (VIDEO_MAX_FRAME);
461
462   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
463 }
464
465 #define GST_V4L2_ALLOCATOR_PROBE(obj,type) \
466     gst_v4l2_allocator_probe ((obj), V4L2_MEMORY_ ## type, \
467         GST_V4L2_ALLOCATOR_FLAG_ ## type ## _REQBUFS, \
468         GST_V4L2_ALLOCATOR_FLAG_ ## type ## _CREATE_BUFS)
469 static guint32
470 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
471     guint32 breq_flag, guint32 bcreate_flag)
472 {
473   struct v4l2_requestbuffers breq = { 0 };
474   guint32 flags = 0;
475
476   breq.type = allocator->type;
477   breq.count = 0;
478   breq.memory = memory;
479
480   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) == 0) {
481     struct v4l2_create_buffers bcreate = { 0 };
482
483     flags |= breq_flag;
484
485     bcreate.memory = V4L2_MEMORY_MMAP;
486     bcreate.format = allocator->format;
487
488     if ((v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0))
489       flags |= bcreate_flag;
490   }
491
492   return flags;
493 }
494
495 static GstV4l2MemoryGroup *
496 gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
497 {
498   struct v4l2_create_buffers bcreate = { 0 };
499   GstV4l2MemoryGroup *group = NULL;
500
501   GST_OBJECT_LOCK (allocator);
502
503   if (!allocator->active)
504     goto done;
505
506   bcreate.memory = allocator->memory;
507   bcreate.format = allocator->format;
508   bcreate.count = 1;
509
510   if (!allocator->can_allocate)
511     goto done;
512
513   if (v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0)
514     goto create_bufs_failed;
515
516   group = gst_v4l2_memory_group_new (allocator, bcreate.index);
517
518   if (group) {
519     allocator->groups[bcreate.index] = group;
520     allocator->count++;
521   }
522
523 done:
524   GST_OBJECT_UNLOCK (allocator);
525   return group;
526
527 create_bufs_failed:
528   {
529     GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s",
530         g_strerror (errno));
531     goto done;
532   }
533 }
534
535 static GstV4l2MemoryGroup *
536 gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator)
537 {
538   GstV4l2MemoryGroup *group;
539
540   if (!g_atomic_int_get (&allocator->active))
541     return NULL;
542
543   group = gst_atomic_queue_pop (allocator->free_queue);
544
545   if (group == NULL) {
546     if (allocator->can_allocate) {
547       group = gst_v4l2_allocator_create_buf (allocator);
548
549       /* Don't hammer on CREATE_BUFS */
550       if (group == NULL)
551         allocator->can_allocate = FALSE;
552     }
553   }
554
555   return group;
556 }
557
558 static void
559 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
560     GstV4l2MemoryGroup * group)
561 {
562   gsize size;
563   gboolean imported = FALSE;
564
565   switch (allocator->memory) {
566     case V4L2_MEMORY_USERPTR:
567     case V4L2_MEMORY_DMABUF:
568       imported = TRUE;
569       break;
570   }
571
572   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
573     gint i;
574
575     for (i = 0; i < group->n_mem; i++) {
576       size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage;
577
578       if (imported)
579         group->mem[i]->maxsize = size;
580
581       gst_memory_resize (group->mem[i], 0, size);
582     }
583
584   } else {
585     size = allocator->format.fmt.pix.sizeimage;
586
587     if (imported)
588       group->mem[0]->maxsize = size;
589
590     gst_memory_resize (group->mem[0], 0, size);
591   }
592 }
593
594 static void
595 _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group)
596 {
597   if (group->mems_allocated > 0) {
598     gint i;
599     /* If one or more mmap worked, we need to unref the memory, otherwise
600      * they will keep a ref on the allocator and leak it. This will put back
601      * the group into the free_queue */
602     for (i = 0; i < group->n_mem; i++)
603       gst_memory_unref (group->mem[i]);
604   } else {
605     /* Otherwise, group has to be on free queue for _stop() to work */
606     gst_atomic_queue_push (allocator->free_queue, group);
607   }
608 }
609
610
611
612 GstV4l2Allocator *
613 gst_v4l2_allocator_new (GstObject * parent, gint video_fd,
614     struct v4l2_format *format)
615 {
616   GstV4l2Allocator *allocator;
617   guint32 flags = 0;
618   gchar *name, *parent_name;
619
620   parent_name = gst_object_get_name (parent);
621   name = g_strconcat (parent_name, ":allocator", NULL);
622   g_free (parent_name);
623
624   allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL);
625   g_free (name);
626
627   /* Save everything */
628   allocator->video_fd = v4l2_dup (video_fd);
629   allocator->type = format->type;
630   allocator->format = *format;
631
632   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP);
633   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR);
634   flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF);
635
636   GST_OBJECT_FLAG_SET (allocator, flags);
637
638   if (flags == 0)
639     goto not_supported;
640
641   return allocator;
642
643 not_supported:
644   {
645     GST_ERROR_OBJECT (allocator,
646         "No memory model supported by GStreamer for this device");
647     g_object_unref (allocator);
648     return NULL;
649   }
650 }
651
652 guint
653 gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
654     guint32 memory)
655 {
656   struct v4l2_requestbuffers breq = { count, allocator->type, memory };
657   gboolean can_allocate;
658   gint i;
659
660   g_return_val_if_fail (count != 0, 0);
661
662   GST_OBJECT_LOCK (allocator);
663
664   if (allocator->active)
665     goto already_active;
666
667   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0)
668     goto reqbufs_failed;
669
670   if (breq.count < 1)
671     goto out_of_memory;
672
673   switch (memory) {
674     case V4L2_MEMORY_MMAP:
675       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, MMAP);
676       break;
677     case V4L2_MEMORY_USERPTR:
678       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, USERPTR);
679       break;
680     case V4L2_MEMORY_DMABUF:
681       can_allocate = GST_V4L2_ALLOCATOR_CAN_ALLOCATE (allocator, DMABUF);
682       break;
683     default:
684       can_allocate = FALSE;
685       break;
686   }
687
688   GST_DEBUG_OBJECT (allocator, "allocated %u %s buffers out of %u requested",
689       breq.count, memory_type_to_str (memory), count);
690
691   allocator->can_allocate = can_allocate;
692   allocator->count = breq.count;
693   allocator->memory = memory;
694
695   /* Create memory groups */
696   for (i = 0; i < allocator->count; i++) {
697     allocator->groups[i] = gst_v4l2_memory_group_new (allocator, i);
698     if (allocator->groups[i] == NULL)
699       goto error;
700
701     gst_atomic_queue_push (allocator->free_queue, allocator->groups[i]);
702   }
703
704   g_atomic_int_set (&allocator->active, TRUE);
705
706 done:
707   GST_OBJECT_UNLOCK (allocator);
708   return breq.count;
709
710 already_active:
711   {
712     GST_ERROR_OBJECT (allocator,
713         "error requesting %d buffers: %s", count, g_strerror (errno));
714     goto error;
715   }
716 reqbufs_failed:
717   {
718     GST_ERROR_OBJECT (allocator,
719         "error requesting %d buffers: %s", count, g_strerror (errno));
720     goto error;
721   }
722 out_of_memory:
723   {
724     GST_ERROR_OBJECT (allocator, "Not enough memory to allocate buffers");
725     goto error;
726   }
727 error:
728   {
729     breq.count = 0;
730     goto done;
731   }
732 }
733
734 GstV4l2Return
735 gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
736 {
737   struct v4l2_requestbuffers breq = { 0, allocator->type, allocator->memory };
738   gint i = 0;
739   GstV4l2Return ret = GST_V4L2_OK;
740
741   GST_DEBUG_OBJECT (allocator, "stop allocator");
742
743   GST_OBJECT_LOCK (allocator);
744
745   if (!allocator->active)
746     goto done;
747
748   if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) {
749     GST_DEBUG_OBJECT (allocator, "allocator is still in use");
750     ret = GST_V4L2_BUSY;
751     goto done;
752   }
753
754   while (gst_atomic_queue_pop (allocator->free_queue)) {
755     /* nothing */
756   };
757
758   for (i = 0; i < allocator->count; i++) {
759     GstV4l2MemoryGroup *group = allocator->groups[i];
760     allocator->groups[i] = NULL;
761     if (group)
762       gst_v4l2_memory_group_free (group);
763   }
764
765   if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0)
766     goto reqbufs_failed;
767
768   g_atomic_int_set (&allocator->active, FALSE);
769
770 done:
771   GST_OBJECT_UNLOCK (allocator);
772   return ret;
773
774 reqbufs_failed:
775   {
776     GST_ERROR_OBJECT (allocator,
777         "error releasing buffers buffers: %s", g_strerror (errno));
778     ret = GST_V4L2_ERROR;
779     goto done;
780   }
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     _v4l2mem_parent_to_dmabuf (mem, dma_mem);
887
888     group->mem[i] = dma_mem;
889     group->mems_allocated++;
890   }
891
892   gst_v4l2_allocator_reset_size (allocator, group);
893
894   return group;
895
896 expbuf_failed:
897   {
898     GST_ERROR_OBJECT (allocator, "Failed to export DMABUF: %s",
899         g_strerror (errno));
900     goto cleanup;
901   }
902 dup_failed:
903   {
904     GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
905         g_strerror (errno));
906     goto cleanup;
907   }
908 cleanup:
909   {
910     _cleanup_failed_alloc (allocator, group);
911     return NULL;
912   }
913 }
914
915 static void
916 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
917     GstV4l2MemoryGroup * group)
918 {
919   GstV4l2Memory *mem;
920   gint i;
921
922   g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF);
923
924   for (i = 0; i < group->n_mem; i++) {
925
926     mem = (GstV4l2Memory *) group->mem[i];
927
928     GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d",
929         mem->dmafd, i);
930
931     if (mem->dmafd >= 0)
932       close (mem->dmafd);
933
934     /* Update memory */
935     mem->mem.maxsize = 0;
936     mem->mem.offset = 0;
937     mem->mem.size = 0;
938     mem->dmafd = -1;
939
940     /* Update v4l2 structure */
941     group->planes[i].length = 0;
942     group->planes[i].bytesused = 0;
943     group->planes[i].m.fd = -1;
944     group->planes[i].data_offset = 0;
945   }
946
947   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
948     group->buffer.bytesused = 0;
949     group->buffer.length = 0;
950     group->buffer.m.fd = -1;
951   }
952 }
953
954 GstV4l2MemoryGroup *
955 gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator)
956 {
957   GstV4l2MemoryGroup *group;
958   gint i;
959
960   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, NULL);
961
962   group = gst_v4l2_allocator_alloc (allocator);
963
964   if (group == NULL)
965     return NULL;
966
967   for (i = 0; i < group->n_mem; i++) {
968     GST_LOG_OBJECT (allocator, "allocation empty DMABUF import group");
969
970     if (group->mem[i] == NULL) {
971       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
972           NULL, 0, 0, 0, 0, i, NULL, -1, group);
973     } else {
974       /* Take back the allocator reference */
975       gst_object_ref (allocator);
976     }
977
978     group->mems_allocated++;
979   }
980
981   gst_v4l2_allocator_clear_dmabufin (allocator, group);
982
983   return group;
984 }
985
986 static void
987 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
988     GstV4l2MemoryGroup * group)
989 {
990   GstV4l2Memory *mem;
991   gint i;
992
993   g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR);
994
995   for (i = 0; i < group->n_mem; i++) {
996     mem = (GstV4l2Memory *) group->mem[i];
997
998     GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %"
999         G_GSIZE_FORMAT, mem->data, i, mem->mem.size);
1000
1001     mem->mem.maxsize = 0;
1002     mem->mem.size = 0;
1003     mem->data = NULL;
1004
1005     group->planes[i].length = 0;
1006     group->planes[i].bytesused = 0;
1007     group->planes[i].m.userptr = 0;
1008   }
1009
1010   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1011     group->buffer.bytesused = 0;
1012     group->buffer.length = 0;
1013     group->buffer.m.userptr = 0;
1014   }
1015 }
1016
1017 GstV4l2MemoryGroup *
1018 gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator)
1019 {
1020   GstV4l2MemoryGroup *group;
1021   gint i;
1022
1023   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, NULL);
1024
1025   group = gst_v4l2_allocator_alloc (allocator);
1026
1027   if (group == NULL)
1028     return NULL;
1029
1030   for (i = 0; i < group->n_mem; i++) {
1031
1032     GST_LOG_OBJECT (allocator, "allocating empty USERPTR group");
1033
1034     if (group->mem[i] == NULL) {
1035       group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator),
1036           NULL, 0, 0, 0, 0, i, NULL, -1, group);
1037     } else {
1038       /* Take back the allocator reference */
1039       gst_object_ref (allocator);
1040     }
1041
1042     group->mems_allocated++;
1043   }
1044
1045   gst_v4l2_allocator_clear_userptr (allocator, group);
1046
1047   return group;
1048 }
1049
1050 gboolean
1051 gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
1052     GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem)
1053 {
1054   GstV4l2Memory *mem;
1055   gint i;
1056
1057   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE);
1058
1059   if (group->n_mem != n_mem)
1060     goto n_mem_missmatch;
1061
1062   for (i = 0; i < group->n_mem; i++) {
1063     gint dmafd;
1064     gsize size, offset, maxsize;
1065
1066     if (!gst_is_dmabuf_memory (dma_mem[i]))
1067       goto not_dmabuf;
1068
1069     size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize);
1070
1071     if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0)
1072       goto dup_failed;
1073
1074     GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i);
1075
1076     mem = (GstV4l2Memory *) group->mem[i];
1077
1078     /* Update memory */
1079     mem->mem.maxsize = maxsize;
1080     mem->mem.offset = offset;
1081     mem->mem.size = size;
1082     mem->dmafd = dmafd;
1083
1084     /* Update v4l2 structure */
1085     group->planes[i].length = maxsize;
1086     group->planes[i].bytesused = size;
1087     group->planes[i].m.fd = dmafd;
1088     group->planes[i].data_offset = offset;
1089   }
1090
1091   /* Copy into buffer structure if not using planes */
1092   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1093     group->buffer.bytesused = group->planes[0].bytesused;
1094     group->buffer.length = group->planes[0].length;
1095     group->buffer.m.fd = group->planes[0].m.userptr;
1096   } else {
1097     group->buffer.length = group->n_mem;
1098   }
1099
1100   return TRUE;
1101
1102 n_mem_missmatch:
1103   {
1104     GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem,
1105         group->n_mem);
1106     return FALSE;
1107   }
1108 not_dmabuf:
1109   {
1110     GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i);
1111     return FALSE;
1112   }
1113 dup_failed:
1114   {
1115     GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s",
1116         g_strerror (errno));
1117     return FALSE;
1118   }
1119 }
1120
1121 gboolean
1122 gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
1123     GstV4l2MemoryGroup * group, gsize img_size, int n_planes,
1124     gpointer * data, gsize * offset)
1125 {
1126   GstV4l2Memory *mem;
1127   gint i;
1128
1129   g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE);
1130
1131   /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */
1132   if (n_planes != group->n_mem)
1133     goto n_mem_missmatch;
1134
1135   for (i = 0; i < group->n_mem; i++) {
1136     gsize size, maxsize;
1137
1138     if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1139       struct v4l2_pix_format_mplane *pix = &allocator->format.fmt.pix_mp;
1140       maxsize = pix->plane_fmt[i].sizeimage;
1141     } else {
1142       maxsize = allocator->format.fmt.pix.sizeimage;
1143     }
1144
1145     if ((i + 1) == n_planes) {
1146       size = img_size - offset[i];
1147     } else {
1148       size = offset[i + 1] - offset[i];
1149     }
1150
1151     g_assert (size <= img_size);
1152
1153     GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
1154         G_GSIZE_FORMAT, data[i], i, size);
1155
1156     mem = (GstV4l2Memory *) group->mem[i];
1157
1158     mem->mem.maxsize = maxsize;
1159     mem->mem.size = size;
1160     mem->data = data[i];
1161
1162     group->planes[i].length = maxsize;
1163     group->planes[i].bytesused = size;
1164     group->planes[i].m.userptr = (unsigned long) data[i];
1165     group->planes[i].data_offset = 0;
1166   }
1167
1168   /* Copy into buffer structure if not using planes */
1169   if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1170     group->buffer.bytesused = group->planes[0].bytesused;
1171     group->buffer.length = group->planes[0].length;
1172     group->buffer.m.userptr = group->planes[0].m.userptr;
1173   } else {
1174     group->buffer.length = group->n_mem;
1175   }
1176
1177   return TRUE;
1178
1179 n_mem_missmatch:
1180   {
1181     GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i",
1182         n_planes, group->n_mem);
1183     return FALSE;
1184   }
1185 }
1186
1187 void
1188 gst_v4l2_allocator_flush (GstV4l2Allocator * allocator)
1189 {
1190   gint i;
1191
1192   GST_OBJECT_LOCK (allocator);
1193
1194   if (!allocator->active)
1195     goto done;
1196
1197   for (i = 0; i < allocator->count; i++) {
1198     GstV4l2MemoryGroup *group = allocator->groups[i];
1199     gint n;
1200
1201     if (IS_QUEUED (group->buffer)) {
1202       UNSET_QUEUED (group->buffer);
1203
1204       gst_v4l2_allocator_reset_group (allocator, group);
1205
1206       for (n = 0; n < group->n_mem; n++)
1207         gst_memory_unref (group->mem[n]);
1208     }
1209   }
1210
1211 done:
1212   GST_OBJECT_UNLOCK (allocator);
1213 }
1214
1215 gboolean
1216 gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator,
1217     GstV4l2MemoryGroup * group)
1218 {
1219   gboolean ret = TRUE;
1220   gint i;
1221
1222   /* update sizes */
1223   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1224     for (i = 0; i < group->n_mem; i++)
1225       group->planes[i].bytesused =
1226           gst_memory_get_sizes (group->mem[i], NULL, NULL);
1227   } else {
1228     group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL);
1229   }
1230
1231   if (v4l2_ioctl (allocator->video_fd, VIDIOC_QBUF, &group->buffer) < 0) {
1232     GST_ERROR_OBJECT (allocator, "failed queing buffer %i: %s",
1233         group->buffer.index, g_strerror (errno));
1234     ret = FALSE;
1235     if (IS_QUEUED (group->buffer)) {
1236       GST_DEBUG_OBJECT (allocator,
1237           "driver pretends buffer is queued even if queue failed");
1238       UNSET_QUEUED (group->buffer);
1239     }
1240     goto done;
1241   }
1242
1243   GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)",
1244       group->buffer.index, group->buffer.flags);
1245
1246   if (!IS_QUEUED (group->buffer)) {
1247     GST_DEBUG_OBJECT (allocator,
1248         "driver pretends buffer is not queued even if queue succeeded");
1249     SET_QUEUED (group->buffer);
1250   }
1251
1252   /* Ensure the memory will stay around and is RO */
1253   for (i = 0; i < group->n_mem; i++)
1254     gst_memory_ref (group->mem[i]);
1255
1256 done:
1257   return ret;
1258 }
1259
1260 GstV4l2MemoryGroup *
1261 gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator)
1262 {
1263   struct v4l2_buffer buffer = { 0 };
1264   struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} };
1265   gint i;
1266
1267   GstV4l2MemoryGroup *group = NULL;
1268
1269   buffer.type = allocator->type;
1270   buffer.memory = allocator->memory;
1271
1272   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1273     buffer.length = allocator->format.fmt.pix_mp.num_planes;
1274     buffer.m.planes = planes;
1275   }
1276
1277   if (v4l2_ioctl (allocator->video_fd, VIDIOC_DQBUF, &buffer) < 0)
1278     goto error;
1279
1280   group = allocator->groups[buffer.index];
1281   group->buffer = buffer;
1282
1283   GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index,
1284       buffer.flags);
1285
1286   if (IS_QUEUED (group->buffer)) {
1287     GST_DEBUG_OBJECT (allocator,
1288         "driver pretends buffer is queued even if dequeue succeeded");
1289     UNSET_QUEUED (group->buffer);
1290   }
1291
1292   if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) {
1293     group->buffer.m.planes = group->planes;
1294     memcpy (group->planes, buffer.m.planes, sizeof (planes));
1295   } else {
1296     group->planes[0].bytesused = group->buffer.bytesused;
1297     group->planes[0].length = group->buffer.length;
1298     g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m));
1299     memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m));
1300   }
1301
1302   /* And update memory size */
1303   if (V4L2_TYPE_IS_OUTPUT (allocator->type)) {
1304     gst_v4l2_allocator_reset_size (allocator, group);
1305   } else {
1306     /* for capture, simply read the size */
1307     for (i = 0; i < group->n_mem; i++) {
1308       gst_memory_resize (group->mem[i], 0, group->planes[i].bytesused);
1309     }
1310   }
1311
1312   /* Release the memory, possibly making it RW again */
1313   for (i = 0; i < group->n_mem; i++)
1314     gst_memory_unref (group->mem[i]);
1315
1316   return group;
1317
1318 error:
1319   GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s",
1320       memory_type_to_str (allocator->memory), g_strerror (errno));
1321
1322   switch (errno) {
1323     case EAGAIN:
1324       GST_WARNING_OBJECT (allocator,
1325           "Non-blocking I/O has been selected using O_NONBLOCK and"
1326           " no buffer was in the outgoing queue.");
1327       break;
1328     case EINVAL:
1329       GST_ERROR_OBJECT (allocator,
1330           "The buffer type is not supported, or the index is out of bounds, "
1331           "or no buffers have been allocated yet, or the userptr "
1332           "or length are invalid.");
1333       break;
1334     case ENOMEM:
1335       GST_ERROR_OBJECT (allocator,
1336           "insufficient memory to enqueue a user pointer buffer");
1337       break;
1338     case EIO:
1339       GST_INFO_OBJECT (allocator,
1340           "VIDIOC_DQBUF failed due to an internal error."
1341           " Can also indicate temporary problems like signal loss."
1342           " Note the driver might dequeue an (empty) buffer despite"
1343           " returning an error, or even stop capturing.");
1344       /* have we de-queued a buffer ? */
1345       if (!IS_QUEUED (buffer)) {
1346         GST_DEBUG_OBJECT (allocator, "reenqueing buffer");
1347         /* FIXME ... should we do something here? */
1348       }
1349       break;
1350     case EINTR:
1351       GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device");
1352       break;
1353     default:
1354       GST_WARNING_OBJECT (allocator,
1355           "Grabbing frame got interrupted unexpectedly. %d: %s.", errno,
1356           g_strerror (errno));
1357       break;
1358   }
1359
1360   return NULL;
1361 }
1362
1363 void
1364 gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
1365     GstV4l2MemoryGroup * group)
1366 {
1367   switch (allocator->memory) {
1368     case V4L2_MEMORY_USERPTR:
1369       gst_v4l2_allocator_clear_userptr (allocator, group);
1370       break;
1371     case V4L2_MEMORY_DMABUF:
1372       gst_v4l2_allocator_clear_dmabufin (allocator, group);
1373       break;
1374     case V4L2_MEMORY_MMAP:
1375       break;
1376     default:
1377       g_assert_not_reached ();
1378       break;
1379   }
1380
1381   gst_v4l2_allocator_reset_size (allocator, group);
1382 }
1383
1384 gsize
1385 gst_v4l2_allocator_num_allocated (GstV4l2Allocator * allocator)
1386 {
1387   gsize num_allocated;
1388
1389   GST_OBJECT_LOCK (allocator);
1390
1391   num_allocated = allocator->count;
1392
1393   GST_OBJECT_UNLOCK (allocator);
1394
1395   return num_allocated;
1396 }