• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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