v4l2h264codecdec: Copy bitstream parameter and data
[platform/upstream/gstreamer.git] / sys / v4l2codecs / gstv4l2codecallocator.c
1 /* GStreamer
2  * Copyright (C) 2020 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 #include "gstv4l2codecallocator.h"
22
23 #include <gst/video/video.h>
24 #include <sys/types.h>
25
26 #define GST_CAT_DEFAULT allocator_debug
27 GST_DEBUG_CATEGORY_STATIC (allocator_debug);
28
29 typedef struct _GstV4l2CodecBuffer GstV4l2CodecBuffer;
30 struct _GstV4l2CodecBuffer
31 {
32   gint index;
33
34   GstMemory *mem[GST_VIDEO_MAX_PLANES];
35   guint num_mems;
36
37   guint outstanding_mems;
38 };
39
40 struct _GstV4l2CodecAllocator
41 {
42   GstDmaBufAllocator parent;
43
44   GQueue pool;
45   gint pool_size;
46   gboolean detached;
47
48   GstV4l2Decoder *decoder;
49   GstPadDirection direction;
50 };
51
52 G_DEFINE_TYPE_WITH_CODE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator,
53     GST_TYPE_DMABUF_ALLOCATOR,
54     GST_DEBUG_CATEGORY_INIT (allocator_debug, "v4l2codecs-allocator", 0,
55         "V4L2 Codecs Allocator"));
56
57 static gboolean gst_v4l2_codec_allocator_release (GstMiniObject * mini_object);
58
59 static GQuark
60 gst_v4l2_codec_buffer_quark (void)
61 {
62   static gsize buffer_quark = 0;
63
64   if (g_once_init_enter (&buffer_quark)) {
65     GQuark quark = g_quark_from_string ("GstV4l2CodecBuffer");
66     g_once_init_leave (&buffer_quark, quark);
67   }
68
69   return buffer_quark;
70 }
71
72 static GstV4l2CodecBuffer *
73 gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder,
74     GstPadDirection direction, gint index)
75 {
76   GstV4l2CodecBuffer *buf;
77   guint i, num_mems;
78   gint fds[GST_VIDEO_MAX_PLANES];
79   gsize sizes[GST_VIDEO_MAX_PLANES];
80   gsize offsets[GST_VIDEO_MAX_PLANES];
81
82   if (!gst_v4l2_decoder_export_buffer (decoder, direction, index, fds, sizes,
83           offsets, &num_mems))
84     return NULL;
85
86   buf = g_new0 (GstV4l2CodecBuffer, 1);
87   buf->index = index;
88   buf->num_mems = num_mems;
89   for (i = 0; i < buf->num_mems; i++) {
90     GstMemory *mem = gst_dmabuf_allocator_alloc (allocator, fds[i], sizes[i]);
91     gst_memory_resize (mem, offsets[i], sizes[i] - offsets[i]);
92
93     GST_MINI_OBJECT (mem)->dispose = gst_v4l2_codec_allocator_release;
94     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
95         gst_v4l2_codec_buffer_quark (), buf, NULL);
96
97     /* On outstanding memory keeps a reference on the allocator, this is
98      * needed to break the cycle. */
99     gst_object_unref (mem->allocator);
100     buf->mem[i] = mem;
101   }
102
103   GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds",
104       buf->index, buf->num_mems);
105
106   return buf;
107 }
108
109 static void
110 gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf)
111 {
112   guint i;
113
114   g_warn_if_fail (buf->outstanding_mems == 0);
115
116   GST_DEBUG_OBJECT (buf->mem[0]->allocator, "Freeing buffer %i", buf->index);
117
118   for (i = 0; i < buf->num_mems; i++) {
119     GstMemory *mem = buf->mem[i];
120     GST_MINI_OBJECT (mem)->dispose = NULL;
121     g_object_ref (mem->allocator);
122     gst_memory_unref (mem);
123   }
124
125   g_free (buf);
126 }
127
128 static void
129 gst_v4l2_codec_buffer_acquire (GstV4l2CodecBuffer * buf)
130 {
131   buf->outstanding_mems += buf->num_mems;
132 }
133
134 static gboolean
135 gst_v4l2_codec_buffer_release_mem (GstV4l2CodecBuffer * buf)
136 {
137   return (--buf->outstanding_mems == 0);
138 }
139
140 static gboolean
141 gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
142 {
143   GstMemory *mem = GST_MEMORY_CAST (mini_object);
144   GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (mem->allocator);
145   GstV4l2CodecBuffer *buf;
146
147   GST_OBJECT_LOCK (self);
148
149   buf = gst_mini_object_get_qdata (mini_object, gst_v4l2_codec_buffer_quark ());
150   gst_memory_ref (mem);
151
152   if (gst_v4l2_codec_buffer_release_mem (buf)) {
153     GST_DEBUG_OBJECT (self, "Placing back buffer %i into pool", buf->index);
154     g_queue_push_tail (&self->pool, buf);
155   }
156
157   GST_OBJECT_UNLOCK (self);
158
159   /* Keep last in case we are holding on the last allocator ref */
160   g_object_unref (mem->allocator);
161
162   /* Returns FALSE so that our mini object isn't freed */
163   return FALSE;
164 }
165
166 static gboolean
167 gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
168 {
169   GstV4l2Decoder *decoder = self->decoder;
170   GstPadDirection direction = self->direction;
171   gint ret;
172   guint i;
173
174   ret = gst_v4l2_decoder_request_buffers (decoder, direction, self->pool_size);
175   if (ret < self->pool_size) {
176     if (ret >= 0)
177       GST_ERROR_OBJECT (self,
178           "%i buffer was needed, but only %i could be allocated",
179           self->pool_size, ret);
180     goto failed;
181   }
182
183   for (i = 0; i < self->pool_size; i++) {
184     GstV4l2CodecBuffer *buf = gst_v4l2_codec_buffer_new (GST_ALLOCATOR (self),
185         decoder, direction, i);
186     g_queue_push_tail (&self->pool, buf);
187   }
188
189   return TRUE;
190
191 failed:
192   gst_v4l2_decoder_request_buffers (decoder, direction, 0);
193   return FALSE;
194 }
195
196 static void
197 gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self)
198 {
199 }
200
201 static void
202 gst_v4l2_codec_allocator_dispose (GObject * object)
203 {
204   GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
205   GstV4l2CodecBuffer *buf;
206
207   while ((buf = g_queue_pop_head (&self->pool)))
208     gst_v4l2_codec_buffer_free (buf);
209
210   g_clear_object (&self->decoder);
211
212   G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object);
213 }
214
215 static void
216 gst_v4l2_codec_allocator_finalize (GObject * object)
217 {
218   GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
219
220   if (!self->detached)
221     gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
222
223   G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->finalize (object);
224 }
225
226 static void
227 gst_v4l2_codec_allocator_class_init (GstV4l2CodecAllocatorClass * klass)
228 {
229   GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
230   GObjectClass *object_class = G_OBJECT_CLASS (klass);
231
232   object_class->dispose = gst_v4l2_codec_allocator_dispose;
233   object_class->finalize = gst_v4l2_codec_allocator_finalize;
234   allocator_class->alloc = NULL;
235 }
236
237 GstV4l2CodecAllocator *
238 gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
239     GstPadDirection direction, guint num_buffers)
240 {
241   GstV4l2CodecAllocator *self =
242       g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL);
243
244   self->decoder = g_object_ref (decoder);
245   self->direction = direction;
246   self->pool_size = num_buffers;
247
248   if (!gst_v4l2_codec_allocator_prepare (self)) {
249     g_object_unref (self);
250     return NULL;
251   }
252
253   return self;
254 }
255
256 GstMemory *
257 gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * self)
258 {
259   GstV4l2CodecBuffer *buf;
260   GstMemory *mem = NULL;
261
262   GST_OBJECT_LOCK (self);
263   buf = g_queue_pop_head (&self->pool);
264   if (buf) {
265     GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
266     g_warn_if_fail (buf->num_mems == 1);
267     mem = buf->mem[0];
268     g_object_ref (mem->allocator);
269     buf->outstanding_mems++;
270   }
271   GST_OBJECT_UNLOCK (self);
272
273   return mem;
274 }
275
276 gboolean
277 gst_v4l2_codec_allocator_prepare_buffer (GstV4l2CodecAllocator * self,
278     GstBuffer * gstbuf)
279 {
280   GstV4l2CodecBuffer *buf;
281   guint i;
282
283   GST_OBJECT_LOCK (self);
284
285   buf = g_queue_pop_head (&self->pool);
286   if (!buf) {
287     GST_OBJECT_UNLOCK (self);
288     return FALSE;
289   }
290
291   GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
292
293   gst_v4l2_codec_buffer_acquire (buf);
294   for (i = 0; i < buf->num_mems; i++) {
295     gst_buffer_append_memory (gstbuf, buf->mem[i]);
296     g_object_ref (buf->mem[i]->allocator);
297   }
298
299   GST_OBJECT_UNLOCK (self);
300
301   return TRUE;
302 }
303
304 guint
305 gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator * self)
306 {
307   guint size;
308
309   GST_OBJECT_LOCK (self);
310   size = self->pool_size;
311   GST_OBJECT_UNLOCK (self);
312
313   return size;
314 }
315
316 void
317 gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
318 {
319   GST_OBJECT_LOCK (self);
320   if (!self->detached) {
321     self->detached = TRUE;
322     gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
323   }
324   GST_OBJECT_UNLOCK (self);
325 }