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