2 * Copyright (C) 2020 Collabora Ltd.
3 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
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.
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.
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.
21 #include "gstv4l2codecallocator.h"
23 #include <gst/video/video.h>
24 #include <sys/types.h>
26 #define GST_CAT_DEFAULT allocator_debug
27 GST_DEBUG_CATEGORY_STATIC (allocator_debug);
29 typedef struct _GstV4l2CodecBuffer GstV4l2CodecBuffer;
30 struct _GstV4l2CodecBuffer
34 GstMemory *mem[GST_VIDEO_MAX_PLANES];
37 guint outstanding_mems;
40 struct _GstV4l2CodecAllocator
42 GstDmaBufAllocator parent;
48 GstV4l2Decoder *decoder;
49 GstPadDirection direction;
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"));
57 static gboolean gst_v4l2_codec_allocator_release (GstMiniObject * mini_object);
60 gst_v4l2_codec_buffer_quark (void)
62 static gsize buffer_quark = 0;
64 if (g_once_init_enter (&buffer_quark)) {
65 GQuark quark = g_quark_from_string ("GstV4l2CodecBuffer");
66 g_once_init_leave (&buffer_quark, quark);
72 static GstV4l2CodecBuffer *
73 gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder,
74 GstPadDirection direction, gint index)
76 GstV4l2CodecBuffer *buf;
78 gint fds[GST_VIDEO_MAX_PLANES];
79 gsize sizes[GST_VIDEO_MAX_PLANES];
80 gsize offsets[GST_VIDEO_MAX_PLANES];
82 if (!gst_v4l2_decoder_export_buffer (decoder, direction, index, fds, sizes,
86 buf = g_new0 (GstV4l2CodecBuffer, 1);
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]);
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);
97 /* On outstanding memory keeps a reference on the allocator, this is
98 * needed to break the cycle. */
99 gst_object_unref (mem->allocator);
103 GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds",
104 buf->index, buf->num_mems);
110 gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf)
114 g_warn_if_fail (buf->outstanding_mems == 0);
116 GST_DEBUG_OBJECT (buf->mem[0]->allocator, "Freeing buffer %i", buf->index);
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);
129 gst_v4l2_codec_buffer_acquire (GstV4l2CodecBuffer * buf)
131 buf->outstanding_mems += buf->num_mems;
135 gst_v4l2_codec_buffer_release_mem (GstV4l2CodecBuffer * buf)
137 return (--buf->outstanding_mems == 0);
141 gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
143 GstMemory *mem = GST_MEMORY_CAST (mini_object);
144 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (mem->allocator);
145 GstV4l2CodecBuffer *buf;
147 GST_OBJECT_LOCK (self);
149 buf = gst_mini_object_get_qdata (mini_object, gst_v4l2_codec_buffer_quark ());
150 gst_memory_ref (mem);
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);
157 GST_OBJECT_UNLOCK (self);
159 /* Keep last in case we are holding on the last allocator ref */
160 g_object_unref (mem->allocator);
162 /* Returns FALSE so that our mini object isn't freed */
167 gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
169 GstV4l2Decoder *decoder = self->decoder;
170 GstPadDirection direction = self->direction;
174 ret = gst_v4l2_decoder_request_buffers (decoder, direction, self->pool_size);
175 if (ret < self->pool_size) {
177 GST_ERROR_OBJECT (self,
178 "%i buffer was needed, but only %i could be allocated",
179 self->pool_size, ret);
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);
192 gst_v4l2_decoder_request_buffers (decoder, direction, 0);
197 gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self)
202 gst_v4l2_codec_allocator_dispose (GObject * object)
204 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
205 GstV4l2CodecBuffer *buf;
207 while ((buf = g_queue_pop_head (&self->pool)))
208 gst_v4l2_codec_buffer_free (buf);
210 g_clear_object (&self->decoder);
212 G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object);
216 gst_v4l2_codec_allocator_finalize (GObject * object)
218 GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
221 gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
223 G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->finalize (object);
227 gst_v4l2_codec_allocator_class_init (GstV4l2CodecAllocatorClass * klass)
229 GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
230 GObjectClass *object_class = G_OBJECT_CLASS (klass);
232 object_class->dispose = gst_v4l2_codec_allocator_dispose;
233 object_class->finalize = gst_v4l2_codec_allocator_finalize;
234 allocator_class->alloc = NULL;
237 GstV4l2CodecAllocator *
238 gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
239 GstPadDirection direction, guint num_buffers)
241 GstV4l2CodecAllocator *self =
242 g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL);
244 self->decoder = g_object_ref (decoder);
245 self->direction = direction;
246 self->pool_size = num_buffers;
248 if (!gst_v4l2_codec_allocator_prepare (self)) {
249 g_object_unref (self);
257 gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * self)
259 GstV4l2CodecBuffer *buf;
260 GstMemory *mem = NULL;
262 GST_OBJECT_LOCK (self);
263 buf = g_queue_pop_head (&self->pool);
265 GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
266 g_warn_if_fail (buf->num_mems == 1);
268 g_object_ref (mem->allocator);
269 buf->outstanding_mems++;
271 GST_OBJECT_UNLOCK (self);
277 gst_v4l2_codec_allocator_prepare_buffer (GstV4l2CodecAllocator * self,
280 GstV4l2CodecBuffer *buf;
283 GST_OBJECT_LOCK (self);
285 buf = g_queue_pop_head (&self->pool);
287 GST_OBJECT_UNLOCK (self);
291 GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
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);
299 GST_OBJECT_UNLOCK (self);
305 gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator * self)
309 GST_OBJECT_LOCK (self);
310 size = self->pool_size;
311 GST_OBJECT_UNLOCK (self);
317 gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
319 GST_OBJECT_LOCK (self);
320 if (!self->detached) {
321 self->detached = TRUE;
322 gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
324 GST_OBJECT_UNLOCK (self);