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