libgstomx_la_SOURCES = \
gstomx.c \
+ gstomxbufferpool.c \
gstomxvideodec.c \
gstomxvideoenc.c \
gstomxaudioenc.c \
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ * Copyright (C) 2013, Collabora Ltd.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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);
+}
--- /dev/null
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ * Author: Christian König <christian.koenig@amd.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/video/gstvideometa.h>
+#include <gst/video/gstvideopool.h>
+
+#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__ */
#endif
#include <gst/gst.h>
-#include <gst/video/gstvideometa.h>
-#include <gst/video/gstvideopool.h>
#if defined (USE_OMX_TARGET_RPI) && defined(__GNUC__)
#ifndef __VCCOREVER__
#include <string.h>
+#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);