omxvideodec: support interlace-mode=interleaved input
[platform/upstream/gstreamer.git] / omx / gstomxallocator.c
1 /*
2  * Copyright (C) 2019, Collabora Ltd.
3  *   Author: George Kiagiadakis <george.kiagiadakis@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation
8  * version 2.1 of the License.
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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstomxallocator.h"
26 #include <gst/allocators/gstdmabuf.h>
27
28 GST_DEBUG_CATEGORY_STATIC (gst_omx_allocator_debug_category);
29 #define GST_CAT_DEFAULT gst_omx_allocator_debug_category
30
31 #define DEBUG_INIT \
32   GST_DEBUG_CATEGORY_INIT (gst_omx_allocator_debug_category, "omxallocator", 0, \
33       "debug category for gst-omx allocator class");
34
35 G_DEFINE_TYPE_WITH_CODE (GstOMXAllocator, gst_omx_allocator, GST_TYPE_ALLOCATOR,
36     DEBUG_INIT);
37
38 enum
39 {
40   SIG_OMXBUF_RELEASED,
41   SIG_FOREIGN_MEM_RELEASED,
42   LAST_SIGNAL
43 };
44
45 static guint signals[LAST_SIGNAL] = { 0 };
46
47 /* Custom allocator for memory associated with OpenMAX buffers
48  *
49  * The main purpose of this allocator is to track memory that is associated
50  * with OpenMAX buffers, so that we know when the buffers can be released
51  * back to OpenMAX.
52  *
53  * This allocator looks and behaves more like a buffer pool. It allocates
54  * the memory objects before starting and sets a miniobject dispose function
55  * on them, which allows them to return when their last ref count is dropped.
56  *
57  * The type of memory that this allocator manages is GstOMXMemory. However, it
58  * is possible to manage a different type of memory, in which case the
59  * GstOMXMemory object is used only internally. There are two supported cases:
60  * - Allocate memory from the dmabuf allocator
61  * - Take memory that was allocated externally and manage it here
62  *
63  * In both cases, this allocator will replace the miniobject dispose function
64  * of these memory objects, so if they were acquired from here, they will also
65  * return here on their last unref.
66  *
67  * The caller initially needs to configure how many memory objects will be
68  * managed here by calling configure(). After that it needs to call
69  * set_active(TRUE) and finally allocate() for each memory. Allocation is done
70  * like this to facilitate calling allocate() from the alloc() function of
71  * the buffer pool for each OMX buffer on the port.
72  *
73  * After the allocator has been activated and all buffers have been allocated,
74  * the acquire() method can be called to retrieve a memory object. acquire() can
75  * be given an OMX buffer index or pointer to locate and return the memory
76  * object that corresponds to this OMX buffer. If the buffer is already
77  * acquired, this will result in a GST_FLOW_ERROR.
78  *
79  * When the last reference count is dropped on a memory that was acquired from
80  * here, its dispose function will ref it again and allow it to be acquired
81  * again. In addition, the omxbuf-released signal is fired to let the caller
82  * know that it can return this OMX buffer to the port, as it is no longer
83  * used outside this allocator.
84  */
85
86 /******************/
87 /** GstOMXMemory **/
88 /******************/
89
90 #define GST_OMX_MEMORY_TYPE "openmax"
91
92 GQuark
93 gst_omx_memory_quark (void)
94 {
95   static GQuark quark = 0;
96
97   if (quark == 0)
98     quark = g_quark_from_static_string ("GstOMXMemory");
99
100   return quark;
101 }
102
103 static GstOMXMemory *
104 gst_omx_memory_new (GstOMXAllocator * allocator, GstOMXBuffer * omx_buf,
105     GstMemoryFlags flags, GstMemory * parent, gssize offset, gssize size)
106 {
107   GstOMXMemory *mem;
108   gint align;
109   gsize maxsize;
110
111   /* GStreamer uses a bitmask for the alignment while
112    * OMX uses the alignment itself. So we have to convert
113    * here */
114   align = allocator->port->port_def.nBufferAlignment;
115   if (align > 0)
116     align -= 1;
117   if (((align + 1) & align) != 0) {
118     GST_WARNING ("Invalid alignment that is not a power of two: %u",
119         (guint) allocator->port->port_def.nBufferAlignment);
120     align = 0;
121   }
122
123   maxsize = omx_buf->omx_buf->nAllocLen;
124
125   if (size == -1) {
126     size = maxsize - offset;
127   }
128
129   mem = g_slice_new0 (GstOMXMemory);
130   gst_memory_init (GST_MEMORY_CAST (mem), flags, (GstAllocator *) allocator,
131       parent, maxsize, align, offset, size);
132
133   mem->buf = omx_buf;
134
135   return mem;
136 }
137
138 static gpointer
139 gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
140 {
141   GstOMXMemory *omem = (GstOMXMemory *) mem;
142
143   /* if we are using foreign_mem, the GstOMXMemory should never appear
144    * anywhere outside this allocator, therefore it should never be mapped */
145   g_return_val_if_fail (!omem->foreign_mem, NULL);
146
147   return omem->buf->omx_buf->pBuffer;
148 }
149
150 static void
151 gst_omx_memory_unmap (GstMemory * mem)
152 {
153 }
154
155 static GstMemory *
156 gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size)
157 {
158   GstOMXMemory *omem = (GstOMXMemory *) mem;
159   GstOMXMemory *sub;
160   GstMemory *parent;
161
162   /* find the real parent */
163   if ((parent = mem->parent) == NULL)
164     parent = mem;
165
166   if (size == -1)
167     size = mem->size - offset;
168
169   /* the shared memory is always readonly */
170   sub = gst_omx_memory_new ((GstOMXAllocator *) mem->allocator, omem->buf,
171       GST_MINI_OBJECT_FLAGS (parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY,
172       parent, offset, size);
173
174   return (GstMemory *) sub;
175 }
176
177 GstOMXBuffer *
178 gst_omx_memory_get_omx_buf (GstMemory * mem)
179 {
180   GstOMXMemory *omx_mem;
181
182   if (GST_IS_OMX_ALLOCATOR (mem->allocator))
183     omx_mem = (GstOMXMemory *) mem;
184   else
185     omx_mem = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
186         GST_OMX_MEMORY_QUARK);
187
188   if (!omx_mem)
189     return NULL;
190
191   return omx_mem->buf;
192 }
193
194 /*********************/
195 /** GstOMXAllocator **/
196 /*********************/
197
198 static void
199 gst_omx_allocator_init (GstOMXAllocator * allocator)
200 {
201   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
202
203   alloc->mem_type = GST_OMX_MEMORY_TYPE;
204
205   alloc->mem_map = gst_omx_memory_map;
206   alloc->mem_unmap = gst_omx_memory_unmap;
207   alloc->mem_share = gst_omx_memory_share;
208   /* default copy & is_span */
209
210   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
211
212   g_mutex_init (&allocator->lock);
213   g_cond_init (&allocator->cond);
214 }
215
216 GstOMXAllocator *
217 gst_omx_allocator_new (GstOMXComponent * component, GstOMXPort * port)
218 {
219   GstOMXAllocator *allocator;
220
221   allocator = g_object_new (gst_omx_allocator_get_type (), NULL);
222   allocator->component = gst_omx_component_ref (component);
223   allocator->port = port;
224
225   return allocator;
226 }
227
228 static void
229 gst_omx_allocator_finalize (GObject * object)
230 {
231   GstOMXAllocator *allocator = GST_OMX_ALLOCATOR (object);
232
233   gst_omx_component_unref (allocator->component);
234   g_mutex_clear (&allocator->lock);
235   g_cond_clear (&allocator->cond);
236
237   G_OBJECT_CLASS (gst_omx_allocator_parent_class)->finalize (object);
238 }
239
240 gboolean
241 gst_omx_allocator_configure (GstOMXAllocator * allocator, guint count,
242     GstOMXAllocatorForeignMemMode mode)
243 {
244   /* check if already configured */
245   if (allocator->n_memories > 0)
246     return FALSE;
247
248   allocator->n_memories = count;
249   allocator->foreign_mode = mode;
250   if (mode == GST_OMX_ALLOCATOR_FOREIGN_MEM_DMABUF)
251     allocator->foreign_allocator = gst_dmabuf_allocator_new ();
252
253   return TRUE;
254 }
255
256 /* must be protected with allocator->lock */
257 static void
258 gst_omx_allocator_dealloc (GstOMXAllocator * allocator)
259 {
260   /* might be called more than once */
261   if (!allocator->memories)
262     return;
263
264   /* return foreign memory back to whoever lended it to us.
265    * the signal handler is expected to increase the ref count of foreign_mem */
266   if (allocator->foreign_mode == GST_OMX_ALLOCATOR_FOREIGN_MEM_OTHER_POOL) {
267     gint i;
268     GstOMXMemory *m;
269
270     for (i = 0; i < allocator->memories->len; i++) {
271       m = g_ptr_array_index (allocator->memories, i);
272
273       /* this should not happen, but let's not crash for this */
274       if (!m->foreign_mem) {
275         GST_WARNING_OBJECT (allocator, "no foreign_mem to release");
276         continue;
277       }
278
279       /* restore the original dispose function */
280       GST_MINI_OBJECT_CAST (m->foreign_mem)->dispose =
281           (GstMiniObjectDisposeFunction) m->foreign_dispose;
282
283       g_signal_emit (allocator, signals[SIG_FOREIGN_MEM_RELEASED], 0, i,
284           m->foreign_mem);
285     }
286   }
287
288   g_ptr_array_foreach (allocator->memories, (GFunc) gst_memory_unref, NULL);
289   g_ptr_array_free (allocator->memories, TRUE);
290   allocator->memories = NULL;
291   allocator->n_memories = 0;
292   allocator->foreign_mode = GST_OMX_ALLOCATOR_FOREIGN_MEM_NONE;
293   if (allocator->foreign_allocator) {
294     g_object_unref (allocator->foreign_allocator);
295     allocator->foreign_allocator = NULL;
296   }
297
298   g_cond_broadcast (&allocator->cond);
299 }
300
301 gboolean
302 gst_omx_allocator_set_active (GstOMXAllocator * allocator, gboolean active)
303 {
304   gboolean changed = FALSE;
305
306   /* on activation, _configure() must be called first */
307   g_return_val_if_fail (!active || allocator->n_memories > 0, FALSE);
308
309   g_mutex_lock (&allocator->lock);
310
311   if (allocator->active != active)
312     changed = TRUE;
313
314   if (changed) {
315     if (active) {
316       allocator->memories = g_ptr_array_sized_new (allocator->n_memories);
317       g_ptr_array_set_size (allocator->memories, allocator->n_memories);
318     } else {
319       if (g_atomic_int_get (&allocator->n_outstanding) == 0)
320         gst_omx_allocator_dealloc (allocator);
321     }
322   }
323
324   allocator->active = active;
325   g_mutex_unlock (&allocator->lock);
326
327   return changed;
328 }
329
330 void
331 gst_omx_allocator_wait_inactive (GstOMXAllocator * allocator)
332 {
333   g_mutex_lock (&allocator->lock);
334   while (allocator->memories)
335     g_cond_wait (&allocator->cond, &allocator->lock);
336   g_mutex_unlock (&allocator->lock);
337 }
338
339 static inline void
340 dec_outstanding (GstOMXAllocator * allocator)
341 {
342   if (g_atomic_int_dec_and_test (&allocator->n_outstanding)) {
343     /* keep a ref to the allocator because _dealloc() will free
344      * all the memories and the memories might be the only thing holding
345      * a reference to the allocator; we need to keep it alive until the
346      * end of this function call */
347     g_object_ref (allocator);
348
349     /* take the lock so that _set_active() is not run concurrently */
350     g_mutex_lock (&allocator->lock);
351
352     /* now that we have the lock, check if we have been de-activated with
353      * outstanding buffers */
354     if (!allocator->active)
355       gst_omx_allocator_dealloc (allocator);
356
357     g_mutex_unlock (&allocator->lock);
358     g_object_unref (allocator);
359   }
360 }
361
362 GstFlowReturn
363 gst_omx_allocator_acquire (GstOMXAllocator * allocator, GstMemory ** memory,
364     gint index, GstOMXBuffer * omx_buf)
365 {
366   GstFlowReturn ret = GST_FLOW_OK;
367   GstOMXMemory *omx_mem = NULL;
368
369   /* ensure memories are not going to disappear concurrently */
370   g_atomic_int_inc (&allocator->n_outstanding);
371
372   if (!allocator->active) {
373     ret = GST_FLOW_FLUSHING;
374     goto beach;
375   }
376
377   if (index >= 0 && index < allocator->n_memories)
378     omx_mem = g_ptr_array_index (allocator->memories, index);
379   else if (omx_buf) {
380     for (index = 0; index < allocator->n_memories; index++) {
381       omx_mem = g_ptr_array_index (allocator->memories, index);
382       if (omx_mem->buf == omx_buf)
383         break;
384     }
385   }
386
387   if (G_UNLIKELY (!omx_mem || index >= allocator->n_memories)) {
388     GST_ERROR_OBJECT (allocator, "Failed to find OMX memory");
389     ret = GST_FLOW_ERROR;
390     goto beach;
391   }
392
393   if (G_UNLIKELY (omx_mem->buf->used)) {
394     GST_ERROR_OBJECT (allocator,
395         "Trying to acquire a buffer that is being used by the OMX port");
396     ret = GST_FLOW_ERROR;
397     goto beach;
398   }
399
400   omx_mem->acquired = TRUE;
401
402   if (omx_mem->foreign_mem)
403     *memory = omx_mem->foreign_mem;
404   else
405     *memory = GST_MEMORY_CAST (omx_mem);
406
407 beach:
408   if (ret != GST_FLOW_OK)
409     dec_outstanding (allocator);
410   return ret;
411 }
412
413 /* installed as the GstMiniObject::dispose function of the acquired GstMemory */
414 static gboolean
415 gst_omx_allocator_memory_dispose (GstMemory * mem)
416 {
417   GstOMXMemory *omx_mem;
418   GstOMXAllocator *allocator;
419
420   /* memory may be from our allocator, but
421    * may as well be from the dmabuf allocator */
422   if (GST_IS_OMX_ALLOCATOR (mem->allocator))
423     omx_mem = (GstOMXMemory *) mem;
424   else
425     omx_mem = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
426         GST_OMX_MEMORY_QUARK);
427
428   if (omx_mem->acquired) {
429     /* keep the memory alive */
430     gst_memory_ref (mem);
431
432     omx_mem->acquired = FALSE;
433
434     allocator = GST_OMX_ALLOCATOR (GST_MEMORY_CAST (omx_mem)->allocator);
435
436     /* inform the upper layer that we are no longer using this GstOMXBuffer */
437     g_signal_emit (allocator, signals[SIG_OMXBUF_RELEASED], 0, omx_mem->buf);
438
439     dec_outstanding (allocator);
440
441     /* be careful here, both the memory and the allocator
442      * may have been free'd as part of the call to dec_outstanding() */
443
444     return FALSE;
445   }
446
447   /* if the foreign memory had a dispose function, let that one decide
448    * the fate of this memory. We are no longer going to be using it here */
449   if (omx_mem->foreign_dispose)
450     return omx_mem->foreign_dispose (GST_MINI_OBJECT_CAST (mem));
451
452   return TRUE;
453 }
454
455 static inline void
456 install_mem_dispose (GstOMXMemory * mem)
457 {
458   GstMemory *managed_mem = (GstMemory *) mem;
459
460   if (mem->foreign_mem) {
461     managed_mem = mem->foreign_mem;
462     mem->foreign_dispose = GST_MINI_OBJECT_CAST (managed_mem)->dispose;
463   }
464
465   GST_MINI_OBJECT_CAST (managed_mem)->dispose =
466       (GstMiniObjectDisposeFunction) gst_omx_allocator_memory_dispose;
467 }
468
469 /* the returned memory is transfer:none, ref still belongs to the allocator */
470 GstMemory *
471 gst_omx_allocator_allocate (GstOMXAllocator * allocator, gint index,
472     GstMemory * foreign_mem)
473 {
474   GstOMXMemory *mem;
475   GstOMXBuffer *omx_buf;
476
477   g_return_val_if_fail (allocator->port->buffers, NULL);
478   g_return_val_if_fail (allocator->memories, NULL);
479   g_return_val_if_fail (index >= 0 && index < allocator->n_memories, NULL);
480   g_return_val_if_fail ((foreign_mem == NULL &&
481           allocator->foreign_mode != GST_OMX_ALLOCATOR_FOREIGN_MEM_OTHER_POOL)
482       || (foreign_mem != NULL
483           && allocator->foreign_mode ==
484           GST_OMX_ALLOCATOR_FOREIGN_MEM_OTHER_POOL), NULL);
485
486   omx_buf = g_ptr_array_index (allocator->port->buffers, index);
487   g_return_val_if_fail (omx_buf != NULL, NULL);
488
489   mem = gst_omx_memory_new (allocator, omx_buf, 0, NULL, 0, -1);
490
491   switch (allocator->foreign_mode) {
492     case GST_OMX_ALLOCATOR_FOREIGN_MEM_NONE:
493       install_mem_dispose (mem);
494       break;
495     case GST_OMX_ALLOCATOR_FOREIGN_MEM_DMABUF:
496     {
497       gint fd = GPOINTER_TO_INT (omx_buf->omx_buf->pBuffer);
498       mem->foreign_mem =
499           gst_dmabuf_allocator_alloc (allocator->foreign_allocator, fd,
500           omx_buf->omx_buf->nAllocLen);
501       gst_mini_object_set_qdata (GST_MINI_OBJECT (mem->foreign_mem),
502           GST_OMX_MEMORY_QUARK, mem, NULL);
503       install_mem_dispose (mem);
504       break;
505     }
506     case GST_OMX_ALLOCATOR_FOREIGN_MEM_OTHER_POOL:
507       mem->foreign_mem = foreign_mem;
508       gst_mini_object_set_qdata (GST_MINI_OBJECT (mem->foreign_mem),
509           GST_OMX_MEMORY_QUARK, mem, NULL);
510       install_mem_dispose (mem);
511       break;
512     default:
513       g_assert_not_reached ();
514       break;
515   }
516
517   g_ptr_array_index (allocator->memories, index) = mem;
518   return mem->foreign_mem ? mem->foreign_mem : (GstMemory *) mem;
519 }
520
521 static void
522 gst_omx_allocator_free (GstAllocator * allocator, GstMemory * mem)
523 {
524   GstOMXMemory *omem = (GstOMXMemory *) mem;
525
526   g_warn_if_fail (!omem->acquired);
527
528   if (omem->foreign_mem)
529     gst_memory_unref (omem->foreign_mem);
530
531   g_slice_free (GstOMXMemory, omem);
532 }
533
534 static void
535 gst_omx_allocator_class_init (GstOMXAllocatorClass * klass)
536 {
537   GObjectClass *object_class;
538   GstAllocatorClass *allocator_class;
539
540   object_class = (GObjectClass *) klass;
541   allocator_class = (GstAllocatorClass *) klass;
542
543   object_class->finalize = gst_omx_allocator_finalize;
544   allocator_class->alloc = NULL;
545   allocator_class->free = gst_omx_allocator_free;
546
547   signals[SIG_OMXBUF_RELEASED] = g_signal_new ("omxbuf-released",
548       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
549       NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER);
550
551   signals[SIG_FOREIGN_MEM_RELEASED] = g_signal_new ("foreign-mem-released",
552       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
553       NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
554 }