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 GCond buffer_cond;
49 gboolean flushing;
50
51 GstV4l2Decoder *decoder;
52 GstPadDirection direction;
53 };
54
55 G_DEFINE_TYPE_WITH_CODE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator,
56 GST_TYPE_DMABUF_ALLOCATOR,
57 GST_DEBUG_CATEGORY_INIT (allocator_debug, "v4l2codecs-allocator", 0,
58 "V4L2 Codecs Allocator"));
59
60 static gboolean gst_v4l2_codec_allocator_release (GstMiniObject * mini_object);
61
62 static GQuark
gst_v4l2_codec_buffer_quark(void)63 gst_v4l2_codec_buffer_quark (void)
64 {
65 static gsize buffer_quark = 0;
66
67 if (g_once_init_enter (&buffer_quark)) {
68 GQuark quark = g_quark_from_string ("GstV4l2CodecBuffer");
69 g_once_init_leave (&buffer_quark, quark);
70 }
71
72 return buffer_quark;
73 }
74
75 static GstV4l2CodecBuffer *
gst_v4l2_codec_buffer_new(GstAllocator * allocator,GstV4l2Decoder * decoder,GstPadDirection direction,gint index)76 gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder,
77 GstPadDirection direction, gint index)
78 {
79 GstV4l2CodecBuffer *buf;
80 guint i, num_mems;
81 gint fds[GST_VIDEO_MAX_PLANES];
82 gsize sizes[GST_VIDEO_MAX_PLANES];
83 gsize offsets[GST_VIDEO_MAX_PLANES];
84
85 if (!gst_v4l2_decoder_export_buffer (decoder, direction, index, fds, sizes,
86 offsets, &num_mems))
87 return NULL;
88
89 buf = g_new0 (GstV4l2CodecBuffer, 1);
90 buf->index = index;
91 buf->num_mems = num_mems;
92 for (i = 0; i < buf->num_mems; i++) {
93 GstMemory *mem = gst_fd_allocator_alloc (allocator, fds[i], sizes[i],
94 GST_FD_MEMORY_FLAG_KEEP_MAPPED);
95 gst_memory_resize (mem, offsets[i], sizes[i] - offsets[i]);
96
97 GST_MINI_OBJECT (mem)->dispose = gst_v4l2_codec_allocator_release;
98 gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
99 gst_v4l2_codec_buffer_quark (), buf, NULL);
100
101 /* On outstanding memory keeps a reference on the allocator, this is
102 * needed to break the cycle. */
103 gst_object_unref (mem->allocator);
104 buf->mem[i] = mem;
105 }
106
107 GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds",
108 buf->index, buf->num_mems);
109
110 return buf;
111 }
112
113 static void
gst_v4l2_codec_buffer_free(GstV4l2CodecBuffer * buf)114 gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf)
115 {
116 guint i;
117
118 g_warn_if_fail (buf->outstanding_mems == 0);
119
120 GST_DEBUG_OBJECT (buf->mem[0]->allocator, "Freeing buffer %i", buf->index);
121
122 for (i = 0; i < buf->num_mems; i++) {
123 GstMemory *mem = buf->mem[i];
124 GST_MINI_OBJECT (mem)->dispose = NULL;
125 g_object_ref (mem->allocator);
126 gst_memory_unref (mem);
127 }
128
129 g_free (buf);
130 }
131
132 static void
gst_v4l2_codec_buffer_acquire(GstV4l2CodecBuffer * buf)133 gst_v4l2_codec_buffer_acquire (GstV4l2CodecBuffer * buf)
134 {
135 buf->outstanding_mems += buf->num_mems;
136 }
137
138 static gboolean
gst_v4l2_codec_buffer_release_mem(GstV4l2CodecBuffer * buf)139 gst_v4l2_codec_buffer_release_mem (GstV4l2CodecBuffer * buf)
140 {
141 return (--buf->outstanding_mems == 0);
142 }
143
144 static gboolean
gst_v4l2_codec_allocator_release(GstMiniObject * mini_object)145 gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
146 {
147 GstMemory *mem = GST_MEMORY_CAST (mini_object);
148 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (mem->allocator);
149 GstV4l2CodecBuffer *buf;
150
151 GST_OBJECT_LOCK (self);
152
153 buf = gst_mini_object_get_qdata (mini_object, gst_v4l2_codec_buffer_quark ());
154 gst_memory_ref (mem);
155
156 if (gst_v4l2_codec_buffer_release_mem (buf)) {
157 GST_DEBUG_OBJECT (self, "Placing back buffer %i into pool", buf->index);
158 g_queue_push_tail (&self->pool, buf);
159 g_cond_signal (&self->buffer_cond);
160 }
161
162 GST_OBJECT_UNLOCK (self);
163
164 /* Keep last in case we are holding on the last allocator ref */
165 g_object_unref (mem->allocator);
166
167 /* Returns FALSE so that our mini object isn't freed */
168 return FALSE;
169 }
170
171 static gboolean
gst_v4l2_codec_allocator_prepare(GstV4l2CodecAllocator * self)172 gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
173 {
174 GstV4l2Decoder *decoder = self->decoder;
175 GstPadDirection direction = self->direction;
176 gint ret;
177 guint i;
178
179 ret = gst_v4l2_decoder_request_buffers (decoder, direction, self->pool_size);
180 if (ret < self->pool_size) {
181 if (ret >= 0)
182 GST_ERROR_OBJECT (self,
183 "%i buffer was needed, but only %i could be allocated",
184 self->pool_size, ret);
185 goto failed;
186 }
187
188 for (i = 0; i < self->pool_size; i++) {
189 GstV4l2CodecBuffer *buf = gst_v4l2_codec_buffer_new (GST_ALLOCATOR (self),
190 decoder, direction, i);
191 g_queue_push_tail (&self->pool, buf);
192 }
193
194 return TRUE;
195
196 failed:
197 gst_v4l2_decoder_request_buffers (decoder, direction, 0);
198 return FALSE;
199 }
200
201 static void
gst_v4l2_codec_allocator_init(GstV4l2CodecAllocator * self)202 gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self)
203 {
204 g_cond_init (&self->buffer_cond);
205 }
206
207 static void
gst_v4l2_codec_allocator_dispose(GObject * object)208 gst_v4l2_codec_allocator_dispose (GObject * object)
209 {
210 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
211 GstV4l2CodecBuffer *buf;
212
213 while ((buf = g_queue_pop_head (&self->pool)))
214 gst_v4l2_codec_buffer_free (buf);
215
216 if (self->decoder) {
217 gst_v4l2_codec_allocator_detach (self);
218 gst_clear_object (&self->decoder);
219 }
220
221 G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object);
222 }
223
224 static void
gst_v4l2_codec_allocator_finalize(GObject * object)225 gst_v4l2_codec_allocator_finalize (GObject * object)
226 {
227 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
228
229 g_cond_clear (&self->buffer_cond);
230
231 G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->finalize (object);
232 }
233
234 static void
gst_v4l2_codec_allocator_class_init(GstV4l2CodecAllocatorClass * klass)235 gst_v4l2_codec_allocator_class_init (GstV4l2CodecAllocatorClass * klass)
236 {
237 GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
238 GObjectClass *object_class = G_OBJECT_CLASS (klass);
239
240 object_class->dispose = gst_v4l2_codec_allocator_dispose;
241 object_class->finalize = gst_v4l2_codec_allocator_finalize;
242 allocator_class->alloc = NULL;
243 }
244
245 GstV4l2CodecAllocator *
gst_v4l2_codec_allocator_new(GstV4l2Decoder * decoder,GstPadDirection direction,guint num_buffers)246 gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
247 GstPadDirection direction, guint num_buffers)
248 {
249 GstV4l2CodecAllocator *self =
250 g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL);
251
252 self->decoder = g_object_ref (decoder);
253 self->direction = direction;
254 self->pool_size = num_buffers;
255
256 if (!gst_v4l2_codec_allocator_prepare (self)) {
257 g_object_unref (self);
258 return NULL;
259 }
260
261 return self;
262 }
263
264 GstMemory *
gst_v4l2_codec_allocator_alloc(GstV4l2CodecAllocator * self)265 gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * self)
266 {
267 GstV4l2CodecBuffer *buf;
268 GstMemory *mem = NULL;
269
270 GST_OBJECT_LOCK (self);
271 buf = g_queue_pop_head (&self->pool);
272 if (buf) {
273 GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
274 g_warn_if_fail (buf->num_mems == 1);
275 mem = buf->mem[0];
276 g_object_ref (mem->allocator);
277 buf->outstanding_mems++;
278 }
279 GST_OBJECT_UNLOCK (self);
280
281 return mem;
282 }
283
284 gboolean
gst_v4l2_codec_allocator_create_buffer(GstV4l2CodecAllocator * self)285 gst_v4l2_codec_allocator_create_buffer (GstV4l2CodecAllocator * self)
286 {
287 /* TODO implement */
288 return FALSE;
289 }
290
291 gboolean
gst_v4l2_codec_allocator_wait_for_buffer(GstV4l2CodecAllocator * self)292 gst_v4l2_codec_allocator_wait_for_buffer (GstV4l2CodecAllocator * self)
293 {
294 gboolean ret;
295
296 GST_OBJECT_LOCK (self);
297 while (self->pool.length == 0 && !self->flushing)
298 g_cond_wait (&self->buffer_cond, GST_OBJECT_GET_LOCK (self));
299 ret = !self->flushing;
300 GST_OBJECT_UNLOCK (self);
301
302 return ret;
303 }
304
305 gboolean
gst_v4l2_codec_allocator_prepare_buffer(GstV4l2CodecAllocator * self,GstBuffer * gstbuf)306 gst_v4l2_codec_allocator_prepare_buffer (GstV4l2CodecAllocator * self,
307 GstBuffer * gstbuf)
308 {
309 GstV4l2CodecBuffer *buf;
310 guint i;
311
312 GST_OBJECT_LOCK (self);
313
314 buf = g_queue_pop_head (&self->pool);
315 if (!buf) {
316 GST_OBJECT_UNLOCK (self);
317 return FALSE;
318 }
319
320 GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
321
322 gst_v4l2_codec_buffer_acquire (buf);
323 for (i = 0; i < buf->num_mems; i++) {
324 gst_buffer_append_memory (gstbuf, buf->mem[i]);
325 g_object_ref (buf->mem[i]->allocator);
326 }
327
328 GST_OBJECT_UNLOCK (self);
329
330 return TRUE;
331 }
332
333 guint
gst_v4l2_codec_allocator_get_pool_size(GstV4l2CodecAllocator * self)334 gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator * self)
335 {
336 guint size;
337
338 GST_OBJECT_LOCK (self);
339 size = self->pool_size;
340 GST_OBJECT_UNLOCK (self);
341
342 return size;
343 }
344
345 void
gst_v4l2_codec_allocator_detach(GstV4l2CodecAllocator * self)346 gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
347 {
348 GST_OBJECT_LOCK (self);
349 if (!self->detached) {
350 self->detached = TRUE;
351 gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
352 }
353 GST_OBJECT_UNLOCK (self);
354 }
355
356 void
gst_v4l2_codec_allocator_set_flushing(GstV4l2CodecAllocator * self,gboolean flushing)357 gst_v4l2_codec_allocator_set_flushing (GstV4l2CodecAllocator * self,
358 gboolean flushing)
359 {
360 GST_OBJECT_LOCK (self);
361 self->flushing = flushing;
362 if (flushing)
363 g_cond_broadcast (&self->buffer_cond);
364 GST_OBJECT_UNLOCK (self);
365 }
366
367 guint32
gst_v4l2_codec_memory_get_index(GstMemory * mem)368 gst_v4l2_codec_memory_get_index (GstMemory * mem)
369 {
370 GstV4l2CodecBuffer *buf;
371
372 buf = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
373 gst_v4l2_codec_buffer_quark ());
374 g_return_val_if_fail (buf, G_MAXUINT32);
375
376 return buf->index;
377 }
378