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