diff --git a/omx/Makefile.am b/omx/Makefile.am index dbe2f54353..9006214d03 100644 --- a/omx/Makefile.am +++ b/omx/Makefile.am @@ -12,6 +12,7 @@ endif libgstomx_la_SOURCES = \ gstomx.c \ + gstomxbufferpool.c \ gstomxvideodec.c \ gstomxvideoenc.c \ gstomxaudioenc.c \ diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c new file mode 100644 index 0000000000..2bb0db0536 --- /dev/null +++ b/omx/gstomxbufferpool.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , Collabora Ltd. + * Copyright (C) 2013, Collabora Ltd. + * Author: Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstomxbufferpool.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category); +#define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category + +typedef struct _GstOMXMemory GstOMXMemory; +typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; +typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; + +struct _GstOMXMemory +{ + GstMemory mem; + + GstOMXBuffer *buf; +}; + +struct _GstOMXMemoryAllocator +{ + GstAllocator parent; +}; + +struct _GstOMXMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +#define GST_OMX_MEMORY_TYPE "openmax" + +static GstMemory * +gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_assert_not_reached (); + return NULL; +} + +static void +gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) +{ + GstOMXMemory *omem = (GstOMXMemory *) mem; + + /* TODO: We need to remember which memories are still used + * so we can wait until everything is released before allocating + * new memory + */ + + g_slice_free (GstOMXMemory, omem); +} + +static gpointer +gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +{ + GstOMXMemory *omem = (GstOMXMemory *) mem; + + return omem->buf->omx_buf->pBuffer + omem->mem.offset; +} + +static void +gst_omx_memory_unmap (GstMemory * mem) +{ +} + +static GstMemory * +gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) +{ + g_assert_not_reached (); + return NULL; +} + +GType gst_omx_memory_allocator_get_type (void); +G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, + GST_TYPE_ALLOCATOR); + +#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) +#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) + +static void +gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; + allocator_class->free = gst_omx_memory_allocator_free; +} + +static void +gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_OMX_MEMORY_TYPE; + alloc->mem_map = gst_omx_memory_map; + alloc->mem_unmap = gst_omx_memory_unmap; + alloc->mem_share = gst_omx_memory_share; + + /* default copy & is_span */ + + GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static GstMemory * +gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, + GstOMXBuffer * buf) +{ + GstOMXMemory *mem; + + /* FIXME: We don't allow sharing because we need to know + * when the memory becomes unused and can only then put + * it back to the pool. Which is done in the pool's release + * function + */ + flags |= GST_MEMORY_FLAG_NO_SHARE; + + mem = g_slice_new (GstOMXMemory); + /* the shared memory is always readonly */ + gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, + buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment, + 0, buf->omx_buf->nAllocLen); + + mem->buf = buf; + + return GST_MEMORY_CAST (mem); +} + +/* Buffer pool for the buffers of an OpenMAX port. + * + * This pool is only used if we either passed buffers from another + * pool to the OMX port or provide the OMX buffers directly to other + * elements. + * + * + * A buffer is in the pool if it is currently owned by the port, + * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside + * the pool after it was taken from the port after it was handled + * by the port, i.e. {Empty,Fill}BufferDone. + * + * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated + * by someone else and (temporarily) passed to this pool + * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of + * the buffer will be overriden, and restored in free_buffer(). Other + * buffers are just freed there. + * + * The pool always has a fixed number of minimum and maximum buffers + * and these are allocated while starting the pool and released afterwards. + * They correspond 1:1 to the OMX buffers of the port, which are allocated + * before the pool is started. + * + * Acquiring a buffer from this pool happens after the OMX buffer has + * been acquired from the port. gst_buffer_pool_acquire_buffer() is + * supposed to return the buffer that corresponds to the OMX buffer. + * + * For buffers provided to upstream, the buffer will be passed to + * the component manually when it arrives and then unreffed. If the + * buffer is released before reaching the component it will be just put + * back into the pool as if EmptyBufferDone has happened. If it was + * passed to the component, it will be back into the pool when it was + * released and EmptyBufferDone has happened. + * + * For buffers provided to downstream, the buffer will be returned + * back to the component (OMX_FillThisBuffer()) when it is released. + */ + +static GQuark gst_omx_buffer_data_quark = 0; + +G_DEFINE_TYPE (GstOMXBufferPool, gst_omx_buffer_pool, GST_TYPE_BUFFER_POOL); + +static gboolean +gst_omx_buffer_pool_start (GstBufferPool * bpool) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + /* Only allow to start the pool if we still are attached + * to a component and port */ + GST_OBJECT_LOCK (pool); + if (!pool->component || !pool->port) { + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + GST_OBJECT_UNLOCK (pool); + + return + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); +} + +static gboolean +gst_omx_buffer_pool_stop (GstBufferPool * bpool) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + /* Remove any buffers that are there */ + g_ptr_array_set_size (pool->buffers, 0); + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + pool->add_videometa = FALSE; + + return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); +} + +static const gchar ** +gst_omx_buffer_pool_get_options (GstBufferPool * bpool) +{ + static const gchar *raw_video_options[] = + { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; + static const gchar *options[] = { NULL }; + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + GST_OBJECT_LOCK (pool); + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GST_OBJECT_UNLOCK (pool); + return raw_video_options; + } + GST_OBJECT_UNLOCK (pool); + + return options; +} + +static gboolean +gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + GstCaps *caps; + + GST_OBJECT_LOCK (pool); + + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) + goto wrong_config; + + if (caps == NULL) + goto no_caps; + + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GstVideoInfo info; + + /* now parse the caps from the config */ + if (!gst_video_info_from_caps (&info, caps)) + goto wrong_video_caps; + + /* enable metadata based on config of the pool */ + pool->add_videometa = + gst_buffer_pool_config_has_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + + pool->video_info = info; + } + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = gst_caps_ref (caps); + + GST_OBJECT_UNLOCK (pool); + + return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config + (bpool, config); + + /* ERRORS */ +wrong_config: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "invalid config"); + return FALSE; + } +no_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "no caps in config"); + return FALSE; + } +wrong_video_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, + "failed getting geometry from caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +} + +static GstFlowReturn +gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + GstBuffer *buf; + GstOMXBuffer *omx_buf; + + g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); + + omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); + g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); + + if (pool->other_pool) { + guint i, n; + + buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); + g_assert (pool->other_pool == buf->pool); + gst_object_replace ((GstObject **) & buf->pool, NULL); + + n = gst_buffer_n_memory (buf); + for (i = 0; i < n; i++) { + GstMemory *mem = gst_buffer_peek_memory (buf, i); + + /* FIXME: We don't allow sharing because we need to know + * when the memory becomes unused and can only then put + * it back to the pool. Which is done in the pool's release + * function + */ + GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); + } + + if (pool->add_videometa) { + GstVideoMeta *meta; + + meta = gst_buffer_get_video_meta (buf); + if (!meta) { + gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info)); + } + } + } else { + GstMemory *mem; + + mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf); + buf = gst_buffer_new (); + gst_buffer_append_memory (buf, mem); + g_ptr_array_add (pool->buffers, buf); + + if (pool->add_videometa) { + gsize offset[4] = { 0, }; + gint stride[4] = { 0, }; + + switch (pool->video_info.finfo->format) { + case GST_VIDEO_FORMAT_I420: + offset[0] = 0; + stride[0] = pool->port->port_def.format.video.nStride; + offset[1] = + stride[0] * pool->port->port_def.format.video.nSliceHeight; + stride[1] = pool->port->port_def.format.video.nStride / 2; + offset[2] = + offset[1] + + stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2); + stride[2] = pool->port->port_def.format.video.nStride / 2; + break; + case GST_VIDEO_FORMAT_NV12: + offset[0] = 0; + stride[0] = pool->port->port_def.format.video.nStride; + offset[1] = + stride[0] * pool->port->port_def.format.video.nSliceHeight; + stride[1] = pool->port->port_def.format.video.nStride; + break; + default: + g_assert_not_reached (); + break; + } + + gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info), + GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); + } + } + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), + gst_omx_buffer_data_quark, omx_buf, NULL); + + *buffer = buf; + + pool->current_buffer_index++; + + return GST_FLOW_OK; +} + +static void +gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + /* If the buffers belong to another pool, restore them now */ + GST_OBJECT_LOCK (pool); + if (pool->other_pool) { + gst_object_replace ((GstObject **) & buffer->pool, + (GstObject *) pool->other_pool); + } + GST_OBJECT_UNLOCK (pool); + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_buffer_data_quark, NULL, NULL); + + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, + buffer); +} + +static GstFlowReturn +gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstFlowReturn ret; + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + if (pool->port->port_def.eDir == OMX_DirOutput) { + GstBuffer *buf; + + g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); + + buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + *buffer = buf; + ret = GST_FLOW_OK; + + /* If it's our own memory we have to set the sizes */ + if (!pool->other_pool) { + GstMemory *mem = gst_buffer_peek_memory (*buffer, 0); + + g_assert (mem + && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0); + mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen; + mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset; + } + } else { + /* Acquire any buffer that is available to be filled by upstream */ + ret = + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer + (bpool, buffer, params); + } + + return ret; +} + +static void +gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + OMX_ERRORTYPE err; + GstOMXBuffer *omx_buf; + + g_assert (pool->component && pool->port); + + if (!pool->allocating && !pool->deactivated) { + omx_buf = + gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_buffer_data_quark); + if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { + /* Release back to the port, can be filled again */ + err = gst_omx_port_release_buffer (pool->port, omx_buf); + if (err != OMX_ErrorNone) { + GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), + ("Failed to relase output buffer to component: %s (0x%08x)", + gst_omx_error_to_string (err), err)); + } + } else if (!omx_buf->used) { + /* TODO: Implement. + * + * If not used (i.e. was not passed to the component) this should do + * the same as EmptyBufferDone. + * If it is used (i.e. was passed to the component) this should do + * nothing until EmptyBufferDone. + * + * EmptyBufferDone should release the buffer to the pool so it can + * be allocated again + * + * Needs something to call back here in EmptyBufferDone, like keeping + * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which + * would ensure that the buffer is always unused when this is called. + */ + g_assert_not_reached (); + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer + (bpool, buffer); + } + } +} + +static void +gst_omx_buffer_pool_finalize (GObject * object) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); + + if (pool->element) + gst_object_unref (pool->element); + pool->element = NULL; + + if (pool->buffers) + g_ptr_array_unref (pool->buffers); + pool->buffers = NULL; + + if (pool->other_pool) + gst_object_unref (pool->other_pool); + pool->other_pool = NULL; + + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = NULL; + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); +} + +static void +gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; + + gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); + + gobject_class->finalize = gst_omx_buffer_pool_finalize; + gstbufferpool_class->start = gst_omx_buffer_pool_start; + gstbufferpool_class->stop = gst_omx_buffer_pool_stop; + gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; + gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; + gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; + gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; + gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; + gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; +} + +static void +gst_omx_buffer_pool_init (GstOMXBufferPool * pool) +{ + pool->buffers = g_ptr_array_new (); + pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); +} + +GstBufferPool * +gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, + GstOMXPort * port) +{ + GstOMXBufferPool *pool; + + pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); + pool->element = gst_object_ref (element); + pool->component = component; + pool->port = port; + + return GST_BUFFER_POOL (pool); +} diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h new file mode 100644 index 0000000000..acc69f6613 --- /dev/null +++ b/omx/gstomxbufferpool.h @@ -0,0 +1,87 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * Author: Christian König + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_OMX_BUFFER_POOL_H__ +#define __GST_OMX_BUFEFR_POOL_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstomx.h" + +G_BEGIN_DECLS + +#define GST_OMX_BUFFER_POOL(pool) ((GstOMXBufferPool *) pool) +typedef struct _GstOMXBufferPool GstOMXBufferPool; +typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; + +struct _GstOMXBufferPool +{ + GstVideoBufferPool parent; + + GstElement *element; + + GstCaps *caps; + gboolean add_videometa; + GstVideoInfo video_info; + + /* Owned by element, element has to stop this pool before + * it destroys component or port */ + GstOMXComponent *component; + GstOMXPort *port; + + /* For handling OpenMAX allocated memory */ + GstAllocator *allocator; + + /* Set from outside this pool */ + /* TRUE if we're currently allocating all our buffers */ + gboolean allocating; + /* TRUE if the pool is not used anymore */ + gboolean deactivated; + + /* For populating the pool from another one */ + GstBufferPool *other_pool; + GPtrArray *buffers; + + /* Used during acquire for output ports to + * specify which buffer has to be retrieved + * and during alloc, which buffer has to be + * wrapped + */ + gint current_buffer_index; +}; + +struct _GstOMXBufferPoolClass +{ + GstVideoBufferPoolClass parent_class; +}; + +GType gst_omx_buffer_pool_get_type (void); + +GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port); + +G_END_DECLS + +#endif /* __GST_OMX_BUFFER_POOL_H__ */ diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c index 045117610d..3b6605a34b 100644 --- a/omx/gstomxvideodec.c +++ b/omx/gstomxvideodec.c @@ -25,8 +25,6 @@ #endif #include -#include -#include #if defined (USE_OMX_TARGET_RPI) && defined(__GNUC__) #ifndef __VCCOREVER__ @@ -49,603 +47,12 @@ #include +#include "gstomxbufferpool.h" #include "gstomxvideodec.h" GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category); #define GST_CAT_DEFAULT gst_omx_video_dec_debug_category -typedef struct _GstOMXMemory GstOMXMemory; -typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; -typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; - -struct _GstOMXMemory -{ - GstMemory mem; - - GstOMXBuffer *buf; -}; - -struct _GstOMXMemoryAllocator -{ - GstAllocator parent; -}; - -struct _GstOMXMemoryAllocatorClass -{ - GstAllocatorClass parent_class; -}; - -#define GST_OMX_MEMORY_TYPE "openmax" - -static GstMemory * -gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, - GstAllocationParams * params) -{ - g_assert_not_reached (); - return NULL; -} - -static void -gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) -{ - GstOMXMemory *omem = (GstOMXMemory *) mem; - - /* TODO: We need to remember which memories are still used - * so we can wait until everything is released before allocating - * new memory - */ - - g_slice_free (GstOMXMemory, omem); -} - -static gpointer -gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) -{ - GstOMXMemory *omem = (GstOMXMemory *) mem; - - return omem->buf->omx_buf->pBuffer + omem->mem.offset; -} - -static void -gst_omx_memory_unmap (GstMemory * mem) -{ -} - -static GstMemory * -gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) -{ - g_assert_not_reached (); - return NULL; -} - -GType gst_omx_memory_allocator_get_type (void); -G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, - GST_TYPE_ALLOCATOR); - -#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) -#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) - -static void -gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) -{ - GstAllocatorClass *allocator_class; - - allocator_class = (GstAllocatorClass *) klass; - - allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; - allocator_class->free = gst_omx_memory_allocator_free; -} - -static void -gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) -{ - GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); - - alloc->mem_type = GST_OMX_MEMORY_TYPE; - alloc->mem_map = gst_omx_memory_map; - alloc->mem_unmap = gst_omx_memory_unmap; - alloc->mem_share = gst_omx_memory_share; - - /* default copy & is_span */ - - GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); -} - -static GstMemory * -gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, - GstOMXBuffer * buf) -{ - GstOMXMemory *mem; - - /* FIXME: We don't allow sharing because we need to know - * when the memory becomes unused and can only then put - * it back to the pool. Which is done in the pool's release - * function - */ - flags |= GST_MEMORY_FLAG_NO_SHARE; - - mem = g_slice_new (GstOMXMemory); - /* the shared memory is always readonly */ - gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, - buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment, - 0, buf->omx_buf->nAllocLen); - - mem->buf = buf; - - return GST_MEMORY_CAST (mem); -} - -/* Buffer pool for the buffers of an OpenMAX port. - * - * This pool is only used if we either passed buffers from another - * pool to the OMX port or provide the OMX buffers directly to other - * elements. - * - * - * A buffer is in the pool if it is currently owned by the port, - * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside - * the pool after it was taken from the port after it was handled - * by the port, i.e. {Empty,Fill}BufferDone. - * - * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated - * by someone else and (temporarily) passed to this pool - * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of - * the buffer will be overriden, and restored in free_buffer(). Other - * buffers are just freed there. - * - * The pool always has a fixed number of minimum and maximum buffers - * and these are allocated while starting the pool and released afterwards. - * They correspond 1:1 to the OMX buffers of the port, which are allocated - * before the pool is started. - * - * Acquiring a buffer from this pool happens after the OMX buffer has - * been acquired from the port. gst_buffer_pool_acquire_buffer() is - * supposed to return the buffer that corresponds to the OMX buffer. - * - * For buffers provided to upstream, the buffer will be passed to - * the component manually when it arrives and then unreffed. If the - * buffer is released before reaching the component it will be just put - * back into the pool as if EmptyBufferDone has happened. If it was - * passed to the component, it will be back into the pool when it was - * released and EmptyBufferDone has happened. - * - * For buffers provided to downstream, the buffer will be returned - * back to the component (OMX_FillThisBuffer()) when it is released. - */ - -static GQuark gst_omx_buffer_data_quark = 0; - -#define GST_OMX_BUFFER_POOL(pool) ((GstOMXBufferPool *) pool) -typedef struct _GstOMXBufferPool GstOMXBufferPool; -typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; - -struct _GstOMXBufferPool -{ - GstVideoBufferPool parent; - - GstElement *element; - - GstCaps *caps; - gboolean add_videometa; - GstVideoInfo video_info; - - /* Owned by element, element has to stop this pool before - * it destroys component or port */ - GstOMXComponent *component; - GstOMXPort *port; - - /* For handling OpenMAX allocated memory */ - GstAllocator *allocator; - - /* Set from outside this pool */ - /* TRUE if we're currently allocating all our buffers */ - gboolean allocating; - - /* TRUE if the pool is not used anymore */ - gboolean deactivated; - - /* For populating the pool from another one */ - GstBufferPool *other_pool; - GPtrArray *buffers; - - /* Used during acquire for output ports to - * specify which buffer has to be retrieved - * and during alloc, which buffer has to be - * wrapped - */ - gint current_buffer_index; -}; - -struct _GstOMXBufferPoolClass -{ - GstVideoBufferPoolClass parent_class; -}; - -GType gst_omx_buffer_pool_get_type (void); - -G_DEFINE_TYPE (GstOMXBufferPool, gst_omx_buffer_pool, GST_TYPE_BUFFER_POOL); - -static gboolean -gst_omx_buffer_pool_start (GstBufferPool * bpool) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* Only allow to start the pool if we still are attached - * to a component and port */ - GST_OBJECT_LOCK (pool); - if (!pool->component || !pool->port) { - GST_OBJECT_UNLOCK (pool); - return FALSE; - } - GST_OBJECT_UNLOCK (pool); - - return - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); -} - -static gboolean -gst_omx_buffer_pool_stop (GstBufferPool * bpool) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* Remove any buffers that are there */ - g_ptr_array_set_size (pool->buffers, 0); - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = NULL; - - pool->add_videometa = FALSE; - - return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); -} - -static const gchar ** -gst_omx_buffer_pool_get_options (GstBufferPool * bpool) -{ - static const gchar *raw_video_options[] = - { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; - static const gchar *options[] = { NULL }; - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - GST_OBJECT_LOCK (pool); - if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo - && pool->port->port_def.format.video.eCompressionFormat == - OMX_VIDEO_CodingUnused) { - GST_OBJECT_UNLOCK (pool); - return raw_video_options; - } - GST_OBJECT_UNLOCK (pool); - - return options; -} - -static gboolean -gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - GstCaps *caps; - - GST_OBJECT_LOCK (pool); - - if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) - goto wrong_config; - - if (caps == NULL) - goto no_caps; - - if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo - && pool->port->port_def.format.video.eCompressionFormat == - OMX_VIDEO_CodingUnused) { - GstVideoInfo info; - - /* now parse the caps from the config */ - if (!gst_video_info_from_caps (&info, caps)) - goto wrong_video_caps; - - /* enable metadata based on config of the pool */ - pool->add_videometa = - gst_buffer_pool_config_has_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_META); - - pool->video_info = info; - } - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = gst_caps_ref (caps); - - GST_OBJECT_UNLOCK (pool); - - return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config - (bpool, config); - - /* ERRORS */ -wrong_config: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, "invalid config"); - return FALSE; - } -no_caps: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, "no caps in config"); - return FALSE; - } -wrong_video_caps: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, - "failed getting geometry from caps %" GST_PTR_FORMAT, caps); - return FALSE; - } -} - -static GstFlowReturn -gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, - GstBuffer ** buffer, GstBufferPoolAcquireParams * params) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - GstBuffer *buf; - GstOMXBuffer *omx_buf; - - g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); - - omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); - g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); - - if (pool->other_pool) { - guint i, n; - - buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); - g_assert (pool->other_pool == buf->pool); - gst_object_replace ((GstObject **) & buf->pool, NULL); - - n = gst_buffer_n_memory (buf); - for (i = 0; i < n; i++) { - GstMemory *mem = gst_buffer_peek_memory (buf, i); - - /* FIXME: We don't allow sharing because we need to know - * when the memory becomes unused and can only then put - * it back to the pool. Which is done in the pool's release - * function - */ - GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); - } - - if (pool->add_videometa) { - GstVideoMeta *meta; - - meta = gst_buffer_get_video_meta (buf); - if (!meta) { - gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (&pool->video_info), - GST_VIDEO_INFO_WIDTH (&pool->video_info), - GST_VIDEO_INFO_HEIGHT (&pool->video_info)); - } - } - } else { - GstMemory *mem; - - mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf); - buf = gst_buffer_new (); - gst_buffer_append_memory (buf, mem); - g_ptr_array_add (pool->buffers, buf); - - if (pool->add_videometa) { - gsize offset[4] = { 0, }; - gint stride[4] = { 0, }; - - switch (pool->video_info.finfo->format) { - case GST_VIDEO_FORMAT_I420: - offset[0] = 0; - stride[0] = pool->port->port_def.format.video.nStride; - offset[1] = - stride[0] * pool->port->port_def.format.video.nSliceHeight; - stride[1] = pool->port->port_def.format.video.nStride / 2; - offset[2] = - offset[1] + - stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2); - stride[2] = pool->port->port_def.format.video.nStride / 2; - break; - case GST_VIDEO_FORMAT_NV12: - offset[0] = 0; - stride[0] = pool->port->port_def.format.video.nStride; - offset[1] = - stride[0] * pool->port->port_def.format.video.nSliceHeight; - stride[1] = pool->port->port_def.format.video.nStride; - break; - default: - g_assert_not_reached (); - break; - } - - gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (&pool->video_info), - GST_VIDEO_INFO_WIDTH (&pool->video_info), - GST_VIDEO_INFO_HEIGHT (&pool->video_info), - GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); - } - } - - gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), - gst_omx_buffer_data_quark, omx_buf, NULL); - - *buffer = buf; - - pool->current_buffer_index++; - - return GST_FLOW_OK; -} - -static void -gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* If the buffers belong to another pool, restore them now */ - GST_OBJECT_LOCK (pool); - if (pool->other_pool) { - gst_object_replace ((GstObject **) & buffer->pool, - (GstObject *) pool->other_pool); - } - GST_OBJECT_UNLOCK (pool); - - gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), - gst_omx_buffer_data_quark, NULL, NULL); - - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, - buffer); -} - -static GstFlowReturn -gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, - GstBuffer ** buffer, GstBufferPoolAcquireParams * params) -{ - GstFlowReturn ret; - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - if (pool->port->port_def.eDir == OMX_DirOutput) { - GstBuffer *buf; - - g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); - - buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); - g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); - *buffer = buf; - ret = GST_FLOW_OK; - - /* If it's our own memory we have to set the sizes */ - if (!pool->other_pool) { - GstMemory *mem = gst_buffer_peek_memory (*buffer, 0); - - g_assert (mem - && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0); - mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen; - mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset; - } - } else { - /* Acquire any buffer that is available to be filled by upstream */ - ret = - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer - (bpool, buffer, params); - } - - return ret; -} - -static void -gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - OMX_ERRORTYPE err; - GstOMXBuffer *omx_buf; - - g_assert (pool->component && pool->port); - - if (!pool->allocating && !pool->deactivated) { - omx_buf = - gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), - gst_omx_buffer_data_quark); - if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { - /* Release back to the port, can be filled again */ - err = gst_omx_port_release_buffer (pool->port, omx_buf); - if (err != OMX_ErrorNone) { - GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), - ("Failed to relase output buffer to component: %s (0x%08x)", - gst_omx_error_to_string (err), err)); - } - } else if (!omx_buf->used) { - /* TODO: Implement. - * - * If not used (i.e. was not passed to the component) this should do - * the same as EmptyBufferDone. - * If it is used (i.e. was passed to the component) this should do - * nothing until EmptyBufferDone. - * - * EmptyBufferDone should release the buffer to the pool so it can - * be allocated again - * - * Needs something to call back here in EmptyBufferDone, like keeping - * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which - * would ensure that the buffer is always unused when this is called. - */ - g_assert_not_reached (); - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer - (bpool, buffer); - } - } -} - -static void -gst_omx_buffer_pool_finalize (GObject * object) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); - - if (pool->element) - gst_object_unref (pool->element); - pool->element = NULL; - - if (pool->buffers) - g_ptr_array_unref (pool->buffers); - pool->buffers = NULL; - - if (pool->other_pool) - gst_object_unref (pool->other_pool); - pool->other_pool = NULL; - - if (pool->allocator) - gst_object_unref (pool->allocator); - pool->allocator = NULL; - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = NULL; - - G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); -} - -static void -gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; - - gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); - - gobject_class->finalize = gst_omx_buffer_pool_finalize; - gstbufferpool_class->start = gst_omx_buffer_pool_start; - gstbufferpool_class->stop = gst_omx_buffer_pool_stop; - gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; - gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; - gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; - gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; - gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; - gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; -} - -static void -gst_omx_buffer_pool_init (GstOMXBufferPool * pool) -{ - pool->buffers = g_ptr_array_new (); - pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); -} - -static GstBufferPool * -gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, - GstOMXPort * port) -{ - GstOMXBufferPool *pool; - - pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); - pool->element = gst_object_ref (element); - pool->component = component; - pool->port = port; - - return GST_BUFFER_POOL (pool); -} - /* prototypes */ static void gst_omx_video_dec_finalize (GObject * object);