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