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