2 * Copyright (C) 2019, Collabora Ltd.
3 * Author: George Kiagiadakis <george.kiagiadakis@collabora.com>
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.
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.
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
25 #include "gstomxallocator.h"
26 #include <gst/allocators/gstdmabuf.h>
28 GST_DEBUG_CATEGORY_STATIC (gst_omx_allocator_debug_category);
29 #define GST_CAT_DEFAULT gst_omx_allocator_debug_category
32 GST_DEBUG_CATEGORY_INIT (gst_omx_allocator_debug_category, "omxallocator", 0, \
33 "debug category for gst-omx allocator class");
35 G_DEFINE_TYPE_WITH_CODE (GstOMXAllocator, gst_omx_allocator, GST_TYPE_ALLOCATOR,
41 SIG_FOREIGN_MEM_RELEASED,
45 static guint signals[LAST_SIGNAL] = { 0 };
47 /* Custom allocator for memory associated with OpenMAX buffers
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
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.
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
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.
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.
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.
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.
90 #define GST_OMX_MEMORY_TYPE "openmax"
93 gst_omx_memory_quark (void)
95 static GQuark quark = 0;
98 quark = g_quark_from_static_string ("GstOMXMemory");
103 static GstOMXMemory *
104 gst_omx_memory_new (GstOMXAllocator * allocator, GstOMXBuffer * omx_buf,
105 GstMemoryFlags flags, GstMemory * parent, gssize offset, gssize size)
111 /* GStreamer uses a bitmask for the alignment while
112 * OMX uses the alignment itself. So we have to convert
114 align = allocator->port->port_def.nBufferAlignment;
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);
123 maxsize = omx_buf->omx_buf->nAllocLen;
126 size = maxsize - offset;
129 mem = g_slice_new0 (GstOMXMemory);
130 gst_memory_init (GST_MEMORY_CAST (mem), flags, (GstAllocator *) allocator,
131 parent, maxsize, align, offset, size);
139 gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
141 GstOMXMemory *omem = (GstOMXMemory *) mem;
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);
147 return omem->buf->omx_buf->pBuffer;
151 gst_omx_memory_unmap (GstMemory * mem)
156 gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size)
158 GstOMXMemory *omem = (GstOMXMemory *) mem;
162 /* find the real parent */
163 if ((parent = mem->parent) == NULL)
167 size = mem->size - offset;
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);
174 return (GstMemory *) sub;
178 gst_omx_memory_get_omx_buf (GstMemory * mem)
180 GstOMXMemory *omx_mem;
182 if (GST_IS_OMX_ALLOCATOR (mem->allocator))
183 omx_mem = (GstOMXMemory *) mem;
185 omx_mem = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
186 GST_OMX_MEMORY_QUARK);
194 /*********************/
195 /** GstOMXAllocator **/
196 /*********************/
199 gst_omx_allocator_init (GstOMXAllocator * allocator)
201 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
203 alloc->mem_type = GST_OMX_MEMORY_TYPE;
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 */
210 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
212 g_mutex_init (&allocator->lock);
213 g_cond_init (&allocator->cond);
217 gst_omx_allocator_new (GstOMXComponent * component, GstOMXPort * port)
219 GstOMXAllocator *allocator;
221 allocator = g_object_new (gst_omx_allocator_get_type (), NULL);
222 allocator->component = gst_omx_component_ref (component);
223 allocator->port = port;
229 gst_omx_allocator_finalize (GObject * object)
231 GstOMXAllocator *allocator = GST_OMX_ALLOCATOR (object);
233 gst_omx_component_unref (allocator->component);
234 g_mutex_clear (&allocator->lock);
235 g_cond_clear (&allocator->cond);
237 G_OBJECT_CLASS (gst_omx_allocator_parent_class)->finalize (object);
241 gst_omx_allocator_configure (GstOMXAllocator * allocator, guint count,
242 GstOMXAllocatorForeignMemMode mode)
244 /* check if already configured */
245 if (allocator->n_memories > 0)
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 ();
256 /* must be protected with allocator->lock */
258 gst_omx_allocator_dealloc (GstOMXAllocator * allocator)
260 /* might be called more than once */
261 if (!allocator->memories)
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) {
270 for (i = 0; i < allocator->memories->len; i++) {
271 m = g_ptr_array_index (allocator->memories, i);
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");
279 /* restore the original dispose function */
280 GST_MINI_OBJECT_CAST (m->foreign_mem)->dispose =
281 (GstMiniObjectDisposeFunction) m->foreign_dispose;
283 g_signal_emit (allocator, signals[SIG_FOREIGN_MEM_RELEASED], 0, i,
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;
298 g_cond_broadcast (&allocator->cond);
302 gst_omx_allocator_set_active (GstOMXAllocator * allocator, gboolean active)
304 gboolean changed = FALSE;
306 /* on activation, _configure() must be called first */
307 g_return_val_if_fail (!active || allocator->n_memories > 0, FALSE);
309 g_mutex_lock (&allocator->lock);
311 if (allocator->active != active)
316 allocator->memories = g_ptr_array_sized_new (allocator->n_memories);
317 g_ptr_array_set_size (allocator->memories, allocator->n_memories);
319 if (g_atomic_int_get (&allocator->n_outstanding) == 0)
320 gst_omx_allocator_dealloc (allocator);
324 allocator->active = active;
325 g_mutex_unlock (&allocator->lock);
331 gst_omx_allocator_wait_inactive (GstOMXAllocator * allocator)
333 g_mutex_lock (&allocator->lock);
334 while (allocator->memories)
335 g_cond_wait (&allocator->cond, &allocator->lock);
336 g_mutex_unlock (&allocator->lock);
340 dec_outstanding (GstOMXAllocator * allocator)
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);
349 /* take the lock so that _set_active() is not run concurrently */
350 g_mutex_lock (&allocator->lock);
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);
357 g_mutex_unlock (&allocator->lock);
358 g_object_unref (allocator);
363 gst_omx_allocator_acquire (GstOMXAllocator * allocator, GstMemory ** memory,
364 gint index, GstOMXBuffer * omx_buf)
366 GstFlowReturn ret = GST_FLOW_OK;
367 GstOMXMemory *omx_mem = NULL;
369 /* ensure memories are not going to disappear concurrently */
370 g_atomic_int_inc (&allocator->n_outstanding);
372 if (!allocator->active) {
373 ret = GST_FLOW_FLUSHING;
377 if (index >= 0 && index < allocator->n_memories)
378 omx_mem = g_ptr_array_index (allocator->memories, index);
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)
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;
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;
400 omx_mem->acquired = TRUE;
402 if (omx_mem->foreign_mem)
403 *memory = omx_mem->foreign_mem;
405 *memory = GST_MEMORY_CAST (omx_mem);
408 if (ret != GST_FLOW_OK)
409 dec_outstanding (allocator);
413 /* installed as the GstMiniObject::dispose function of the acquired GstMemory */
415 gst_omx_allocator_memory_dispose (GstMemory * mem)
417 GstOMXMemory *omx_mem;
418 GstOMXAllocator *allocator;
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;
425 omx_mem = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
426 GST_OMX_MEMORY_QUARK);
428 if (omx_mem->acquired) {
429 /* keep the memory alive */
430 gst_memory_ref (mem);
432 omx_mem->acquired = FALSE;
434 allocator = GST_OMX_ALLOCATOR (GST_MEMORY_CAST (omx_mem)->allocator);
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);
439 dec_outstanding (allocator);
441 /* be careful here, both the memory and the allocator
442 * may have been free'd as part of the call to dec_outstanding() */
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));
456 install_mem_dispose (GstOMXMemory * mem)
458 GstMemory *managed_mem = (GstMemory *) mem;
460 if (mem->foreign_mem) {
461 managed_mem = mem->foreign_mem;
462 mem->foreign_dispose = GST_MINI_OBJECT_CAST (managed_mem)->dispose;
465 GST_MINI_OBJECT_CAST (managed_mem)->dispose =
466 (GstMiniObjectDisposeFunction) gst_omx_allocator_memory_dispose;
469 /* the returned memory is transfer:none, ref still belongs to the allocator */
471 gst_omx_allocator_allocate (GstOMXAllocator * allocator, gint index,
472 GstMemory * foreign_mem)
475 GstOMXBuffer *omx_buf;
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);
486 omx_buf = g_ptr_array_index (allocator->port->buffers, index);
487 g_return_val_if_fail (omx_buf != NULL, NULL);
489 mem = gst_omx_memory_new (allocator, omx_buf, 0, NULL, 0, -1);
491 switch (allocator->foreign_mode) {
492 case GST_OMX_ALLOCATOR_FOREIGN_MEM_NONE:
493 install_mem_dispose (mem);
495 case GST_OMX_ALLOCATOR_FOREIGN_MEM_DMABUF:
497 gint fd = GPOINTER_TO_INT (omx_buf->omx_buf->pBuffer);
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);
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);
513 g_assert_not_reached ();
517 g_ptr_array_index (allocator->memories, index) = mem;
518 return mem->foreign_mem ? mem->foreign_mem : (GstMemory *) mem;
522 gst_omx_allocator_free (GstAllocator * allocator, GstMemory * mem)
524 GstOMXMemory *omem = (GstOMXMemory *) mem;
526 g_warn_if_fail (!omem->acquired);
528 if (omem->foreign_mem)
529 gst_memory_unref (omem->foreign_mem);
531 g_slice_free (GstOMXMemory, omem);
535 gst_omx_allocator_class_init (GstOMXAllocatorClass * klass)
537 GObjectClass *object_class;
538 GstAllocatorClass *allocator_class;
540 object_class = (GObjectClass *) klass;
541 allocator_class = (GstAllocatorClass *) klass;
543 object_class->finalize = gst_omx_allocator_finalize;
544 allocator_class->alloc = NULL;
545 allocator_class->free = gst_omx_allocator_free;
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);
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);