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, Collabora Ltd.
5 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation
10 * version 2.1 of the License.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "gstomxbufferpool.h"
29 GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category);
30 #define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category
32 typedef struct _GstOMXMemory GstOMXMemory;
33 typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator;
34 typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass;
43 struct _GstOMXMemoryAllocator
48 struct _GstOMXMemoryAllocatorClass
50 GstAllocatorClass parent_class;
53 #define GST_OMX_MEMORY_TYPE "openmax"
56 gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size,
57 GstAllocationParams * params)
59 g_assert_not_reached ();
64 gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem)
66 GstOMXMemory *omem = (GstOMXMemory *) mem;
68 /* TODO: We need to remember which memories are still used
69 * so we can wait until everything is released before allocating
73 g_slice_free (GstOMXMemory, omem);
77 gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
79 GstOMXMemory *omem = (GstOMXMemory *) mem;
81 return omem->buf->omx_buf->pBuffer + omem->mem.offset;
85 gst_omx_memory_unmap (GstMemory * mem)
90 gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size)
92 g_assert_not_reached ();
96 GType gst_omx_memory_allocator_get_type (void);
97 G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator,
100 #define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type())
101 #define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR))
104 gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass)
106 GstAllocatorClass *allocator_class;
108 allocator_class = (GstAllocatorClass *) klass;
110 allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy;
111 allocator_class->free = gst_omx_memory_allocator_free;
115 gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator)
117 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
119 alloc->mem_type = GST_OMX_MEMORY_TYPE;
120 alloc->mem_map = gst_omx_memory_map;
121 alloc->mem_unmap = gst_omx_memory_unmap;
122 alloc->mem_share = gst_omx_memory_share;
124 /* default copy & is_span */
126 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
130 gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags,
136 /* FIXME: We don't allow sharing because we need to know
137 * when the memory becomes unused and can only then put
138 * it back to the pool. Which is done in the pool's release
141 flags |= GST_MEMORY_FLAG_NO_SHARE;
143 /* GStreamer uses a bitmask for the alignment while
144 * OMX uses the alignment itself. So we have to convert
146 align = buf->port->port_def.nBufferAlignment;
149 if (((align + 1) & align) != 0) {
150 GST_WARNING ("Invalid alignment that is not a power of two: %u",
151 (guint) buf->port->port_def.nBufferAlignment);
155 mem = g_slice_new (GstOMXMemory);
156 /* the shared memory is always readonly */
157 gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL,
158 buf->omx_buf->nAllocLen, align, 0, buf->omx_buf->nAllocLen);
162 return GST_MEMORY_CAST (mem);
165 /* Buffer pool for the buffers of an OpenMAX port.
167 * This pool is only used if we either passed buffers from another
168 * pool to the OMX port or provide the OMX buffers directly to other
172 * A buffer is in the pool if it is currently owned by the port,
173 * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside
174 * the pool after it was taken from the port after it was handled
175 * by the port, i.e. {Empty,Fill}BufferDone.
177 * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated
178 * by someone else and (temporarily) passed to this pool
179 * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of
180 * the buffer will be overriden, and restored in free_buffer(). Other
181 * buffers are just freed there.
183 * The pool always has a fixed number of minimum and maximum buffers
184 * and these are allocated while starting the pool and released afterwards.
185 * They correspond 1:1 to the OMX buffers of the port, which are allocated
186 * before the pool is started.
188 * Acquiring a buffer from this pool happens after the OMX buffer has
189 * been acquired from the port. gst_buffer_pool_acquire_buffer() is
190 * supposed to return the buffer that corresponds to the OMX buffer.
192 * For buffers provided to upstream, the buffer will be passed to
193 * the component manually when it arrives and then unreffed. If the
194 * buffer is released before reaching the component it will be just put
195 * back into the pool as if EmptyBufferDone has happened. If it was
196 * passed to the component, it will be back into the pool when it was
197 * released and EmptyBufferDone has happened.
199 * For buffers provided to downstream, the buffer will be returned
200 * back to the component (OMX_FillThisBuffer()) when it is released.
203 static GQuark gst_omx_buffer_data_quark = 0;
206 GST_DEBUG_CATEGORY_INIT (gst_omx_buffer_pool_debug_category, "omxbufferpool", 0, \
207 "debug category for gst-omx buffer pool base class");
209 G_DEFINE_TYPE_WITH_CODE (GstOMXBufferPool, gst_omx_buffer_pool,
210 GST_TYPE_BUFFER_POOL, DEBUG_INIT);
212 static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool,
216 gst_omx_buffer_pool_start (GstBufferPool * bpool)
218 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
220 /* Only allow to start the pool if we still are attached
221 * to a component and port */
222 GST_OBJECT_LOCK (pool);
223 if (!pool->component || !pool->port) {
224 GST_OBJECT_UNLOCK (pool);
227 GST_OBJECT_UNLOCK (pool);
230 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool);
234 gst_omx_buffer_pool_stop (GstBufferPool * bpool)
236 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
239 /* When not using the default GstBufferPool::GstAtomicQueue then
240 * GstBufferPool::free_buffer is not called while stopping the pool
241 * (because the queue is empty) */
242 for (i = 0; i < pool->buffers->len; i++)
243 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
244 (bpool, g_ptr_array_index (pool->buffers, i));
246 /* Remove any buffers that are there */
247 g_ptr_array_set_size (pool->buffers, 0);
250 gst_caps_unref (pool->caps);
253 pool->add_videometa = FALSE;
255 return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool);
258 static const gchar **
259 gst_omx_buffer_pool_get_options (GstBufferPool * bpool)
261 static const gchar *raw_video_options[] =
262 { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
263 static const gchar *options[] = { NULL };
264 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
266 GST_OBJECT_LOCK (pool);
267 if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
268 && pool->port->port_def.format.video.eCompressionFormat ==
269 OMX_VIDEO_CodingUnused) {
270 GST_OBJECT_UNLOCK (pool);
271 return raw_video_options;
273 GST_OBJECT_UNLOCK (pool);
279 gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config)
281 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
284 GST_OBJECT_LOCK (pool);
286 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
292 if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo
293 && pool->port->port_def.format.video.eCompressionFormat ==
294 OMX_VIDEO_CodingUnused) {
297 /* now parse the caps from the config */
298 if (!gst_video_info_from_caps (&info, caps))
299 goto wrong_video_caps;
301 /* enable metadata based on config of the pool */
302 pool->add_videometa =
303 gst_buffer_pool_config_has_option (config,
304 GST_BUFFER_POOL_OPTION_VIDEO_META);
306 pool->video_info = info;
310 gst_caps_unref (pool->caps);
311 pool->caps = gst_caps_ref (caps);
313 GST_OBJECT_UNLOCK (pool);
315 return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config
321 GST_OBJECT_UNLOCK (pool);
322 GST_WARNING_OBJECT (pool, "invalid config");
327 GST_OBJECT_UNLOCK (pool);
328 GST_WARNING_OBJECT (pool, "no caps in config");
333 GST_OBJECT_UNLOCK (pool);
334 GST_WARNING_OBJECT (pool,
335 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
341 gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool,
342 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
344 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
346 GstOMXBuffer *omx_buf;
348 g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR);
350 omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index);
351 g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR);
353 if (pool->other_pool) {
356 buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
357 g_assert (pool->other_pool == buf->pool);
358 gst_object_replace ((GstObject **) & buf->pool, NULL);
360 n = gst_buffer_n_memory (buf);
361 for (i = 0; i < n; i++) {
362 GstMemory *mem = gst_buffer_peek_memory (buf, i);
364 /* FIXME: We don't allow sharing because we need to know
365 * when the memory becomes unused and can only then put
366 * it back to the pool. Which is done in the pool's release
369 GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE);
372 if (pool->add_videometa) {
375 meta = gst_buffer_get_video_meta (buf);
377 gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
378 GST_VIDEO_INFO_FORMAT (&pool->video_info),
379 GST_VIDEO_INFO_WIDTH (&pool->video_info),
380 GST_VIDEO_INFO_HEIGHT (&pool->video_info));
384 pool->need_copy = FALSE;
387 const guint nstride = pool->port->port_def.format.video.nStride;
388 const guint nslice = pool->port->port_def.format.video.nSliceHeight;
389 gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
390 gint stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, };
392 mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf);
393 buf = gst_buffer_new ();
394 gst_buffer_append_memory (buf, mem);
395 g_ptr_array_add (pool->buffers, buf);
397 switch (GST_VIDEO_INFO_FORMAT (&pool->video_info)) {
398 case GST_VIDEO_FORMAT_ABGR:
399 case GST_VIDEO_FORMAT_ARGB:
400 case GST_VIDEO_FORMAT_RGB16:
401 case GST_VIDEO_FORMAT_BGR16:
402 case GST_VIDEO_FORMAT_YUY2:
403 case GST_VIDEO_FORMAT_UYVY:
404 case GST_VIDEO_FORMAT_YVYU:
405 case GST_VIDEO_FORMAT_GRAY8:
407 case GST_VIDEO_FORMAT_I420:
408 stride[1] = nstride / 2;
409 offset[1] = offset[0] + stride[0] * nslice;
410 stride[2] = nstride / 2;
411 offset[2] = offset[1] + (stride[1] * nslice / 2);
413 case GST_VIDEO_FORMAT_NV12:
414 case GST_VIDEO_FORMAT_NV16:
416 offset[1] = offset[0] + stride[0] * nslice;
418 case GST_VIDEO_FORMAT_SN12:
419 case GST_VIDEO_FORMAT_ST12:
421 stride[0] = pool->port->port_def.format.video.nStride;
422 offset[1] = stride[0] * pool->port->port_def.format.video.nSliceHeight;
423 stride[1] = pool->port->port_def.format.video.nStride;
426 g_assert_not_reached ();
430 if (pool->add_videometa) {
431 pool->need_copy = FALSE;
434 gboolean need_copy = FALSE;
437 gst_video_info_init (&info);
438 gst_video_info_set_format (&info,
439 GST_VIDEO_INFO_FORMAT (&pool->video_info),
440 GST_VIDEO_INFO_WIDTH (&pool->video_info),
441 GST_VIDEO_INFO_HEIGHT (&pool->video_info));
443 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&pool->video_info); i++) {
444 if (info.stride[i] != stride[i] || info.offset[i] != offset[i]) {
449 pool->need_copy = need_copy;
451 #ifdef TIZEN_FEATURE_OMX
452 pool->need_copy = FALSE;
455 if (pool->need_copy || pool->add_videometa) {
456 /* We always add the videometa. It's the job of the user
457 * to copy the buffer if pool->need_copy is TRUE
459 gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
460 GST_VIDEO_INFO_FORMAT (&pool->video_info),
461 GST_VIDEO_INFO_WIDTH (&pool->video_info),
462 GST_VIDEO_INFO_HEIGHT (&pool->video_info),
463 GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride);
467 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf),
468 gst_omx_buffer_data_quark, omx_buf, NULL);
473 GST_DEBUG_OBJECT (pool, "alloc buffers : %d %d", pool->num_buffers, pool->current_buffer_index);
474 pool->current_buffer_index++;
480 gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer)
482 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
484 /* If the buffers belong to another pool, restore them now */
485 GST_OBJECT_LOCK (pool);
486 if (pool->other_pool) {
487 gst_object_replace ((GstObject **) & buffer->pool,
488 (GstObject *) pool->other_pool);
490 GST_OBJECT_UNLOCK (pool);
492 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer),
493 gst_omx_buffer_data_quark, NULL, NULL);
495 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool,
497 GST_DEBUG_OBJECT (pool, "free buffers : %d %d", pool->num_buffers, pool->current_buffer_index);
502 gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool,
503 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
506 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
508 if (pool->port->port_def.eDir == OMX_DirOutput) {
511 g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR);
513 buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index);
514 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
517 GST_DEBUG_OBJECT (pool, "acquire buffers : %d %d", pool->num_buffers, pool->current_buffer_index);
520 /* If it's our own memory we have to set the sizes */
521 if (!pool->other_pool) {
522 GstMemory *mem = gst_buffer_peek_memory (*buffer, 0);
525 && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0);
526 mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen;
527 mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset;
530 /* Acquire any buffer that is available to be filled by upstream */
532 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer
533 (bpool, buffer, params);
540 gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer)
542 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool);
544 GstOMXBuffer *omx_buf;
546 g_assert (pool->component && pool->port);
548 if (!pool->allocating && !pool->deactivated) {
550 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer),
551 gst_omx_buffer_data_quark);
552 if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) {
553 /* Release back to the port, can be filled again */
555 GST_DEBUG_OBJECT (pool, "release buffers : %d %d", pool->num_buffers, pool->current_buffer_index);
556 err = gst_omx_port_release_buffer (pool->port, omx_buf);
557 if (err != OMX_ErrorNone) {
558 GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL),
559 ("Failed to relase output buffer to component: %s (0x%08x)",
560 gst_omx_error_to_string (err), err));
562 } else if (!omx_buf->used) {
565 * If not used (i.e. was not passed to the component) this should do
566 * the same as EmptyBufferDone.
567 * If it is used (i.e. was passed to the component) this should do
568 * nothing until EmptyBufferDone.
570 * EmptyBufferDone should release the buffer to the pool so it can
573 * Needs something to call back here in EmptyBufferDone, like keeping
574 * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which
575 * would ensure that the buffer is always unused when this is called.
577 g_assert_not_reached ();
578 GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer
585 gst_omx_buffer_pool_finalize (GObject * object)
587 GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object);
590 gst_object_unref (pool->element);
591 pool->element = NULL;
594 g_ptr_array_unref (pool->buffers);
595 pool->buffers = NULL;
597 if (pool->other_pool)
598 gst_object_unref (pool->other_pool);
599 pool->other_pool = NULL;
602 gst_object_unref (pool->allocator);
603 pool->allocator = NULL;
606 gst_caps_unref (pool->caps);
609 G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object);
613 gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass)
615 GObjectClass *gobject_class = (GObjectClass *) klass;
616 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
618 gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData");
620 gobject_class->finalize = gst_omx_buffer_pool_finalize;
621 gstbufferpool_class->start = gst_omx_buffer_pool_start;
622 gstbufferpool_class->stop = gst_omx_buffer_pool_stop;
623 gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options;
624 gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config;
625 gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer;
626 gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer;
627 gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer;
628 gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer;
632 gst_omx_buffer_pool_init (GstOMXBufferPool * pool)
634 pool->buffers = g_ptr_array_new ();
635 pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL);
639 gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component,
642 GstOMXBufferPool *pool;
644 pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL);
645 pool->element = gst_object_ref (element);
646 pool->component = component;
649 return GST_BUFFER_POOL (pool);