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