This currently hardcodes a lot of stuff but works at least.
Also adds a generic framework for handling OpenMAX cores, components
and ports.
+plugin_LTLIBRARIES = libgstomx.la
+
+libgstomx_la_SOURCES = \
+ gstomx.c \
+ gstomxvideodec.c \
+ gstomxmpeg4videodec.c \
+ gstbasevideocodec.c \
+ gstbasevideodecoder.c \
+ gstbasevideoencoder.c \
+ gstbasevideoutils.c
+
+noinst_HEADERS = \
+ gstomxvideodec.h \
+ gstomxmpeg4videodec.h \
+ gstbasevideocodec.h \
+ gstbasevideodecoder.h \
+ gstbasevideoencoder.h
+
+libgstomx_la_CFLAGS = \
+ -DGST_USE_UNSTABLE_API=1 \
+ -I$(srcdir)/openmax \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS)
+libgstomx_la_LIBADD = \
+ $(GST_PLUGINS_BASE_LIBS) \
+ -lgstvideo-@GST_MAJORMINOR@ \
+ $(GST_BASE_LIBS) \
+ $(GST_LIBS)
+libgstomx_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+EXTRA_DIST = openmax
+
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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 <gst/gst.h>
+#include <string.h>
+
+#include "gstomx.h"
+#include "gstomxmpeg4videodec.h"
+
+GST_DEBUG_CATEGORY (gstomx_debug);
+#define GST_CAT_DEFAULT gstomx_debug
+
+G_LOCK_DEFINE_STATIC (core_handles);
+static GHashTable *core_handles;
+
+GstOMXCore *
+gst_omx_core_acquire (const gchar * filename)
+{
+ GstOMXCore *core;
+
+ G_LOCK (core_handles);
+ if (!core_handles)
+ core_handles =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ core = g_hash_table_lookup (core_handles, filename);
+ if (!core) {
+ core = g_slice_new0 (GstOMXCore);
+ core->lock = g_mutex_new ();
+ core->user_count = 0;
+ g_hash_table_insert (core_handles, g_strdup (filename), core);
+
+ core->module = g_module_open (filename, G_MODULE_BIND_LAZY);
+ if (!core->module)
+ goto load_failed;
+
+ if (!g_module_symbol (core->module, "OMX_Init", (gpointer *) & core->init))
+ goto symbol_error;
+ if (!g_module_symbol (core->module, "OMX_Deinit",
+ (gpointer *) & core->deinit))
+ goto symbol_error;
+ if (!g_module_symbol (core->module, "OMX_GetHandle",
+ (gpointer *) & core->get_handle))
+ goto symbol_error;
+ if (!g_module_symbol (core->module, "OMX_FreeHandle",
+ (gpointer *) & core->free_handle))
+ goto symbol_error;
+
+ GST_DEBUG ("Successfully loaded core '%s'", filename);
+ }
+
+ g_mutex_lock (core->lock);
+ core->user_count++;
+ if (core->user_count == 1) {
+ OMX_ERRORTYPE err;
+
+ err = core->init ();
+ if (err != OMX_ErrorNone) {
+ GST_ERROR ("Failed to initialize core '%s': 0x%08x", filename, err);
+ g_mutex_unlock (core->lock);
+ goto error;
+ }
+
+ GST_DEBUG ("Successfully initialized core '%s'", filename);
+ }
+
+ g_mutex_unlock (core->lock);
+ G_UNLOCK (core_handles);
+
+ return core;
+
+load_failed:
+ {
+ GST_ERROR ("Failed to load module '%s': %s", filename, g_module_error ());
+ goto error;
+ }
+symbol_error:
+ {
+ GST_ERROR ("Failed to locate required OpenMAX symbol in '%s': %s", filename,
+ g_module_error ());
+ g_module_close (core->module);
+ core->module = NULL;
+ goto error;
+ }
+error:
+ {
+ g_hash_table_remove (core_handles, filename);
+ g_mutex_free (core->lock);
+ g_slice_free (GstOMXCore, core);
+
+ G_UNLOCK (core_handles);
+
+ return NULL;
+ }
+}
+
+void
+gst_omx_core_release (GstOMXCore * core)
+{
+ g_return_if_fail (core != NULL);
+
+ G_LOCK (core_handles);
+
+ g_mutex_lock (core->lock);
+
+ GST_DEBUG ("Releasing core %p", core);
+
+ core->user_count--;
+ if (core->user_count == 0) {
+ GST_DEBUG ("Deinit core %p", core);
+ core->deinit ();
+ }
+
+ g_mutex_unlock (core->lock);
+
+ G_UNLOCK (core_handles);
+}
+
+static OMX_ERRORTYPE
+EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent,
+ OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData)
+{
+ GstOMXComponent *comp = (GstOMXComponent *) pAppData;
+
+ switch (eEvent) {
+ case OMX_EventCmdComplete:
+ {
+ OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE) nData1;
+
+ GST_DEBUG_OBJECT (comp->parent, "Command %d complete", cmd);
+
+ switch (cmd) {
+ case OMX_CommandStateSet:{
+ /* Notify everything that waits for
+ * a state change to be finished */
+ GST_DEBUG_OBJECT (comp->parent, "State change to %d finished",
+ nData2);
+ g_mutex_lock (comp->state_lock);
+ comp->state = (OMX_STATETYPE) nData2;
+ if (comp->state == comp->pending_state)
+ comp->pending_state = OMX_StateInvalid;
+ g_cond_broadcast (comp->state_cond);
+ g_mutex_unlock (comp->state_lock);
+ break;
+ }
+ case OMX_CommandFlush:{
+ GstOMXPort *port = NULL;
+ OMX_U32 index = nData2;
+
+ port = gst_omx_component_get_port (comp, index);
+ if (!port)
+ break;
+
+ GST_DEBUG_OBJECT (comp->parent, "Port %u flushed", port->index);
+
+ /* Now notify gst_omx_port_set_flushing()
+ * that the port is really flushed now and
+ * we can continue
+ */
+ g_mutex_lock (port->port_lock);
+ /* If this is ever called when the port
+ * was not set to flushing something went
+ * wrong but it happens for some reason.
+ */
+ if (port->flushing) {
+ port->flushed = TRUE;
+ g_cond_broadcast (port->port_cond);
+ } else {
+ g_debug ("Port %u is not flushing\n", (guint32) port->index);
+ }
+ g_mutex_unlock (port->port_lock);
+ break;
+ }
+ case OMX_CommandPortEnable:
+ case OMX_CommandPortDisable:{
+ GstOMXPort *port = NULL;
+ OMX_U32 index = nData2;
+
+ port = gst_omx_component_get_port (comp, index);
+ if (!port)
+ break;
+
+ GST_DEBUG_OBJECT (comp->parent, "Port %u %s", port->index,
+ (cmd == OMX_CommandPortEnable ? "enabled" : "disabled"));
+
+ g_mutex_lock (port->port_lock);
+ g_cond_broadcast (port->port_cond);
+ g_mutex_unlock (port->port_lock);
+
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case OMX_EventError:
+ {
+ gint i, n;
+ OMX_ERRORTYPE err = nData1;
+
+ if (err == OMX_ErrorNone)
+ break;
+
+ GST_ERROR_OBJECT (comp->parent, "Got error %d\n", err);
+
+ /* Error events are always fatal, notify all
+ * condition variables that something went
+ * wrong
+ */
+ g_mutex_lock (comp->state_lock);
+ comp->last_error = err;
+ g_cond_broadcast (comp->state_cond);
+ g_mutex_unlock (comp->state_lock);
+
+ /* Now notify all ports, no locking needed
+ * here because the ports are allocated in the
+ * very beginning and never change again until
+ * component destruction.
+ */
+ n = comp->ports->len;
+ for (i = 0; i < n; i++) {
+ GstOMXPort *tmp = g_ptr_array_index (comp->ports, i);
+
+ g_mutex_lock (tmp->port_lock);
+ g_cond_broadcast (tmp->port_cond);
+ g_mutex_unlock (tmp->port_lock);
+ }
+ break;
+ }
+ case OMX_EventPortSettingsChanged:
+ {
+ OMX_U32 index;
+ GstOMXPort *port = NULL;
+
+ /* FIXME XXX: WTF? Bellagio passes
+ * the port index as *second* parameter
+ * instead of first...
+ */
+ index = nData2;
+
+ port = gst_omx_component_get_port (comp, index);
+ if (!port)
+ break;
+
+ GST_DEBUG_OBJECT (comp->parent, "Settings of port %u changed", index);
+
+ g_mutex_lock (port->port_lock);
+ port->settings_changed = TRUE;
+ g_cond_broadcast (port->port_cond);
+ g_mutex_unlock (port->port_lock);
+
+ /* FIXME XXX: Bellagio only sends the event for the
+ * input port even if the output port settings change
+ * too...
+ */
+ {
+ gint i, n;
+
+ n = comp->ports->len;
+ for (i = 0; i < n; i++) {
+ port = g_ptr_array_index (comp->ports, i);
+
+ /* Don't notify the same port twice */
+ if (port->index == index)
+ continue;
+
+ g_mutex_lock (port->port_lock);
+ port->settings_changed = TRUE;
+ g_cond_broadcast (port->port_cond);
+ g_mutex_unlock (port->port_lock);
+ }
+ }
+
+ break;
+ }
+ case OMX_EventPortFormatDetected:
+ case OMX_EventBufferFlag:
+ default:
+ break;
+ }
+
+ return OMX_ErrorNone;
+}
+
+static OMX_ERRORTYPE
+EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+ OMX_BUFFERHEADERTYPE * pBuffer)
+{
+ GstOMXBuffer *buf = pBuffer->pAppPrivate;
+
+ g_assert (buf->omx_buf == pBuffer);
+
+ /* Input buffer is empty again and can
+ * be used to contain new input */
+ g_mutex_lock (buf->port->port_lock);
+ GST_DEBUG_OBJECT (buf->port->comp->parent, "Port %u emptied buffer %p",
+ buf->port->index, buf);
+ buf->used = FALSE;
+ g_queue_push_tail (buf->port->pending_buffers, buf);
+ g_cond_broadcast (buf->port->port_cond);
+ g_mutex_unlock (buf->port->port_lock);
+ return OMX_ErrorNone;
+}
+
+static OMX_ERRORTYPE
+FillBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+ OMX_BUFFERHEADERTYPE * pBuffer)
+{
+ GstOMXBuffer *buf = pBuffer->pAppPrivate;
+
+ g_assert (buf->omx_buf == pBuffer);
+
+ /* Output buffer contains output now or
+ * the port was flushed */
+ g_mutex_lock (buf->port->port_lock);
+ GST_DEBUG_OBJECT (buf->port->comp->parent, "Port %u filled buffer %p",
+ buf->port->index, buf);
+ buf->used = FALSE;
+ g_queue_push_tail (buf->port->pending_buffers, buf);
+ g_cond_broadcast (buf->port->port_cond);
+ g_mutex_unlock (buf->port->port_lock);
+ return OMX_ErrorNone;
+}
+
+static OMX_CALLBACKTYPE callbacks =
+ { EventHandler, EmptyBufferDone, FillBufferDone };
+
+GstOMXComponent *
+gst_omx_component_new (GstObject * parent, const gchar * core_name,
+ const gchar * component_name)
+{
+ OMX_ERRORTYPE err;
+ GstOMXCore *core;
+ GstOMXComponent *comp;
+
+ core = gst_omx_core_acquire (core_name);
+ if (!core)
+ return NULL;
+
+ comp = g_slice_new0 (GstOMXComponent);
+ comp->core = core;
+
+ err =
+ core->get_handle (&comp->handle, (OMX_STRING) component_name, comp,
+ &callbacks);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (parent,
+ "Failed to get component handle '%s' from core '%s': 0x%08x",
+ component_name, core_name, err);
+ gst_omx_core_release (core);
+ g_slice_free (GstOMXComponent, comp);
+ return NULL;
+ }
+ GST_DEBUG_OBJECT (parent,
+ "Successfully got component handle %p (%s) from core '%s'", comp->handle,
+ component_name, core_name);
+ comp->parent = gst_object_ref (parent);
+
+ comp->ports = g_ptr_array_new ();
+
+ comp->state_lock = g_mutex_new ();
+ comp->state_cond = g_cond_new ();
+ comp->pending_state = OMX_StateInvalid;
+ comp->last_error = OMX_ErrorNone;
+
+ OMX_GetState (comp->handle, &comp->state);
+
+ return comp;
+}
+
+void
+gst_omx_component_free (GstOMXComponent * comp)
+{
+ gint i, n;
+
+ g_return_if_fail (comp != NULL);
+
+ GST_DEBUG_OBJECT (comp->parent, "Unloading component %p", comp);
+
+ if (comp->ports) {
+ n = comp->ports->len;
+ for (i = 0; i < n; i++) {
+ GstOMXPort *port = g_ptr_array_index (comp->ports, i);
+
+ g_assert (!port->buffers || port->buffers->len == 0);
+
+ g_mutex_free (port->port_lock);
+ g_cond_free (port->port_cond);
+ g_queue_free (port->pending_buffers);
+
+ g_slice_free (GstOMXPort, port);
+ }
+ g_ptr_array_unref (comp->ports);
+ }
+
+ g_cond_free (comp->state_cond);
+ g_mutex_free (comp->state_lock);
+
+ comp->core->free_handle (comp->handle);
+ gst_omx_core_release (comp->core);
+ gst_object_unref (comp->parent);
+}
+
+OMX_ERRORTYPE
+gst_omx_component_set_state (GstOMXComponent * comp, OMX_STATETYPE state)
+{
+ OMX_STATETYPE old_state;
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
+
+ g_mutex_lock (comp->state_lock);
+ old_state = comp->state;
+ GST_DEBUG_OBJECT (comp->parent, "Setting state from %d to %d", old_state,
+ state);
+ if ((err = comp->last_error) != OMX_ErrorNone)
+ goto done;
+ if (old_state == state || comp->pending_state == state)
+ goto done;
+
+ comp->pending_state = state;
+ err = OMX_SendCommand (comp->handle, OMX_CommandStateSet, state, NULL);
+
+done:
+ g_mutex_unlock (comp->state_lock);
+
+ if (err != OMX_ErrorNone)
+ GST_ERROR_OBJECT (comp->parent, "Error setting state from %d to %d: %d",
+ old_state, state, err);
+ return err;
+}
+
+OMX_STATETYPE
+gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout)
+{
+ OMX_STATETYPE ret;
+ GTimeVal *timeval, abstimeout;
+ gboolean signalled;
+
+ g_return_val_if_fail (comp != NULL, OMX_StateInvalid);
+
+ GST_DEBUG_OBJECT (comp->parent, "Getting state");
+
+ g_mutex_lock (comp->state_lock);
+ ret = comp->state;
+ if (comp->pending_state == OMX_StateInvalid)
+ goto done;
+
+ if (comp->last_error != OMX_ErrorNone) {
+ ret = OMX_StateInvalid;
+ goto done;
+ }
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ glong add = timeout / 1000;
+
+ if (add == 0)
+ goto done;
+
+ g_get_current_time (&abstimeout);
+ g_time_val_add (&abstimeout, add);
+ timeval = &abstimeout;
+ GST_DEBUG_OBJECT (comp->parent, "Waiting for %ld us", add);
+ } else {
+ timeval = NULL;
+ GST_DEBUG_OBJECT (comp->parent, "Waiting for signal");
+ }
+
+ do {
+ signalled = g_cond_timed_wait (comp->state_cond, comp->state_lock, timeval);
+ } while (signalled && comp->last_error == OMX_ErrorNone
+ && comp->pending_state != OMX_StateInvalid);
+
+ if (signalled) {
+ if (comp->last_error != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (comp->parent,
+ "Got error while waiting for state change: %d", comp->last_error);
+ ret = OMX_StateInvalid;
+ } else if (comp->pending_state == OMX_StateInvalid) {
+ ret = comp->state;
+ } else {
+ ret = OMX_StateInvalid;
+ g_assert_not_reached ();
+ }
+ } else {
+ ret = OMX_StateInvalid;
+ comp->state = comp->pending_state = OMX_StateInvalid;
+ GST_WARNING_OBJECT (comp->parent, "Timeout while waiting for state change");
+ }
+
+done:
+ g_mutex_unlock (comp->state_lock);
+
+ GST_DEBUG_OBJECT (comp->parent, "Returning state %d", ret);
+
+ return ret;
+}
+
+GstOMXPort *
+gst_omx_component_add_port (GstOMXComponent * comp, guint32 index)
+{
+ gint i, n;
+ GstOMXPort *port;
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ OMX_ERRORTYPE err;
+
+ g_return_val_if_fail (comp != NULL, NULL);
+
+ /* Check if this port exists already */
+ n = comp->ports->len;
+ for (i = 0; i < n; i++) {
+ port = g_ptr_array_index (comp->ports, i);
+ g_return_val_if_fail (port->index != index, NULL);
+ }
+
+ GST_DEBUG_OBJECT (comp->parent, "Adding port %u", index);
+
+ port_def.nSize = sizeof (port_def);
+ port_def.nVersion.s.nVersionMajor = 1;
+ port_def.nVersion.s.nVersionMinor = 1;
+ port_def.nPortIndex = index;
+ err = OMX_GetParameter (comp->handle, OMX_IndexParamPortDefinition,
+ &port_def);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (comp->parent, "Failed to add port %u: %d", index, err);
+ return NULL;
+ }
+
+ port = g_slice_new0 (GstOMXPort);
+ port->comp = comp;
+ port->index = index;
+
+ port->port_def = port_def;
+
+ port->port_lock = g_mutex_new ();
+ port->port_cond = g_cond_new ();
+ port->pending_buffers = g_queue_new ();
+ port->flushing = TRUE;
+ port->flushed = FALSE;
+ port->settings_changed = FALSE;
+
+ g_ptr_array_add (comp->ports, port);
+
+ return port;
+}
+
+GstOMXPort *
+gst_omx_component_get_port (GstOMXComponent * comp, guint32 index)
+{
+ gint i, n;
+
+ /* No need for locking here because the
+ * ports are all added directly after
+ * creating the component and are removed
+ * when the component is destroyed.
+ */
+
+ n = comp->ports->len;
+ for (i = 0; i < n; i++) {
+ GstOMXPort *tmp = g_ptr_array_index (comp->ports, i);
+
+ if (tmp->index == index)
+ return tmp;
+ }
+ return NULL;
+}
+
+OMX_ERRORTYPE
+gst_omx_component_get_last_error (GstOMXComponent * comp)
+{
+ OMX_ERRORTYPE err;
+
+ g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
+
+ g_mutex_lock (comp->state_lock);
+ err = comp->last_error;
+ g_mutex_unlock (comp->state_lock);
+
+ GST_DEBUG_OBJECT (comp->parent, "Returning last error: %d", err);
+
+ return err;
+}
+
+void
+gst_omx_port_get_port_definition (GstOMXPort * port,
+ OMX_PARAM_PORTDEFINITIONTYPE * port_def)
+{
+ g_return_if_fail (port != NULL);
+
+ memset (port_def, 0, sizeof (*port_def));
+ port_def->nSize = sizeof (*port_def);
+ port_def->nVersion.s.nVersionMajor = 1;
+ port_def->nVersion.s.nVersionMinor = 1;
+ port_def->nPortIndex = port->index;
+
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition, port_def);
+}
+
+gboolean
+gst_omx_port_update_port_definition (GstOMXPort * port,
+ OMX_PARAM_PORTDEFINITIONTYPE * port_def)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ g_return_val_if_fail (port != NULL, FALSE);
+
+ g_mutex_lock (port->port_lock);
+ if (port_def)
+ err =
+ OMX_SetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ port_def);
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Updated port %u definition: %d",
+ port->index, err);
+
+ g_mutex_unlock (port->port_lock);
+
+ return (err == OMX_ErrorNone);
+}
+
+GstOMXBuffer *
+gst_omx_port_acquire_buffer (GstOMXPort * port)
+{
+ GstOMXBuffer *buf = NULL;
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Acquiring buffer from port %u",
+ port->index);
+
+ g_mutex_lock (port->port_lock);
+ if (port->flushing)
+ goto done;
+
+ /* Check if the component is in an error state */
+ g_mutex_lock (port->comp->state_lock);
+ if (port->comp->last_error != OMX_ErrorNone) {
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ g_mutex_unlock (port->comp->state_lock);
+
+ /* Wait until there's something in the queue
+ * or something else happened that requires
+ * to return a NULL buffer, e.g. an error
+ */
+ if (g_queue_is_empty (port->pending_buffers))
+ g_cond_wait (port->port_cond, port->port_lock);
+
+ /* Check if the component is in an error state */
+ g_mutex_lock (port->comp->state_lock);
+ if (port->comp->last_error != OMX_ErrorNone) {
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ g_mutex_unlock (port->comp->state_lock);
+
+ if (!g_queue_is_empty (port->pending_buffers))
+ buf = g_queue_pop_head (port->pending_buffers);
+
+done:
+ g_mutex_unlock (port->port_lock);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Acquired buffer %p from port %u", buf,
+ port->index);
+
+ return buf;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_release_buffer (GstOMXPort * port, GstOMXBuffer * buffer)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Releasing buffer %p to port %u",
+ buffer, port->index);
+
+ g_mutex_lock (port->port_lock);
+
+ if (port->flushing)
+ goto done;
+
+ buffer->used = TRUE;
+ if (port->port_def.eDir == OMX_DirInput) {
+ err = OMX_EmptyThisBuffer (port->comp->handle, buffer->omx_buf);
+ } else {
+ err = OMX_FillThisBuffer (port->comp->handle, buffer->omx_buf);
+ }
+
+done:
+ GST_DEBUG_OBJECT (port->comp->parent, "Released buffer %p to port %u: %d",
+ buffer, port->index, err);
+ g_mutex_unlock (port->port_lock);
+
+ return err;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_set_flushing (GstOMXPort * port, gboolean flush)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Setting port %d to %sflushing",
+ port->index, (flush ? "" : "not "));
+
+ g_mutex_lock (port->port_lock);
+ if (! !flush == ! !port->flushing) {
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %u was %sflushing already",
+ port->index, (flush ? "" : "not "));
+ goto done;
+ }
+
+ g_mutex_lock (port->comp->state_lock);
+ if ((port->comp->state != OMX_StateIdle
+ && port->comp->state != OMX_StateExecuting)
+ || port->comp->last_error != OMX_ErrorNone) {
+
+ if (port->comp->last_error != OMX_ErrorNone) {
+ err = port->comp->last_error;
+ GST_ERROR_OBJECT (port->comp->parent, "Component is in error state: %d",
+ err);
+ } else {
+ GST_ERROR_OBJECT (port->comp->parent, "Component is in wrong state: %d",
+ port->comp->state);
+ err = OMX_ErrorUndefined;
+ }
+
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ g_mutex_unlock (port->comp->state_lock);
+
+ port->flushing = flush;
+ if (flush)
+ g_cond_broadcast (port->port_cond);
+
+ if (flush) {
+ GTimeVal abstimeout, *timeval;
+ gboolean signalled;
+ OMX_ERRORTYPE last_error;
+
+ port->flushed = FALSE;
+ err =
+ OMX_SendCommand (port->comp->handle, OMX_CommandFlush, port->index,
+ NULL);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Error sending flush command to port %u: %d", port->index, err);
+ goto done;
+ }
+
+ g_get_current_time (&abstimeout);
+ g_time_val_add (&abstimeout, 5 * 10000000);
+ timeval = &abstimeout;
+ GST_DEBUG_OBJECT (port->comp->parent, "Waiting for 5s");
+
+ /* Retry until timeout or until an error happend or
+ * until all buffers were released by the component and
+ * the flush command completed */
+ do {
+ signalled = g_cond_timed_wait (port->port_cond, port->port_lock, timeval);
+
+ g_mutex_lock (port->comp->state_lock);
+ last_error = port->comp->last_error;
+ g_mutex_unlock (port->comp->state_lock);
+ } while (signalled && last_error == OMX_ErrorNone && !port->flushed
+ && port->buffers->len != g_queue_get_length (port->pending_buffers));
+ port->flushed = FALSE;
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %d flushed", port->index);
+ if (last_error != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Got error while flushing port %u: %d", port->index, last_error);
+ err = last_error;
+ goto done;
+ } else if (!signalled) {
+ GST_ERROR_OBJECT (port->comp->parent, "Timeout while flushing port %u",
+ port->index);
+ err = OMX_ErrorTimeout;
+ goto done;
+ }
+ } else {
+ if (port->port_def.eDir == OMX_DirOutput && port->buffers) {
+ gint i, n;
+
+ /* Enqueue all buffers for the component to fill */
+ n = port->buffers->len;
+ for (i = 0; i < n; i++) {
+ GstOMXBuffer *buf = g_ptr_array_index (port->buffers, i);
+
+ g_assert (!buf->used);
+
+ err = OMX_FillThisBuffer (port->comp->handle, buf->omx_buf);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Failed to pass buffer %p to port %u: %d", buf, port->index, err);
+ g_mutex_lock (port->comp->state_lock);
+ port->comp->last_error = err;
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ }
+
+ g_queue_clear (port->pending_buffers);
+ }
+ }
+
+done:
+ GST_DEBUG_OBJECT (port->comp->parent, "Set port %u to %sflushing: %d",
+ port->index, (flush ? "" : "not "), err);
+ g_mutex_unlock (port->port_lock);
+
+ return err;
+}
+
+gboolean
+gst_omx_port_is_flushing (GstOMXPort * port)
+{
+ gboolean flushing;
+
+ g_return_val_if_fail (port != NULL, FALSE);
+
+ g_mutex_lock (port->port_lock);
+ flushing = port->flushing;
+ g_mutex_unlock (port->port_lock);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %u is flushing: %d", port->index,
+ flushing);
+
+ return flushing;
+}
+
+/* Must be called while holding port->lock */
+static OMX_ERRORTYPE
+gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+ gint i, n;
+
+ g_assert (!port->buffers || port->buffers->len == 0);
+
+ /* Update the port definition to check if we need more
+ * buffers after the port configuration was done and to
+ * update the buffer size
+ */
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+
+ /* If the configured, actual number of buffers is less than
+ * the minimal number of buffers required, use the minimal
+ * number of buffers
+ */
+ if (port->port_def.nBufferCountActual < port->port_def.nBufferCountMin) {
+ port->port_def.nBufferCountActual = port->port_def.nBufferCountMin;
+ OMX_SetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ }
+
+ n = port->port_def.nBufferCountActual;
+ GST_DEBUG_OBJECT (port->comp->parent,
+ "Allocating %d buffers of size %u for port %u", n,
+ port->port_def.nBufferSize, port->index);
+
+ if (!port->buffers)
+ port->buffers = g_ptr_array_sized_new (n);
+
+ for (i = 0; i < n; i++) {
+ GstOMXBuffer *buf;
+
+ buf = g_slice_new0 (GstOMXBuffer);
+ buf->port = port;
+ buf->used = FALSE;
+ g_ptr_array_add (port->buffers, buf);
+
+ err =
+ OMX_AllocateBuffer (port->comp->handle, &buf->omx_buf, port->index, buf,
+ port->port_def.nBufferSize);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent, "Failed to allocate buffer: %d",
+ err);
+ port->comp->last_error = err;
+ break;
+ }
+
+ /* In the beginning all buffers are not owned by the component */
+ g_queue_push_tail (port->pending_buffers, buf);
+ }
+ g_cond_broadcast (port->port_cond);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Allocated buffers for port %u: %d",
+ port->index, err);
+
+ return err;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_allocate_buffers (GstOMXPort * port)
+{
+ OMX_ERRORTYPE err;
+
+ g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
+
+ g_mutex_lock (port->port_lock);
+ err = gst_omx_port_allocate_buffers_unlocked (port);
+ g_mutex_unlock (port->port_lock);
+
+ return err;
+}
+
+/* Must be called while holding port->lock */
+static OMX_ERRORTYPE
+gst_omx_port_deallocate_buffers_unlocked (GstOMXPort * port)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+ gint i, n;
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Deallocating buffers of port %u",
+ port->index);
+
+ if (!port->buffers) {
+ GST_DEBUG_OBJECT (port->comp->parent, "No buffers allocated for port %u",
+ port->index);
+ goto done;
+ }
+
+ /* We only allow deallocation of buffers after they
+ * were all released from the port, either by flushing
+ * the port or by disabling it.
+ */
+ g_assert (g_queue_get_length (port->pending_buffers) == port->buffers->len);
+
+ n = port->buffers->len;
+
+ for (i = 0; i < n; i++) {
+ GstOMXBuffer *buf = g_ptr_array_index (port->buffers, i);
+ OMX_ERRORTYPE tmp = OMX_ErrorNone;
+
+ g_assert (!buf->used);
+
+ /* omx_buf can be NULL if allocation failed earlier
+ * and we're just shutting down
+ *
+ * errors do not cause exiting this loop because we want
+ * to deallocate as much as possible.
+ */
+ if (buf->omx_buf) {
+ tmp = OMX_FreeBuffer (port->comp->handle, port->index, buf->omx_buf);
+ if (tmp != OMX_ErrorNone && err == OMX_ErrorNone)
+ err = tmp;
+
+ }
+ g_slice_free (GstOMXBuffer, buf);
+ }
+
+ g_queue_clear (port->pending_buffers);
+ g_ptr_array_unref (port->buffers);
+ port->buffers = NULL;
+done:
+ GST_DEBUG_OBJECT (port->comp->parent, "Deallocated buffers of port %u: %d",
+ port->index, err);
+
+ return err;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_deallocate_buffers (GstOMXPort * port)
+{
+ OMX_ERRORTYPE err;
+
+ g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
+
+ g_mutex_lock (port->port_lock);
+ err = gst_omx_port_deallocate_buffers_unlocked (port);
+ g_mutex_unlock (port->port_lock);
+
+ return err;
+}
+
+/* Must be called while holding port->lock */
+static OMX_ERRORTYPE
+gst_omx_port_set_enabled_unlocked (GstOMXPort * port, gboolean enabled)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+ GTimeVal abstimeout, *timeval;
+ gboolean signalled;
+ OMX_ERRORTYPE last_error;
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Setting port %u to %s", port->index,
+ (enabled ? "enabled" : "disabled"));
+
+ /* Check if the port is already enabled/disabled first */
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ if (! !port->port_def.bEnabled == ! !enabled)
+ goto done;
+
+ if (enabled)
+ err =
+ OMX_SendCommand (port->comp->handle, OMX_CommandPortEnable, port->index,
+ NULL);
+ else
+ err =
+ OMX_SendCommand (port->comp->handle, OMX_CommandPortDisable,
+ port->index, NULL);
+ if (err != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Failed to send enable/disable command to port %u: %d", port->index,
+ err);
+ goto done;
+ }
+
+ g_get_current_time (&abstimeout);
+ g_time_val_add (&abstimeout, 5 * 10000000);
+ timeval = &abstimeout;
+ GST_DEBUG_OBJECT (port->comp->parent, "Waiting for 5s");
+
+ /* FIXME XXX: The spec says that bEnabled should be set *immediately*
+ * but bellagio sets bEnabled after all buffers are allocated/deallocated
+ */
+
+ /* First wait until all buffers are released by the port */
+ signalled = TRUE;
+ last_error = OMX_ErrorNone;
+ while (signalled && last_error == OMX_ErrorNone && (port->buffers
+ && port->buffers->len !=
+ g_queue_get_length (port->pending_buffers))) {
+ signalled = g_cond_timed_wait (port->port_cond, port->port_lock, timeval);
+ g_mutex_lock (port->comp->state_lock);
+ last_error = port->comp->last_error;
+ g_mutex_unlock (port->comp->state_lock);
+ }
+
+ if (last_error != OMX_ErrorNone) {
+ err = last_error;
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Got error while waiting for port %u to release all buffers: %d",
+ port->index, err);
+ } else if (!signalled) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Timeout waiting for port %u to release all buffers", port->index);
+ err = OMX_ErrorTimeout;
+ }
+
+ /* Allocate/deallocate all buffers for the port to finish
+ * the enable/disable command */
+ if (enabled) {
+ /* If allocation fails this component can't really be used anymore */
+ if ((err = gst_omx_port_allocate_buffers_unlocked (port)) != OMX_ErrorNone) {
+ g_mutex_lock (port->comp->state_lock);
+ port->comp->last_error = err;
+ g_cond_broadcast (port->comp->state_cond);
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ } else {
+ /* If deallocation fails this component can't really be used anymore */
+ if ((err =
+ gst_omx_port_deallocate_buffers_unlocked (port)) != OMX_ErrorNone) {
+ g_mutex_lock (port->comp->state_lock);
+ port->comp->last_error = err;
+ g_cond_broadcast (port->comp->state_cond);
+ g_mutex_unlock (port->comp->state_lock);
+ goto done;
+ }
+ }
+
+ /* And now wait until the enable/disable command is finished */
+ signalled = TRUE;
+ last_error = OMX_ErrorNone;
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ while (signalled && last_error == OMX_ErrorNone
+ && (! !port->port_def.bEnabled != ! !enabled)) {
+ signalled = g_cond_timed_wait (port->port_cond, port->port_lock, timeval);
+ g_mutex_lock (port->comp->state_lock);
+ last_error = port->comp->last_error;
+ g_mutex_unlock (port->comp->state_lock);
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ }
+
+ if (!signalled) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Timeout waiting for port %u to be enabled/disabled", port->index);
+ err = OMX_ErrorTimeout;
+ } else if (last_error != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (port->comp->parent,
+ "Got error while waiting for port %u to be enabled/disabled: %d",
+ port->index, err);
+ err = last_error;
+ }
+
+done:
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %u is %s%s: %d", port->index,
+ (err == OMX_ErrorNone ? "" : "not "),
+ (enabled ? "enabled" : "disabled"), err);
+
+ return err;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_set_enabled (GstOMXPort * port, gboolean enabled)
+{
+ OMX_ERRORTYPE err;
+
+ g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
+
+ g_mutex_lock (port->port_lock);
+ err = gst_omx_port_set_enabled_unlocked (port, enabled);
+ g_mutex_unlock (port->port_lock);
+
+ return err;
+}
+
+gboolean
+gst_omx_port_is_enabled (GstOMXPort * port)
+{
+ gboolean enabled;
+
+ g_return_val_if_fail (port != NULL, FALSE);
+
+ g_mutex_lock (port->port_lock);
+ OMX_GetParameter (port->comp->handle, OMX_IndexParamPortDefinition,
+ &port->port_def);
+ enabled = port->port_def.bEnabled;
+ g_mutex_unlock (port->port_lock);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %u is enabled: %d", port->index,
+ enabled);
+
+ return enabled;
+}
+
+gboolean
+gst_omx_port_is_settings_changed (GstOMXPort * port)
+{
+ gboolean settings_changed;
+
+ g_return_val_if_fail (port != NULL, FALSE);
+
+ g_mutex_lock (port->port_lock);
+ settings_changed = port->settings_changed;
+ g_mutex_unlock (port->port_lock);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Port %u has settings-changed: %d",
+ port->index, settings_changed);
+
+ return settings_changed;
+}
+
+OMX_ERRORTYPE
+gst_omx_port_reconfigure (GstOMXPort * port)
+{
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
+
+ GST_DEBUG_OBJECT (port->comp->parent, "Reconfiguring port %u", port->index);
+
+ g_mutex_lock (port->port_lock);
+
+ if (!port->settings_changed)
+ goto done;
+
+ /* Disable and enable the port. This already takes
+ * care of deallocating and allocating buffers.
+ */
+ err = gst_omx_port_set_enabled_unlocked (port, FALSE);
+ if (err != OMX_ErrorNone)
+ goto done;
+
+ err = gst_omx_port_set_enabled_unlocked (port, TRUE);
+ if (err != OMX_ErrorNone)
+ goto done;
+
+ port->settings_changed = FALSE;
+
+done:
+ GST_DEBUG_OBJECT (port->comp->parent, "Reconfigured port %u: %d", port->index,
+ err);
+
+ g_mutex_unlock (port->port_lock);
+ return err;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean ret = FALSE;
+
+ GST_DEBUG_CATEGORY_INIT (gstomx_debug, "omx", 0, "gst-omx");
+
+ /* TODO: Use configuration file */
+ ret |=
+ gst_element_register (plugin, "omxmpeg4videodec", GST_RANK_PRIMARY,
+ GST_TYPE_OMX_MPEG4_VIDEO_DEC);
+
+ return ret;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "omx",
+ "GStreamer OpenMAX Plug-ins",
+ plugin_init,
+ PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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_H__
+#define __GST_OMX_H__
+
+#include <gst/gst.h>
+#include <OMX_Core.h>
+#include <OMX_Component.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstOMXCore GstOMXCore;
+typedef struct _GstOMXPort GstOMXPort;
+typedef enum _GstOMXPortDirection GstOMXPortDirection;
+typedef struct _GstOMXComponent GstOMXComponent;
+typedef struct _GstOMXBuffer GstOMXBuffer;
+
+struct _GstOMXCore {
+ /* Handle to the OpenMAX IL core shared library */
+ GModule *module;
+
+ /* Current number of users, transitions from/to 0
+ * call init/deinit */
+ GMutex *lock;
+ gint user_count; /* LOCK */
+
+ /* OpenMAX core library functions, protected with LOCK */
+ /* FIXME: OpenMAX spec does not specify that this is required
+ * but gst-openmax does it */
+ OMX_ERRORTYPE (*init) (void);
+ OMX_ERRORTYPE (*deinit) (void);
+ OMX_ERRORTYPE (*get_handle) (OMX_HANDLETYPE * handle,
+ OMX_STRING name, OMX_PTR data, OMX_CALLBACKTYPE * callbacks);
+ OMX_ERRORTYPE (*free_handle) (OMX_HANDLETYPE handle);
+};
+
+struct _GstOMXPort {
+ GstOMXComponent *comp;
+ OMX_U32 index;
+
+ /* Protects port_def, buffers, pending_buffers,
+ * settings_changed, flushing and flushed.
+ *
+ * Signalled if pending_buffers gets a
+ * new buffer or flushing/flushed is set
+ * to TRUE or an error happens. Always
+ * check comp->last_error after being
+ * signalled!
+ *
+ * Note: flushed==TRUE implies flushing==TRUE!
+ *
+ * Note: This lock must always be taken before
+ * the component's state lock if both are needed!
+ */
+ GMutex *port_lock;
+ GCond *port_cond;
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ GPtrArray *buffers;
+ GQueue *pending_buffers;
+ /* If TRUE we need to get the new caps
+ * of this port */
+ gboolean settings_changed;
+ gboolean flushing;
+ gboolean flushed; /* TRUE after OMX_CommandFlush was done */
+};
+
+struct _GstOMXComponent {
+ GstObject *parent;
+ OMX_HANDLETYPE handle;
+ GstOMXCore *core;
+
+ GPtrArray *ports;
+
+ /* Protecting state, pending_state and last_error
+ * Signalled if one of them changes
+ */
+ GMutex *state_lock;
+ GCond *state_cond;
+ OMX_STATETYPE state;
+ /* OMX_StateInvalid if no pending state */
+ OMX_STATETYPE pending_state;
+ /* OMX_ErrorNone usually, if different nothing will work */
+ OMX_ERRORTYPE last_error;
+};
+
+struct _GstOMXBuffer {
+ GstOMXPort *port;
+ OMX_BUFFERHEADERTYPE *omx_buf;
+
+ /* TRUE if the buffer is used by the port, i.e.
+ * between {Empty,Fill}ThisBuffer and the callback
+ */
+ gboolean used;
+};
+
+GstOMXCore * gst_omx_core_acquire (const gchar * filename);
+void gst_omx_core_release (GstOMXCore * core);
+
+
+GstOMXComponent * gst_omx_component_new (GstObject *parent, const gchar * core_name, const gchar * component_name);
+void gst_omx_component_free (GstOMXComponent * comp);
+
+OMX_ERRORTYPE gst_omx_component_set_state (GstOMXComponent * comp, OMX_STATETYPE state);
+OMX_STATETYPE gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout);
+
+OMX_ERRORTYPE gst_omx_component_get_last_error (GstOMXComponent * comp);
+
+GstOMXPort * gst_omx_component_add_port (GstOMXComponent * comp, guint32 index);
+GstOMXPort * gst_omx_component_get_port (GstOMXComponent * comp, guint32 index);
+
+
+void gst_omx_port_get_port_definition (GstOMXPort * port, OMX_PARAM_PORTDEFINITIONTYPE * port_def);
+gboolean gst_omx_port_update_port_definition (GstOMXPort *port, OMX_PARAM_PORTDEFINITIONTYPE *port_definition);
+
+GstOMXBuffer * gst_omx_port_acquire_buffer (GstOMXPort *port);
+OMX_ERRORTYPE gst_omx_port_release_buffer (GstOMXPort *port, GstOMXBuffer *buffer);
+
+OMX_ERRORTYPE gst_omx_port_set_flushing (GstOMXPort *port, gboolean flush);
+gboolean gst_omx_port_is_flushing (GstOMXPort *port);
+
+OMX_ERRORTYPE gst_omx_port_allocate_buffers (GstOMXPort *port);
+OMX_ERRORTYPE gst_omx_port_deallocate_buffers (GstOMXPort *port);
+
+OMX_ERRORTYPE gst_omx_port_reconfigure (GstOMXPort * port);
+
+OMX_ERRORTYPE gst_omx_port_set_enabled (GstOMXPort * port, gboolean enabled);
+gboolean gst_omx_port_is_enabled (GstOMXPort * port);
+
+gboolean gst_omx_port_is_settings_changed (GstOMXPort * port);
+
+G_END_DECLS
+
+#endif /* __GST_OMX_H__ */
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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 <gst/gst.h>
+
+#include "gstomxmpeg4videodec.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_omx_mpeg4_video_dec_debug_category);
+#define GST_CAT_DEFAULT gst_omx_mpeg4_video_dec_debug_category
+
+/* prototypes */
+static void gst_omx_mpeg4_video_dec_finalize (GObject * object);
+static gboolean gst_omx_mpeg4_video_dec_is_format_change (GstOMXVideoDec * dec,
+ GstOMXPort * port, GstVideoState * state);
+static gboolean gst_omx_mpeg4_video_dec_set_format (GstOMXVideoDec * dec,
+ GstOMXPort * port, GstVideoState * state);
+
+enum
+{
+ PROP_0
+};
+
+/* class initialization */
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_omx_mpeg4_video_dec_debug_category, "omxvideodec", 0, \
+ "debug category for gst-omx video decoder base class");
+
+GST_BOILERPLATE_FULL (GstOMXMPEG4VideoDec, gst_omx_mpeg4_video_dec,
+ GstOMXVideoDec, GST_TYPE_OMX_VIDEO_DEC, DEBUG_INIT);
+
+static GstStaticPadTemplate gst_omx_mpeg4_video_dec_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/mpeg, "
+ "mpegversion=(int) 4, "
+ "systemstream=(boolean) false, "
+ "parsed=(boolean) true, "
+ "width=(int) [ 16, 4096 ], " "height=(int) [ 16, 4096 ]")
+ );
+
+static GstStaticPadTemplate gst_omx_mpeg4_video_dec_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
+ );
+
+static void
+gst_omx_mpeg4_video_dec_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_omx_mpeg4_video_dec_src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_omx_mpeg4_video_dec_sink_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "OpenMAX MPEG4 Video Decoder",
+ "Codec/Decoder/Video",
+ "Decode MPEG4 video streams",
+ "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
+}
+
+static void
+gst_omx_mpeg4_video_dec_class_init (GstOMXMPEG4VideoDecClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstOMXVideoDecClass *videodec_class = GST_OMX_VIDEO_DEC_CLASS (klass);
+
+ gobject_class->finalize = gst_omx_mpeg4_video_dec_finalize;
+
+ /* TODO: Make this configurable */
+ videodec_class->core_name = "/usr/local/lib/libomxil-bellagio.so.0";
+ videodec_class->component_name = "OMX.st.video_decoder.mpeg4";
+
+ videodec_class->is_format_change =
+ GST_DEBUG_FUNCPTR (gst_omx_mpeg4_video_dec_is_format_change);
+ videodec_class->set_format =
+ GST_DEBUG_FUNCPTR (gst_omx_mpeg4_video_dec_set_format);
+}
+
+static void
+gst_omx_mpeg4_video_dec_init (GstOMXMPEG4VideoDec * self,
+ GstOMXMPEG4VideoDecClass * klass)
+{
+}
+
+static void
+gst_omx_mpeg4_video_dec_finalize (GObject * object)
+{
+ /* GstOMXMPEG4VideoDec *self = GST_OMX_MPEG4_VIDEO_DEC (object); */
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_omx_mpeg4_video_dec_is_format_change (GstOMXVideoDec * dec,
+ GstOMXPort * port, GstVideoState * state)
+{
+ return FALSE;
+}
+
+static gboolean
+gst_omx_mpeg4_video_dec_set_format (GstOMXVideoDec * dec, GstOMXPort * port,
+ GstVideoState * state)
+{
+ gboolean ret;
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+
+ gst_omx_port_get_port_definition (port, &port_def);
+ port_def.format.video.eCompressionFormat = OMX_VIDEO_CodingMPEG4;
+ ret = gst_omx_port_update_port_definition (port, &port_def);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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_MPEG4_VIDEO_DEC_H__
+#define __GST_OMX_MPEG4_VIDEO_DEC_H__
+
+#include <gst/gst.h>
+#include "gstomxvideodec.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_OMX_MPEG4_VIDEO_DEC \
+ (gst_omx_mpeg4_video_dec_get_type())
+#define GST_OMX_MPEG4_VIDEO_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_MPEG4_VIDEO_DEC,GstOMXMPEG4VideoDec))
+#define GST_OMX_MPEG4_VIDEO_DEC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_MPEG4_VIDEO_DEC,GstOMXMPEG4VideoDecClass))
+#define GST_OMX_MPEG4_VIDEO_DEC_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_MPEG4_VIDEO_DEC,GstOMXMPEG4VideoDecClass))
+#define GST_IS_OMX_MPEG4_VIDEO_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_MPEG4_VIDEO_DEC))
+#define GST_IS_OMX_MPEG4_VIDEO_DEC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_MPEG4_VIDEO_DEC))
+
+typedef struct _GstOMXMPEG4VideoDec GstOMXMPEG4VideoDec;
+typedef struct _GstOMXMPEG4VideoDecClass GstOMXMPEG4VideoDecClass;
+
+struct _GstOMXMPEG4VideoDec
+{
+ GstOMXVideoDec parent;
+};
+
+struct _GstOMXMPEG4VideoDecClass
+{
+ GstOMXVideoDecClass parent_class;
+};
+
+GType gst_omx_mpeg4_video_dec_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_OMX_MPEG4_VIDEO_DEC_H__ */
+
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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 <gst/gst.h>
+#include <string.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 _BufferIdentification BufferIdentification;
+struct _BufferIdentification
+{
+ guint64 timestamp;
+};
+
+/* prototypes */
+static void gst_omx_video_dec_finalize (GObject * object);
+
+static GstStateChangeReturn
+gst_omx_video_dec_change_state (GstElement * element,
+ GstStateChange transition);
+
+static gboolean gst_omx_video_dec_start (GstBaseVideoDecoder * decoder);
+static gboolean gst_omx_video_dec_stop (GstBaseVideoDecoder * decoder);
+static gboolean gst_omx_video_dec_set_format (GstBaseVideoDecoder * decoder,
+ GstVideoState * state);
+static gboolean gst_omx_video_dec_reset (GstBaseVideoDecoder * decoder);
+static GstFlowReturn gst_omx_video_dec_parse_data (GstBaseVideoDecoder *
+ decoder, gboolean at_eos);
+static GstFlowReturn gst_omx_video_dec_handle_frame (GstBaseVideoDecoder *
+ decoder, GstVideoFrame * frame);
+static GstFlowReturn gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder);
+
+enum
+{
+ PROP_0
+};
+
+/* class initialization */
+
+#define DEBUG_INIT(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_omx_video_dec_debug_category, "omxvideodec", 0, \
+ "debug category for gst-omx video decoder base class");
+
+GST_BOILERPLATE_FULL (GstOMXVideoDec, gst_omx_video_dec, GstBaseVideoDecoder,
+ GST_TYPE_BASE_VIDEO_DECODER, DEBUG_INIT);
+
+static void
+gst_omx_video_dec_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_omx_video_dec_class_init (GstOMXVideoDecClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstBaseVideoDecoderClass *base_video_decoder_class =
+ GST_BASE_VIDEO_DECODER_CLASS (klass);
+
+ gobject_class->finalize = gst_omx_video_dec_finalize;
+
+ element_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_omx_video_dec_change_state);
+
+ base_video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_video_dec_start);
+ base_video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_video_dec_stop);
+ base_video_decoder_class->reset = GST_DEBUG_FUNCPTR (gst_omx_video_dec_reset);
+ base_video_decoder_class->set_format =
+ GST_DEBUG_FUNCPTR (gst_omx_video_dec_set_format);
+ base_video_decoder_class->parse_data =
+ GST_DEBUG_FUNCPTR (gst_omx_video_dec_parse_data);
+ base_video_decoder_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_omx_video_dec_handle_frame);
+ base_video_decoder_class->finish =
+ GST_DEBUG_FUNCPTR (gst_omx_video_dec_finish);
+}
+
+static void
+gst_omx_video_dec_init (GstOMXVideoDec * self, GstOMXVideoDecClass * klass)
+{
+ GST_BASE_VIDEO_DECODER (self)->packetized = TRUE;
+}
+
+static gboolean
+gst_omx_video_dec_open (GstOMXVideoDec * self)
+{
+ GstOMXVideoDecClass *klass = GST_OMX_VIDEO_DEC_GET_CLASS (self);
+
+ self->component =
+ gst_omx_component_new (GST_OBJECT_CAST (self), klass->core_name,
+ klass->component_name);
+
+ if (!self->component)
+ return FALSE;
+
+ if (gst_omx_component_get_state (self->component,
+ GST_CLOCK_TIME_NONE) != OMX_StateLoaded)
+ return FALSE;
+
+ /* FIXME: Always 0 == input, 1 == output? Make configurable? Let subclass decide? */
+ self->in_port = gst_omx_component_add_port (self->component, 0);
+ self->out_port = gst_omx_component_add_port (self->component, 1);
+
+ if (!self->in_port || !self->out_port)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_omx_video_dec_close (GstOMXVideoDec * self)
+{
+ OMX_STATETYPE state;
+
+ gst_omx_component_set_state (self->component, OMX_StateLoaded);
+ gst_omx_port_deallocate_buffers (self->in_port);
+ gst_omx_port_deallocate_buffers (self->out_port);
+ state = gst_omx_component_get_state (self->component, 5 * GST_SECOND);
+
+ self->in_port = NULL;
+ self->out_port = NULL;
+ if (self->component)
+ gst_omx_component_free (self->component);
+ self->component = NULL;
+
+ return (state == OMX_StateLoaded);
+}
+
+static void
+gst_omx_video_dec_finalize (GObject * object)
+{
+ /* GstOMXVideoDec *self = GST_OMX_VIDEO_DEC (object); */
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstStateChangeReturn
+gst_omx_video_dec_change_state (GstElement * element, GstStateChange transition)
+{
+ GstOMXVideoDec *self;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ g_return_val_if_fail (GST_IS_OMX_VIDEO_DEC (element),
+ GST_STATE_CHANGE_FAILURE);
+ self = GST_OMX_VIDEO_DEC (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ if (!gst_omx_video_dec_open (self))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_omx_port_set_flushing (self->out_port, FALSE);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_omx_port_set_flushing (self->out_port, TRUE);
+ break;
+ default:
+ break;
+ }
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ if (!gst_omx_video_dec_close (self))
+ ret = GST_STATE_CHANGE_FAILURE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#define MAX_FRAME_DIST_TICKS (5 * OMX_TICKS_PER_SECOND)
+#define MAX_FRAME_DIST_FRAMES (100)
+
+static GstVideoFrame *
+_find_nearest_frame (GstOMXVideoDec * self, GstOMXBuffer * buf)
+{
+ GList *l, *best_l = NULL;
+ GstVideoFrame *best = NULL;
+ guint64 best_timestamp = 0;
+ guint64 best_diff = G_MAXUINT64;
+ BufferIdentification *best_id = NULL;
+
+ GST_OBJECT_LOCK (self);
+ for (l = self->pending_frames; l; l = l->next) {
+ GstVideoFrame *tmp = l->data;
+ BufferIdentification *id = tmp->coder_hook;
+ guint64 timestamp, diff;
+
+ /* This happens for frames that were just added but
+ * which were not passed to the component yet. Ignore
+ * them here!
+ */
+ if (!id)
+ continue;
+
+ timestamp = id->timestamp;
+
+ if (timestamp > buf->omx_buf->nTimeStamp)
+ diff = timestamp - buf->omx_buf->nTimeStamp;
+ else
+ diff = buf->omx_buf->nTimeStamp - timestamp;
+
+ if (best == NULL || diff < best_diff) {
+ best = tmp;
+ best_timestamp = timestamp;
+ best_diff = diff;
+ best_l = l;
+ best_id = id;
+
+ /* For frames without timestamp we simply take the first frame */
+ if ((buf->omx_buf->nTimeStamp == 0 && timestamp == 0) || diff == 0)
+ break;
+ }
+ }
+
+ if (best_id) {
+ for (l = self->pending_frames; l && l != best_l;) {
+ GstVideoFrame *tmp = l->data;
+ BufferIdentification *id = tmp->coder_hook;
+ guint64 diff_ticks, diff_frames;
+
+ if (id->timestamp > best_timestamp)
+ break;
+
+ if (id->timestamp == 0 || best_timestamp == 0)
+ diff_ticks = 0;
+ else
+ diff_ticks = best_timestamp - id->timestamp;
+ diff_frames = best->system_frame_number - tmp->system_frame_number;
+
+ if (diff_ticks > MAX_FRAME_DIST_TICKS
+ || diff_frames > MAX_FRAME_DIST_FRAMES) {
+ g_warning ("Too old frame, bug in decoder -- please file a bug");
+ gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
+ tmp);
+ self->pending_frames = g_list_delete_link (self->pending_frames, l);
+ l = self->pending_frames;
+ } else {
+ l = l->next;
+ }
+ }
+ }
+
+ if (best_l)
+ self->pending_frames = g_list_delete_link (self->pending_frames, best_l);
+ GST_OBJECT_UNLOCK (self);
+
+ return best;
+}
+
+static void
+gst_omx_video_dec_loop (GstOMXVideoDec * self)
+{
+ GstOMXPort *port = self->out_port;
+ GstOMXBuffer *buf = NULL;
+ GstVideoFrame *frame;
+ GstFlowReturn flow_ret = GST_FLOW_OK;
+
+ buf = gst_omx_port_acquire_buffer (port);
+ if (!buf) {
+ if (gst_omx_component_get_last_error (self->component) != OMX_ErrorNone) {
+ goto component_error;
+ } else if (!gst_omx_port_is_settings_changed (self->out_port)) {
+ goto flushing;
+ }
+ }
+
+ if (!GST_PAD_CAPS (GST_BASE_VIDEO_CODEC_SRC_PAD (self))
+ || gst_omx_port_is_settings_changed (self->out_port)) {
+ GstVideoState *state = &GST_BASE_VIDEO_CODEC (self)->state;
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+
+ gst_omx_port_get_port_definition (port, &port_def);
+ g_assert (port_def.format.video.eCompressionFormat ==
+ OMX_VIDEO_CodingUnused);
+
+ switch (port_def.format.video.eColorFormat) {
+ case OMX_COLOR_FormatYUV420Planar:
+ state->format = GST_VIDEO_FORMAT_I420;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ state->width = port_def.format.video.nFrameWidth;
+ state->height = port_def.format.video.nFrameHeight;
+ /* FIXME XXX: Bellagio does not set this to something useful... */
+ /* gst_util_double_to_fraction (port_def.format.video.xFramerate / ((gdouble) 0xffff), &state->fps_n, &state->fps_d); */
+ gst_base_video_decoder_set_src_caps (GST_BASE_VIDEO_DECODER (self));
+
+ if (gst_omx_port_is_settings_changed (self->out_port)) {
+ if (gst_omx_port_reconfigure (self->out_port) != OMX_ErrorNone)
+ goto reconfigure_error;
+ }
+
+ /* Get a new buffer */
+ if (!buf)
+ return;
+ }
+
+ GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %lu", buf->omx_buf->nFlags,
+ buf->omx_buf->nTimeStamp);
+
+ frame = _find_nearest_frame (self, buf);
+ if (!frame) {
+ GST_ERROR_OBJECT (self, "No corresponding frame found");
+ } else if (buf->omx_buf->nFilledLen > 0) {
+ if (gst_base_video_decoder_alloc_src_frame (GST_BASE_VIDEO_DECODER (self),
+ frame) == GST_FLOW_OK) {
+ memcpy (GST_BUFFER_DATA (frame->src_buffer),
+ buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+ buf->omx_buf->nFilledLen);
+ }
+ g_slice_free (BufferIdentification, frame->coder_hook);
+ flow_ret =
+ gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
+ frame);
+ } else if (frame != NULL) {
+ g_slice_free (BufferIdentification, frame->coder_hook);
+ flow_ret =
+ gst_base_video_decoder_finish_frame (GST_BASE_VIDEO_DECODER (self),
+ frame);
+ }
+
+ if (flow_ret == GST_FLOW_OK && (buf->omx_buf->nFlags & OMX_BUFFERFLAG_EOS))
+ flow_ret = GST_FLOW_UNEXPECTED;
+
+ gst_omx_port_release_buffer (port, buf);
+
+ if (flow_ret != GST_FLOW_OK)
+ goto flow_error;
+
+ return;
+
+component_error:
+ {
+ GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
+ ("OpenMAX component in error state %d",
+ gst_omx_component_get_last_error (self->component)));
+ gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ gst_event_new_eos ());
+ gst_pad_pause_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ return;
+ }
+flushing:
+ {
+ GST_DEBUG_OBJECT (self, "Flushing -- stopping task");
+ gst_pad_pause_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ return;
+ }
+flow_error:
+ {
+ if (flow_ret == GST_FLOW_UNEXPECTED) {
+ GST_DEBUG_OBJECT (self, "EOS");
+
+ gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ gst_event_new_eos ());
+ gst_pad_pause_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ } else if (flow_ret == GST_FLOW_NOT_LINKED
+ || flow_ret < GST_FLOW_UNEXPECTED) {
+ GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Internal data stream error."),
+ ("stream stopped, reason %s", gst_flow_get_name (flow_ret)));
+
+ gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ gst_event_new_eos ());
+ gst_pad_pause_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ }
+ return;
+ }
+reconfigure_error:
+ {
+ GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+ ("Unable to reconfigure output port"));
+ gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ gst_event_new_eos ());
+ gst_pad_pause_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ return;
+ }
+}
+
+static gboolean
+gst_omx_video_dec_start (GstBaseVideoDecoder * decoder)
+{
+ GstOMXVideoDec *self;
+ gboolean ret;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+
+ ret =
+ gst_pad_start_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ (GstTaskFunction) gst_omx_video_dec_loop, self);
+
+ return ret;
+}
+
+static gboolean
+gst_omx_video_dec_stop (GstBaseVideoDecoder * decoder)
+{
+ GstOMXVideoDec *self;
+ gboolean ret;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+
+ ret = gst_pad_stop_task (GST_BASE_VIDEO_CODEC_SRC_PAD (decoder));
+
+ gst_omx_component_set_state (self->component, OMX_StateIdle);
+
+ gst_omx_port_set_flushing (self->in_port, TRUE);
+ gst_omx_port_set_flushing (self->out_port, TRUE);
+
+ gst_omx_component_get_state (self->component, 5 * GST_SECOND);
+
+ g_list_free (self->pending_frames);
+ self->pending_frames = NULL;
+
+ gst_buffer_replace (&self->codec_data, NULL);
+
+ return ret;
+}
+
+static gboolean
+gst_omx_video_dec_set_format (GstBaseVideoDecoder * decoder,
+ GstVideoState * state)
+{
+ GstOMXVideoDec *self;
+ GstOMXVideoDecClass *klass;
+ gboolean is_format_change = FALSE;
+ gboolean needs_disable = FALSE;
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+ klass = GST_OMX_VIDEO_DEC_GET_CLASS (decoder);
+
+ /* FIXME: If called again later, properly set states and reinitialize
+ * only possible in Loaded state or if port is disabled =>
+ * delay if state>loaded into the port-disabled callback */
+
+ gst_omx_port_get_port_definition (self->in_port, &port_def);
+
+ /* Check if the caps change is a real format change or if only irrelevant
+ * parts of the caps have changed or nothing at all.
+ */
+ is_format_change |= port_def.format.video.nFrameWidth != state->width;
+ is_format_change |= port_def.format.video.nFrameHeight != state->height;
+ is_format_change |= (port_def.format.video.xFramerate == 0
+ && state->fps_n != 0)
+ || (port_def.format.video.xFramerate !=
+ (state->fps_n << 16) / (state->fps_d));
+ is_format_change |= (self->codec_data != state->codec_data);
+ if (klass->is_format_change)
+ is_format_change |= klass->is_format_change (self, self->in_port, state);
+
+ needs_disable =
+ gst_omx_component_get_state (self->component,
+ GST_CLOCK_TIME_NONE) != OMX_StateLoaded;
+ /* If the component is not in Loaded state and a real format change happens
+ * we have to disable the port and re-allocate all buffers. If no real
+ * format change happened we can just exit here.
+ */
+ if (needs_disable && !is_format_change) {
+ GST_DEBUG_OBJECT (self,
+ "Already running and caps did not change the format");
+ return TRUE;
+ }
+ if (needs_disable && is_format_change) {
+ if (gst_omx_port_set_enabled (self->in_port, FALSE) != OMX_ErrorNone)
+ return FALSE;
+ }
+
+ port_def.format.video.nFrameWidth = state->width - 100;
+ port_def.format.video.nFrameHeight = state->height;
+ if (state->fps_n == 0)
+ port_def.format.video.xFramerate = 0;
+ else
+ port_def.format.video.xFramerate = (state->fps_n << 16) / (state->fps_d);
+
+ if (!gst_omx_port_update_port_definition (self->in_port, &port_def))
+ return FALSE;
+ if (!gst_omx_port_update_port_definition (self->out_port, NULL))
+ return FALSE;
+
+ if (klass->set_format) {
+ if (!klass->set_format (self, self->in_port, state)) {
+ GST_ERROR_OBJECT (self, "Subclass failed to set the new format");
+ return FALSE;
+ }
+ }
+
+ gst_buffer_replace (&self->codec_data, state->codec_data);
+
+ if (needs_disable) {
+ if (gst_omx_port_set_enabled (self->in_port, TRUE) != OMX_ErrorNone)
+ return FALSE;
+ } else {
+ if (gst_omx_component_set_state (self->component,
+ OMX_StateIdle) != OMX_ErrorNone)
+ return FALSE;
+
+ /* Need to allocate buffers to reach Idle state */
+ if (gst_omx_port_allocate_buffers (self->in_port) != OMX_ErrorNone)
+ return FALSE;
+ if (gst_omx_port_allocate_buffers (self->out_port) != OMX_ErrorNone)
+ return FALSE;
+
+ if (gst_omx_component_get_state (self->component,
+ GST_CLOCK_TIME_NONE) != OMX_StateIdle)
+ return FALSE;
+
+ if (gst_omx_component_set_state (self->component,
+ OMX_StateExecuting) != OMX_ErrorNone)
+ return FALSE;
+ }
+
+ /* Unset flushing to allow ports to accept data again */
+ gst_omx_port_set_flushing (self->in_port, FALSE);
+ gst_omx_port_set_flushing (self->out_port, FALSE);
+
+ if (gst_omx_component_get_last_error (self->component) != OMX_ErrorNone) {
+ GST_ERROR_OBJECT (self, "Component in error state: %d",
+ gst_omx_component_get_last_error (self->component));
+ return FALSE;
+ }
+
+ /* Start the srcpad loop again */
+ gst_pad_start_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ (GstTaskFunction) gst_omx_video_dec_loop, decoder);
+
+ return (gst_omx_component_get_state (self->component,
+ GST_CLOCK_TIME_NONE) == OMX_StateExecuting);
+}
+
+static gboolean
+gst_omx_video_dec_reset (GstBaseVideoDecoder * decoder)
+{
+ GstOMXVideoDec *self;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+
+ GST_DEBUG_OBJECT (self, "Resetting decoder");
+
+ gst_omx_port_set_flushing (self->in_port, TRUE);
+ gst_omx_port_set_flushing (self->out_port, TRUE);
+
+ /* Wait until the srcpad loop is finished */
+ GST_PAD_STREAM_LOCK (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+ GST_PAD_STREAM_UNLOCK (GST_BASE_VIDEO_CODEC_SRC_PAD (self));
+
+ g_list_free (self->pending_frames);
+ self->pending_frames = NULL;
+
+ gst_omx_port_set_flushing (self->in_port, FALSE);
+ gst_omx_port_set_flushing (self->out_port, FALSE);
+
+ /* Start the srcpad loop again */
+ gst_pad_start_task (GST_BASE_VIDEO_CODEC_SRC_PAD (self),
+ (GstTaskFunction) gst_omx_video_dec_loop, decoder);
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_omx_video_dec_parse_data (GstBaseVideoDecoder * decoder, gboolean at_eos)
+{
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_omx_video_dec_handle_frame (GstBaseVideoDecoder * decoder,
+ GstVideoFrame * frame)
+{
+ GstOMXVideoDec *self;
+ GstOMXBuffer *buf;
+ GstBuffer *codec_data = NULL;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+
+ GST_DEBUG_OBJECT (self, "Handling frame");
+
+ if (gst_omx_port_is_flushing (self->in_port))
+ goto flushing;
+ if (gst_omx_component_get_last_error (self->component) != OMX_ErrorNone)
+ goto component_error;
+
+ if (gst_omx_port_is_settings_changed (self->in_port)) {
+ if (gst_omx_port_reconfigure (self->in_port) != OMX_ErrorNone)
+ goto reconfigure_error;
+ }
+
+ if (self->codec_data) {
+ codec_data = self->codec_data;
+
+ retry_codec_data:
+ buf = gst_omx_port_acquire_buffer (self->in_port);
+ if (!buf) {
+ if (gst_omx_component_get_last_error (self->component) != OMX_ErrorNone) {
+ goto component_error;
+ } else if (gst_omx_port_is_settings_changed (self->in_port)) {
+ if (gst_omx_port_reconfigure (self->in_port) != OMX_ErrorNone)
+ goto reconfigure_error;
+ goto retry_codec_data;
+ } else {
+ goto flushing;
+ }
+ }
+
+ if (buf->omx_buf->nAllocLen < GST_BUFFER_SIZE (codec_data)) {
+ gst_omx_port_release_buffer (self->in_port, buf);
+ goto too_large_codec_data;
+ }
+
+ buf->omx_buf->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+ buf->omx_buf->nFilledLen = GST_BUFFER_SIZE (codec_data);
+ memcpy (buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+ GST_BUFFER_DATA (codec_data), GST_BUFFER_SIZE (codec_data));
+
+ gst_omx_port_release_buffer (self->in_port, buf);
+ self->codec_data = NULL;
+ }
+
+ {
+ guint offset = 0;
+ GstClockTime timestamp, duration, timestamp_offset = 0;
+
+ GST_OBJECT_LOCK (self);
+ self->pending_frames = g_list_append (self->pending_frames, frame);
+ GST_OBJECT_UNLOCK (self);
+
+ timestamp = frame->presentation_timestamp;
+ duration = frame->presentation_duration;
+
+ while (offset < GST_BUFFER_SIZE (frame->sink_buffer)) {
+ buf = gst_omx_port_acquire_buffer (self->in_port);
+
+ if (!buf) {
+ if (gst_omx_component_get_last_error (self->component) != OMX_ErrorNone) {
+ goto component_error;
+ } else if (gst_omx_port_is_settings_changed (self->in_port)) {
+ if (gst_omx_port_reconfigure (self->in_port) != OMX_ErrorNone)
+ goto reconfigure_error;
+ continue;
+ } else {
+ goto flushing;
+ }
+ }
+
+ /* Copy the buffer content in chunks of size as requested
+ * by the port */
+ buf->omx_buf->nFilledLen =
+ MIN (GST_BUFFER_SIZE (frame->sink_buffer) - offset,
+ buf->omx_buf->nAllocLen - buf->omx_buf->nOffset);
+ memcpy (buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+ GST_BUFFER_DATA (frame->sink_buffer) + offset,
+ buf->omx_buf->nFilledLen);
+
+ /* Interpolate timestamps if we're passing the buffer
+ * in multiple chunks */
+ if (offset != 0 && duration != GST_CLOCK_TIME_NONE) {
+ timestamp_offset =
+ gst_util_uint64_scale (offset, duration,
+ GST_BUFFER_SIZE (frame->sink_buffer));
+ }
+
+ if (timestamp != GST_CLOCK_TIME_NONE) {
+ buf->omx_buf->nTimeStamp =
+ gst_util_uint64_scale (timestamp + timestamp_offset,
+ OMX_TICKS_PER_SECOND, GST_SECOND);
+ }
+ if (duration != GST_CLOCK_TIME_NONE) {
+ buf->omx_buf->nTickCount =
+ gst_util_uint64_scale (buf->omx_buf->nFilledLen, duration,
+ GST_BUFFER_SIZE (frame->sink_buffer));
+ }
+
+ if (offset == 0) {
+ BufferIdentification *id = g_slice_new0 (BufferIdentification);
+
+ id->timestamp = buf->omx_buf->nTimeStamp;
+ frame->coder_hook = id;
+ }
+
+ /* TODO: Set flags
+ * - OMX_BUFFERFLAG_DECODEONLY for buffers that are outside
+ * the segment
+ * - OMX_BUFFERFLAG_SYNCFRAME for non-delta frames
+ * - OMX_BUFFERFLAG_ENDOFFRAME for parsed input
+ */
+
+ offset += buf->omx_buf->nFilledLen;
+ gst_omx_port_release_buffer (self->in_port, buf);
+ }
+ }
+
+ return GST_FLOW_OK;
+
+too_large_codec_data:
+ {
+ GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL),
+ ("codec_data larger than supported by OpenMAX port (%u > %u)",
+ GST_BUFFER_SIZE (codec_data), self->in_port->port_def.nBufferSize));
+ return GST_FLOW_ERROR;
+ }
+
+component_error:
+ {
+ GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
+ ("OpenMAX component in error state %d",
+ gst_omx_component_get_last_error (self->component)));
+ return GST_FLOW_ERROR;
+ }
+
+flushing:
+ {
+ GST_DEBUG_OBJECT (self, "Flushing -- returning WRONG_STATE");
+ return GST_FLOW_WRONG_STATE;
+ }
+reconfigure_error:
+ {
+ GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+ ("Unable to reconfigure input port"));
+ return GST_FLOW_ERROR;
+ }
+}
+
+static GstFlowReturn
+gst_omx_video_dec_finish (GstBaseVideoDecoder * decoder)
+{
+ GstOMXVideoDec *self;
+ GstOMXBuffer *buf;
+
+ self = GST_OMX_VIDEO_DEC (decoder);
+
+ GST_DEBUG_OBJECT (self, "Sending EOS to the component");
+
+ /* Send an EOS buffer to the component and let the base
+ * class drop the EOS event. We will send it later when
+ * the EOS buffer arrives on the output port. */
+ buf = gst_omx_port_acquire_buffer (self->in_port);
+ if (buf) {
+ buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS;
+ gst_omx_port_release_buffer (self->in_port, buf);
+ }
+
+ return GST_BASE_VIDEO_DECODER_FLOW_DROPPED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * 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_VIDEO_DEC_H__
+#define __GST_OMX_VIDEO_DEC_H__
+
+#include <gst/gst.h>
+#include "gstbasevideodecoder.h"
+
+#include "gstomx.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_OMX_VIDEO_DEC \
+ (gst_omx_video_dec_get_type())
+#define GST_OMX_VIDEO_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_VIDEO_DEC,GstOMXVideoDec))
+#define GST_OMX_VIDEO_DEC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_VIDEO_DEC,GstOMXVideoDecClass))
+#define GST_OMX_VIDEO_DEC_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_VIDEO_DEC,GstOMXVideoDecClass))
+#define GST_IS_OMX_VIDEO_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_VIDEO_DEC))
+#define GST_IS_OMX_VIDEO_DEC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_VIDEO_DEC))
+
+typedef struct _GstOMXVideoDec GstOMXVideoDec;
+typedef struct _GstOMXVideoDecClass GstOMXVideoDecClass;
+
+struct _GstOMXVideoDec
+{
+ GstBaseVideoDecoder parent;
+
+ /* < protected > */
+ GstOMXCore *core;
+ GstOMXComponent *component;
+ GstOMXPort *in_port, *out_port;
+
+ /* < private > */
+ GList *pending_frames;
+
+ GstBuffer *codec_data;
+};
+
+struct _GstOMXVideoDecClass
+{
+ GstBaseVideoDecoderClass parent_class;
+
+ const gchar *core_name;
+ const gchar *component_name;
+
+ gboolean (*is_format_change) (GstOMXVideoDec * self, GstOMXPort * port, GstVideoState * state);
+ gboolean (*set_format) (GstOMXVideoDec * self, GstOMXPort * port, GstVideoState * state);
+};
+
+GType gst_omx_video_dec_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_OMX_VIDEO_DEC_H__ */