2 * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
3 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
4 * Copyright (C) 2013-2019, Collabora Ltd.
5 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * George Kiagiadakis <george.kiagiadakis@collabora.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation
11 * version 2.1 of the License.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "gstomxbufferpool.h"
30 #include <gst/allocators/gstdmabuf.h>
32 GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category);
33 #define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category
41 static guint signals[LAST_SIGNAL] = { 0 };
43 /* Buffer pool for the buffers of an OpenMAX port.
45 * This pool is only used if we either passed buffers from another
46 * pool to the OMX port or provide the OMX buffers directly to other
49 * An output buffer is in the pool if it is currently owned by the port,
50 * i.e. after OMX_FillThisBuffer(). An output buffer is outside
51 * the pool after it was taken from the port after it was handled
52 * by the port, i.e. FillBufferDone.
54 * An input buffer is in the pool if it is currently available to be filled
55 * upstream. It will be put back into the pool when it has been processed by
56 * OMX, (EmptyBufferDone).
58 * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated
59 * by someone else and (temporarily) passed to this pool
60 * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of
61 * the buffer will be overriden, and restored in free_buffer(). Other
62 * buffers are just freed there.
64 * The pool always has a fixed number of minimum and maximum buffers
65 * and these are allocated while starting the pool and released afterwards.
66 * They correspond 1:1 to the OMX buffers of the port, which are allocated
67 * before the pool is started.
69 * Acquiring an output buffer from this pool happens after the OMX buffer has
70 * been acquired from the port. gst_buffer_pool_acquire_buffer() is
71 * supposed to return the buffer that corresponds to the OMX buffer.
73 * For buffers provided to upstream, the buffer will be passed to
74 * the component manually when it arrives and then unreffed. If the
75 * buffer is released before reaching the component it will be just put
76 * back into the pool as if EmptyBufferDone has happened. If it was
77 * passed to the component, it will be back into the pool when it was
78 * released and EmptyBufferDone has happened.
80 * For buffers provided to downstream, the buffer will be returned
81 * back to the component (OMX_FillThisBuffer()) when it is released.
83 * This pool uses a special allocator object, GstOMXAllocator. The main purpose
84 * of this allocator is to track GstMemory objects in the same way that a
85 * GstBufferPool tracks buffers. When a buffer is inserted into this pool
86 * (either because it was just allocated or because it was released back to
87 * the pool), its memory is ripped off and is tracked separately by the
88 * allocator. When a buffer is then acquired, we acquire the corresponding
89 * GstMemory from the allocator and put it back in the buffer.
91 * This allocator mechanism allows us to track memory that has been shared
92 * with buffers that are not part of this pool. When a memory is shared, then
93 * its ref count is > 1, which means it will not be released to the allocator
94 * until the sub-memory is destroyed.
96 * When a memory returns to the allocator, the allocator fires the
97 * omxbuf-released signal, which is handled by the buffer pool to return the
98 * omx buffer to the port or the queue.
102 GST_DEBUG_CATEGORY_INIT (gst_omx_buffer_pool_debug_category, "omxbufferpool", 0, \
103 "debug category for gst-omx buffer pool base class");
105 G_DEFINE_TYPE_WITH_CODE (GstOMXBufferPool, gst_omx_buffer_pool,
106 GST_TYPE_BUFFER_POOL, DEBUG_INIT);
108 static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool,
112 gst_omx_buffer_pool_start (GstBufferPool * bpool)
114 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
115 gboolean has_buffers;
116 GstStructure *config;
118 GstOMXAllocatorForeignMemMode mode;
120 /* Only allow to start the pool if we still are attached
121 * to a component and port */
122 GST_OBJECT_LOCK (pool);
123 if (!pool->component || !pool->port) {
124 GST_OBJECT_UNLOCK (pool);
128 pool->port->using_pool = TRUE;
130 has_buffers = (pool->port->buffers != NULL);
131 GST_OBJECT_UNLOCK (pool);
133 config = gst_buffer_pool_get_config (bpool);
134 gst_buffer_pool_config_get_params (config, NULL, NULL, &min, &max);
135 gst_structure_free (config);
137 GST_WARNING_OBJECT (bpool,
138 "max (%d) cannot be higher than min (%d) as pool cannot allocate buffers on the fly",
144 gboolean result = FALSE;
146 GST_DEBUG_OBJECT (bpool, "Buffers not yet allocated on port %d of %s",
147 pool->port->index, pool->component->name);
149 g_signal_emit (pool, signals[SIG_ALLOCATE], 0, &result);
152 GST_WARNING_OBJECT (bpool,
153 "Element failed to allocate buffers, can't start pool");
158 g_assert (pool->port->buffers);
160 if (pool->other_pool)
161 /* Importing buffers from downstream, either normal or dmabuf ones */
162 mode = GST_OMX_ALLOCATOR_FOREIGN_MEM_OTHER_POOL;
163 else if (pool->output_mode == GST_OMX_BUFFER_MODE_DMABUF)
164 /* Exporting dmabuf */
165 mode = GST_OMX_ALLOCATOR_FOREIGN_MEM_DMABUF;
167 /* Exporting normal buffers */
168 mode = GST_OMX_ALLOCATOR_FOREIGN_MEM_NONE;
170 if (!gst_omx_allocator_configure (pool->allocator, min, mode))
173 if (!gst_omx_allocator_set_active (pool->allocator, TRUE))
177 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool);
181 gst_omx_buffer_pool_stop (GstBufferPool * bpool)
183 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
185 /* Remove any buffers that are there */
186 g_ptr_array_set_size (pool->buffers, 0);
188 GST_DEBUG_OBJECT (pool, "deactivating OMX allocator");
189 gst_omx_allocator_set_active (pool->allocator, FALSE);
191 /* ensure all memories have been deallocated;
192 * this may take a while if some memories are being shared
193 * and therefore are in use somewhere else in the pipeline */
194 gst_omx_allocator_wait_inactive (pool->allocator);
196 GST_DEBUG_OBJECT (pool, "deallocate OMX buffers");
197 gst_omx_port_deallocate_buffers (pool->port);
200 gst_caps_unref (pool->caps);
203 pool->add_videometa = FALSE;
204 pool->deactivated = TRUE;
205 pool->port->using_pool = TRUE;
207 return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool);
210 static const gchar **
211 gst_omx_buffer_pool_get_options (GstBufferPool * bpool)
213 static const gchar *raw_video_options[] =
214 { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
215 static const gchar *options[] = { NULL };
216 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
218 GST_OBJECT_LOCK (pool);
219 if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
220 && pool->port->port_def.format.video.eCompressionFormat ==
221 OMX_VIDEO_CodingUnused) {
222 GST_OBJECT_UNLOCK (pool);
223 return raw_video_options;
225 GST_OBJECT_UNLOCK (pool);
231 gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
233 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
236 GstStructure *fake_config;
239 GST_OBJECT_LOCK (pool);
241 if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min, NULL))
247 if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
248 && pool->port->port_def.format.video.eCompressionFormat ==
249 OMX_VIDEO_CodingUnused) {
252 /* now parse the caps from the config */
253 if (!gst_video_info_from_caps (&info, caps))
254 goto wrong_video_caps;
256 /* enable metadata based on config of the pool */
257 pool->add_videometa =
258 gst_buffer_pool_config_has_option (config,
259 GST_BUFFER_POOL_OPTION_VIDEO_META);
261 pool->video_info = info;
265 gst_caps_unref (pool->caps);
266 pool->caps = gst_caps_ref (caps);
268 /* Ensure max=min as the pool won't be able to allocate more buffers while active */
269 gst_buffer_pool_config_set_params (config, caps, size, min, min);
271 GST_OBJECT_UNLOCK (pool);
273 /* give a fake config to the parent default_set_config() with size == 0
274 * this prevents default_release_buffer() from free'ing the buffers, since
275 * we release them with no memory */
276 fake_config = gst_structure_copy (config);
277 gst_buffer_pool_config_set_params (fake_config, caps, 0, min, min);
279 ret = GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config
280 (bpool, fake_config);
281 gst_structure_free (fake_config);
288 GST_OBJECT_UNLOCK (pool);
289 GST_WARNING_OBJECT (pool, "invalid config");
294 GST_OBJECT_UNLOCK (pool);
295 GST_WARNING_OBJECT (pool, "no caps in config");
300 GST_OBJECT_UNLOCK (pool);
301 GST_WARNING_OBJECT (pool,
302 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
308 gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
309 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
311 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
314 GstMemory *foreign_mem = NULL;
316 if (pool->other_pool) {
319 buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
320 g_assert (pool->other_pool == buf->pool);
321 gst_object_replace ((GstObject **) & buf->pool, NULL);
323 n = gst_buffer_n_memory (buf);
324 g_return_val_if_fail (n == 1, GST_FLOW_ERROR);
326 /* rip the memory out of the buffer;
327 * we like to keep them separate in this pool */
328 foreign_mem = gst_buffer_get_memory (buf, 0);
329 gst_buffer_remove_all_memory (buf);
331 if (pool->add_videometa) {
334 meta = gst_buffer_get_video_meta (buf);
336 gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
337 GST_VIDEO_INFO_FORMAT (&pool->video_info),
338 GST_VIDEO_INFO_WIDTH (&pool->video_info),
339 GST_VIDEO_INFO_HEIGHT (&pool->video_info));
343 pool->need_copy = FALSE;
345 const guint nstride = pool->port->port_def.format.video.nStride;
346 const guint nslice = pool->port->port_def.format.video.nSliceHeight;
347 gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
348 gint stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, };
350 buf = gst_buffer_new ();
352 switch (GST_VIDEO_INFO_FORMAT (&pool->video_info)) {
353 case GST_VIDEO_FORMAT_ABGR:
354 case GST_VIDEO_FORMAT_ARGB:
355 case GST_VIDEO_FORMAT_RGB16:
356 case GST_VIDEO_FORMAT_BGR16:
357 case GST_VIDEO_FORMAT_YUY2:
358 case GST_VIDEO_FORMAT_UYVY:
359 case GST_VIDEO_FORMAT_YVYU:
360 case GST_VIDEO_FORMAT_GRAY8:
362 case GST_VIDEO_FORMAT_I420:
363 stride[1] = nstride / 2;
364 offset[1] = offset[0] + stride[0] * nslice;
365 stride[2] = nstride / 2;
366 offset[2] = offset[1] + (stride[1] * nslice / 2);
368 case GST_VIDEO_FORMAT_NV12:
369 case GST_VIDEO_FORMAT_NV12_10LE32:
370 case GST_VIDEO_FORMAT_NV16:
371 case GST_VIDEO_FORMAT_NV16_10LE32:
373 offset[1] = offset[0] + stride[0] * nslice;
376 g_assert_not_reached ();
380 if (pool->add_videometa) {
381 pool->need_copy = FALSE;
384 gboolean need_copy = FALSE;
387 gst_video_info_init (&info);
388 gst_video_info_set_format (&info,
389 GST_VIDEO_INFO_FORMAT (&pool->video_info),
390 GST_VIDEO_INFO_WIDTH (&pool->video_info),
391 GST_VIDEO_INFO_HEIGHT (&pool->video_info));
393 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&pool->video_info); i++) {
394 if (info.stride[i] != stride[i] || info.offset[i] != offset[i]) {
395 GST_DEBUG_OBJECT (pool,
396 "Need to copy output frames because of stride/offset mismatch: plane %d stride %d (expected: %d) offset %"
397 G_GSIZE_FORMAT " (expected: %" G_GSIZE_FORMAT
398 ") nStride: %d nSliceHeight: %d ", i, stride[i], info.stride[i],
399 offset[i], info.offset[i], nstride, nslice);
406 pool->need_copy = need_copy;
409 if (pool->need_copy || pool->add_videometa) {
410 /* We always add the videometa. It's the job of the user
411 * to copy the buffer if pool->need_copy is TRUE
413 gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
414 GST_VIDEO_INFO_FORMAT (&pool->video_info),
415 GST_VIDEO_INFO_WIDTH (&pool->video_info),
416 GST_VIDEO_INFO_HEIGHT (&pool->video_info),
417 GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride);
421 mem = gst_omx_allocator_allocate (pool->allocator, pool->current_buffer_index,
424 return GST_FLOW_ERROR;
426 if (pool->output_mode == GST_OMX_BUFFER_MODE_DMABUF) {
429 if (!gst_caps_features_contains (gst_caps_get_features (pool->caps, 0),
430 GST_CAPS_FEATURE_MEMORY_DMABUF)) {
431 /* Check if the memory is actually mappable */
432 if (!gst_memory_map (mem, &map, GST_MAP_READWRITE)) {
433 GST_ERROR_OBJECT (pool,
434 "dmabuf memory is not mappable but caps does not have the 'memory:DMABuf' feature");
435 gst_memory_unref (mem);
436 return GST_FLOW_ERROR;
439 gst_memory_unmap (mem, &map);
443 /* mem still belongs to the allocator; do not add it in the buffer just yet */
447 pool->current_buffer_index++;
452 /* called by the allocator when we are using other_pool in order
453 * to restore the foreign GstMemory back to its original GstBuffer */
455 on_allocator_foreign_mem_released (GstOMXAllocator * allocator,
456 gint index, GstMemory * mem, GstOMXBufferPool * pool)
460 buf = g_ptr_array_index (pool->buffers, index);
461 gst_buffer_append_memory (buf, mem);
463 /* the buffer consumed the passed reference.
464 * we still need one more reference for the allocator */
465 gst_memory_ref (mem);
469 gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
471 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
473 /* If the buffers belong to another pool, restore them now */
474 GST_OBJECT_LOCK (pool);
475 if (pool->other_pool) {
476 gst_object_replace ((GstObject **) & buffer->pool,
477 (GstObject *) pool->other_pool);
479 GST_OBJECT_UNLOCK (pool);
481 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool,
486 gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
487 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
490 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
493 if (pool->port->port_def.eDir == OMX_DirOutput) {
494 g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR);
496 ret = gst_omx_allocator_acquire (pool->allocator, &mem,
497 pool->current_buffer_index, NULL);
498 if (ret != GST_FLOW_OK)
501 /* If it's our own memory we have to set the sizes */
502 if (!pool->other_pool) {
503 GstOMXBuffer *omx_buf = gst_omx_memory_get_omx_buf (mem);
504 mem->size = omx_buf->omx_buf->nFilledLen;
505 mem->offset = omx_buf->omx_buf->nOffset;
508 /* Acquire any buffer that is available to be filled by upstream */
509 GstOMXBuffer *omx_buf;
510 GstOMXAcquireBufferReturn r;
511 GstOMXWait wait = GST_OMX_WAIT;
513 if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT))
514 wait = GST_OMX_DONT_WAIT;
516 r = gst_omx_port_acquire_buffer (pool->port, &omx_buf, wait);
517 if (r == GST_OMX_ACQUIRE_BUFFER_OK) {
518 ret = gst_omx_allocator_acquire (pool->allocator, &mem, -1, omx_buf);
519 if (ret != GST_FLOW_OK)
521 } else if (r == GST_OMX_ACQUIRE_BUFFER_FLUSHING) {
522 return GST_FLOW_FLUSHING;
524 return GST_FLOW_ERROR;
528 /* get some GstBuffer available in this pool */
529 ret = GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer
530 (bpool, buffer, params);
532 if (ret == GST_FLOW_OK) {
533 /* attach the acquired memory on it */
534 gst_buffer_append_memory (*buffer, mem);
536 gst_memory_unref (mem);
543 gst_omx_buffer_pool_reset_buffer (GstBufferPool * bpool, GstBuffer * buffer)
545 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
548 n = gst_buffer_n_memory (buffer);
549 if (G_UNLIKELY (n != 1)) {
550 GST_ERROR_OBJECT (pool, "Released buffer does not have 1 memory... "
551 "(n = %u) something went terribly wrong", n);
554 /* rip the memory out of the buffer;
555 * we like to keep them separate in this pool.
556 * if this was the last ref count of the memory, it will be returned
557 * to the allocator, otherwise it will be returned later */
558 gst_buffer_remove_all_memory (buffer);
560 /* reset before removing the TAG_MEMORY flag so that the parent impl
561 * doesn't try to restore the original buffer size */
562 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->reset_buffer
565 /* pretend nothing happened to the memory to avoid discarding the buffer */
566 GST_MINI_OBJECT_FLAG_UNSET (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
570 on_allocator_omxbuf_released (GstOMXAllocator * allocator,
571 GstOMXBuffer * omx_buf, GstOMXBufferPool * pool)
575 if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used &&
576 !pool->deactivated) {
577 /* Release back to the port, can be filled again */
578 err = gst_omx_port_release_buffer (pool->port, omx_buf);
580 if (err != OMX_ErrorNone) {
581 GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL),
582 ("Failed to relase output buffer to component: %s (0x%08x)",
583 gst_omx_error_to_string (err), err));
585 } else if (pool->port->port_def.eDir == OMX_DirInput) {
586 gst_omx_port_requeue_buffer (pool->port, omx_buf);
591 gst_omx_buffer_pool_finalize (GObject * object)
593 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object);
596 gst_object_unref (pool->element);
597 pool->element = NULL;
600 g_ptr_array_unref (pool->buffers);
601 pool->buffers = NULL;
603 if (pool->other_pool)
604 gst_object_unref (pool->other_pool);
605 pool->other_pool = NULL;
608 gst_object_unref (pool->allocator);
609 pool->allocator = NULL;
612 gst_caps_unref (pool->caps);
615 g_clear_pointer (&pool->component, gst_omx_component_unref);
617 G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object);
621 gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass)
623 GObjectClass *gobject_class = (GObjectClass *) klass;
624 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
626 gobject_class->finalize = gst_omx_buffer_pool_finalize;
627 gstbufferpool_class->start = gst_omx_buffer_pool_start;
628 gstbufferpool_class->stop = gst_omx_buffer_pool_stop;
629 gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options;
630 gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config;
631 gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer;
632 gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer;
633 gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer;
634 gstbufferpool_class->reset_buffer = gst_omx_buffer_pool_reset_buffer;
636 signals[SIG_ALLOCATE] = g_signal_new ("allocate",
637 G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
642 gst_omx_buffer_pool_init (GstOMXBufferPool * pool)
644 pool->buffers = g_ptr_array_new ();
648 gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component,
649 GstOMXPort * port, GstOMXBufferMode output_mode)
651 GstOMXBufferPool *pool;
653 pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL);
654 pool->element = gst_object_ref (element);
655 pool->component = gst_omx_component_ref (component);
657 pool->output_mode = output_mode;
658 pool->allocator = gst_omx_allocator_new (component, port);
660 g_signal_connect_object (pool->allocator, "omxbuf-released",
661 (GCallback) on_allocator_omxbuf_released, pool, 0);
662 g_signal_connect_object (pool->allocator, "foreign-mem-released",
663 (GCallback) on_allocator_foreign_mem_released, pool, 0);
665 return GST_BUFFER_POOL (pool);