gstreamer/omx/gstomx.c
Sebastian Dröge 70b735da82 omx: Fix debug level for flushing in wrong state from ERROR to DEBUG
It's not really an error and doesn't matter at all if flush is called
when the component is not running.
2011-11-15 09:47:55 -08:00

2102 lines
60 KiB
C

/*
* 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"
#include "gstomxh264dec.h"
#include "gstomxh263dec.h"
#include "gstomxwmvdec.h"
#include "gstomxmpeg4videoenc.h"
#include "gstomxh264enc.h"
#include "gstomxh263enc.h"
#include "gstomxaacenc.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);
/* FIXME: 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 {
GST_ERROR_OBJECT (comp->parent, "Port %u was not flushing",
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);
port->enabled_changed = TRUE;
g_cond_broadcast (port->port_cond);
g_mutex_unlock (port->port_lock);
break;
}
default:
break;
}
break;
}
case OMX_EventError:
{
OMX_ERRORTYPE err = nData1;
if (err == OMX_ErrorNone)
break;
GST_ERROR_OBJECT (comp->parent, "Got error: %s (0x%08x)",
gst_omx_error_to_string (err), err);
/* Error events are always fatal */
gst_omx_component_set_last_error (comp, err);
break;
}
case OMX_EventPortSettingsChanged:
{
gint i, n;
guint32 port_index;
GList *outports = NULL, *l, *k;
if (!(comp->hacks &
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_NDATA_PARAMETER_SWAP)) {
port_index = nData1;
} else {
port_index = nData2;
}
if (port_index == 0
&& (comp->hacks &
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_PORT_0_TO_1))
port_index = 1;
GST_DEBUG_OBJECT (comp->parent, "Settings changed (port index: %d)",
port_index);
/* Now update the ports' states */
n = (comp->ports ? comp->ports->len : 0);
for (i = 0; i < n; i++) {
GstOMXPort *port = g_ptr_array_index (comp->ports, i);
g_mutex_lock (port->port_lock);
if (port_index == OMX_ALL || port_index == port->index) {
port->settings_cookie++;
if (port->port_def.eDir == OMX_DirOutput)
outports = g_list_prepend (outports, port);
g_cond_broadcast (port->port_cond);
}
g_mutex_unlock (port->port_lock);
}
g_mutex_lock (comp->state_lock);
for (k = outports; k; k = k->next) {
gboolean found = FALSE;
for (l = comp->pending_reconfigure_outports; l; l = l->next) {
if (l->data == k->data) {
found = TRUE;
break;
}
}
if (!found)
comp->pending_reconfigure_outports =
g_list_prepend (comp->pending_reconfigure_outports, k->data);
}
if (comp->pending_reconfigure_outports)
g_atomic_int_set (&comp->have_pending_reconfigure_outports, 1);
g_mutex_unlock (comp->state_lock);
g_list_free (outports);
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;
GstOMXPort *port;
GstOMXComponent *comp;
if (buf == NULL) {
GST_ERROR ("Have unknown or deallocated buffer %p", pBuffer);
return OMX_ErrorNone;
}
port = buf->port;
comp = port->comp;
g_assert (buf->omx_buf == pBuffer);
/* Input buffer is empty again and can
* be used to contain new input */
g_mutex_lock (port->port_lock);
GST_DEBUG_OBJECT (comp->parent, "Port %u emptied buffer %p",
port->index, buf);
buf->used = FALSE;
/* XXX: Some OMX implementations don't reset nOffset
* when the complete buffer is emptied but instead
* only reset nFilledLen. We reset nOffset to 0
* if nFilledLen == 0, which is safe to do because
* the offset *must* be 0 if the buffer is not
* filled at all.
*
* Seen in QCOM's OMX implementation.
*/
if (buf->omx_buf->nFilledLen == 0)
buf->omx_buf->nOffset = 0;
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
buf->omx_buf->nFlags = 0;
g_queue_push_tail (port->pending_buffers, buf);
g_cond_broadcast (port->port_cond);
g_mutex_unlock (port->port_lock);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE
FillBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE * pBuffer)
{
GstOMXBuffer *buf = pBuffer->pAppPrivate;
GstOMXPort *port;
GstOMXComponent *comp;
if (buf == NULL) {
GST_ERROR ("Have unknown or deallocated buffer %p", pBuffer);
return OMX_ErrorNone;
}
port = buf->port;
comp = port->comp;
g_assert (buf->omx_buf == pBuffer);
/* Output buffer contains output now or
* the port was flushed */
g_mutex_lock (port->port_lock);
GST_DEBUG_OBJECT (comp->parent, "Port %u filled buffer %p", port->index, buf);
buf->used = FALSE;
g_queue_push_tail (port->pending_buffers, buf);
g_cond_broadcast (port->port_cond);
g_mutex_unlock (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, const gchar * component_role, guint64 hacks)
{
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->hacks = hacks;
comp->ports = g_ptr_array_new ();
comp->n_in_ports = 0;
comp->n_out_ports = 0;
comp->state_lock = g_mutex_new ();
comp->state_cond = g_cond_new ();
comp->pending_state = OMX_StateInvalid;
comp->last_error = OMX_ErrorNone;
/* Set component role if any */
if (component_role) {
OMX_PARAM_COMPONENTROLETYPE param;
GST_OMX_INIT_STRUCT (&param);
g_strlcpy ((gchar *) param.cRole, component_role, sizeof (param.cRole));
err =
gst_omx_component_set_parameter (comp,
OMX_IndexParamStandardComponentRole, &param);
GST_DEBUG_OBJECT (parent, "Setting component role to '%s': %s (0x%08x)",
component_role, gst_omx_error_to_string (err), err);
/* If setting the role failed this component is unusable */
if (err != OMX_ErrorNone) {
gst_omx_component_free (comp);
return NULL;
}
}
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);
gst_omx_port_deallocate_buffers (port);
g_mutex_free (port->port_lock);
g_cond_free (port->port_cond);
g_queue_free (port->pending_buffers);
g_slice_free (GstOMXPort, port);
}
#if GLIB_CHECK_VERSION(2,22,0)
g_ptr_array_unref (comp->ports);
#else
g_ptr_array_free (comp->ports, TRUE);
#endif
comp->ports = NULL;
}
comp->core->free_handle (comp->handle);
gst_omx_core_release (comp->core);
g_cond_free (comp->state_cond);
g_mutex_free (comp->state_lock);
gst_object_unref (comp->parent);
g_slice_free (GstOMXComponent, comp);
}
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 && state > old_state) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
if (old_state == state || comp->pending_state == state) {
GST_DEBUG_OBJECT (comp->parent, "Component already in state %d", state);
goto done;
}
comp->pending_state = state;
/* Reset some things */
if (old_state == OMX_StateExecuting && state < old_state) {
g_atomic_int_set (&comp->have_pending_reconfigure_outports, 0);
g_list_free (comp->pending_reconfigure_outports);
comp->pending_reconfigure_outports = NULL;
/* Notify all inports that are still waiting */
g_cond_broadcast (comp->state_cond);
}
/* Release lock because the command could call into the callbacks,
* which take the lock again */
g_mutex_unlock (comp->state_lock);
err = OMX_SendCommand (comp->handle, OMX_CommandStateSet, state, NULL);
g_mutex_lock (comp->state_lock);
/* No need to check if anything has changed here */
done:
g_mutex_unlock (comp->state_lock);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Error setting state from %d to %d: %s (0x%08x)", old_state, state,
gst_omx_error_to_string (err), err);
gst_omx_component_set_last_error (comp, err);
}
return err;
}
OMX_STATETYPE
gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout)
{
OMX_STATETYPE ret;
GTimeVal *timeval, abstimeout;
gboolean signalled = TRUE;
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) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (comp->last_error), comp->last_error);
ret = OMX_StateInvalid;
goto done;
}
if (timeout != GST_CLOCK_TIME_NONE) {
glong add = timeout / (GST_SECOND / G_USEC_PER_SEC);
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: %s (0x%08x)",
gst_omx_error_to_string (comp->last_error), comp->last_error);
ret = OMX_StateInvalid;
} else if (comp->pending_state == OMX_StateInvalid) {
/* State change finished and everything's fine */
ret = comp->state;
} else {
ret = OMX_StateInvalid;
g_assert_not_reached ();
}
} else {
ret = OMX_StateInvalid;
GST_WARNING_OBJECT (comp->parent, "Timeout while waiting for state change");
}
done:
g_mutex_unlock (comp->state_lock);
/* If we waited and timed out this component is unusable now */
if (!signalled)
gst_omx_component_set_last_error (comp, OMX_ErrorTimeout);
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);
GST_OMX_INIT_STRUCT (&port_def);
port_def.nPortIndex = index;
err = gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port_def);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Failed to add port %u: %s (0x%08x)", index,
gst_omx_error_to_string (err), 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;
port->enabled_changed = FALSE;
if (port->port_def.eDir == OMX_DirInput)
comp->n_in_ports++;
else
comp->n_out_ports++;
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;
}
void
gst_omx_component_trigger_settings_changed (GstOMXComponent * comp,
guint32 port_index)
{
g_return_if_fail (comp != NULL);
/* Reverse hacks */
if (port_index == 1
&& (comp->hacks & GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_PORT_0_TO_1))
port_index = 0;
if (!(comp->hacks &
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_NDATA_PARAMETER_SWAP)) {
EventHandler (comp->handle, comp, OMX_EventPortSettingsChanged, port_index,
0, NULL);
} else {
EventHandler (comp->handle, comp, OMX_EventPortSettingsChanged, 0,
port_index, NULL);
}
}
/* NOTE: This takes comp->state_lock *and* *all* port->port_lock! */
void
gst_omx_component_set_last_error (GstOMXComponent * comp, OMX_ERRORTYPE err)
{
gint i, n;
g_return_if_fail (comp != NULL);
if (err == OMX_ErrorNone)
return;
GST_ERROR_OBJECT (comp->parent, "Setting last error: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_mutex_lock (comp->state_lock);
/* We only set the first error ever from which
* we can't recover anymore.
*/
if (comp->last_error == OMX_ErrorNone)
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 ? comp->ports->len : 0);
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);
}
}
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: %s (0x%08x)",
gst_omx_error_to_string (err), err);
return err;
}
const gchar *
gst_omx_component_get_last_error_string (GstOMXComponent * comp)
{
g_return_val_if_fail (comp != NULL, NULL);
return gst_omx_error_to_string (gst_omx_component_get_last_error (comp));
}
OMX_ERRORTYPE
gst_omx_component_get_parameter (GstOMXComponent * comp, OMX_INDEXTYPE index,
gpointer param)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (param != NULL, OMX_ErrorUndefined);
GST_DEBUG_OBJECT (comp->parent, "Getting parameter at index 0x%08x", index);
err = OMX_GetParameter (comp->handle, index, param);
GST_DEBUG_OBJECT (comp->parent, "Got parameter at index 0x%08x: %s (0x%08x)",
index, gst_omx_error_to_string (err), err);
return err;
}
OMX_ERRORTYPE
gst_omx_component_set_parameter (GstOMXComponent * comp, OMX_INDEXTYPE index,
gpointer param)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (param != NULL, OMX_ErrorUndefined);
GST_DEBUG_OBJECT (comp->parent, "Setting parameter at index 0x%08x", index);
err = OMX_SetParameter (comp->handle, index, param);
GST_DEBUG_OBJECT (comp->parent, "Set parameter at index 0x%08x: %s (0x%08x)",
index, gst_omx_error_to_string (err), err);
return err;
}
OMX_ERRORTYPE
gst_omx_component_get_config (GstOMXComponent * comp, OMX_INDEXTYPE index,
gpointer config)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (config != NULL, OMX_ErrorUndefined);
GST_DEBUG_OBJECT (comp->parent, "Getting configuration at index 0x%08x",
index);
err = OMX_GetConfig (comp->handle, index, config);
GST_DEBUG_OBJECT (comp->parent, "Got parameter at index 0x%08x: %s (0x%08x)",
index, gst_omx_error_to_string (err), err);
return err;
}
OMX_ERRORTYPE
gst_omx_component_set_config (GstOMXComponent * comp, OMX_INDEXTYPE index,
gpointer config)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (comp != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (config != NULL, OMX_ErrorUndefined);
GST_DEBUG_OBJECT (comp->parent, "Setting configuration at index 0x%08x",
index);
err = OMX_SetConfig (comp->handle, index, config);
GST_DEBUG_OBJECT (comp->parent, "Set parameter at index 0x%08x: %s (0x%08x)",
index, gst_omx_error_to_string (err), err);
return err;
}
void
gst_omx_port_get_port_definition (GstOMXPort * port,
OMX_PARAM_PORTDEFINITIONTYPE * port_def)
{
GstOMXComponent *comp;
g_return_if_fail (port != NULL);
comp = port->comp;
GST_OMX_INIT_STRUCT (port_def);
port_def->nPortIndex = port->index;
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
port_def);
}
gboolean
gst_omx_port_update_port_definition (GstOMXPort * port,
OMX_PARAM_PORTDEFINITIONTYPE * port_def)
{
OMX_ERRORTYPE err = OMX_ErrorNone;
GstOMXComponent *comp;
g_return_val_if_fail (port != NULL, FALSE);
comp = port->comp;
g_mutex_lock (port->port_lock);
if (port_def)
err =
gst_omx_component_set_parameter (comp, OMX_IndexParamPortDefinition,
port_def);
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
GST_DEBUG_OBJECT (comp->parent, "Updated port %u definition: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
g_mutex_unlock (port->port_lock);
return (err == OMX_ErrorNone);
}
GstOMXAcquireBufferReturn
gst_omx_port_acquire_buffer (GstOMXPort * port, GstOMXBuffer ** buf)
{
GstOMXAcquireBufferReturn ret = GST_OMX_ACQUIRE_BUFFER_ERROR;
GstOMXComponent *comp;
OMX_ERRORTYPE err;
GstOMXBuffer *_buf = NULL;
g_return_val_if_fail (port != NULL, GST_OMX_ACQUIRE_BUFFER_ERROR);
g_return_val_if_fail (buf != NULL, GST_OMX_ACQUIRE_BUFFER_ERROR);
*buf = NULL;
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Acquiring buffer from port %u", port->index);
g_mutex_lock (port->port_lock);
retry:
/* Check if the component is in an error state */
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component is in error state: %s",
gst_omx_error_to_string (err));
ret = GST_OMX_ACQUIRE_BUFFER_ERROR;
goto done;
}
/* Check if the port is flushing */
if (port->flushing) {
ret = GST_OMX_ACQUIRE_BUFFER_FLUSHING;
goto done;
}
/* If this is an input port and at least one of the output ports
* needs to be reconfigured, we wait until all output ports are
* reconfigured. Afterwards this port is reconfigured if required
* or buffers are returned to be filled as usual.
*/
if (port->port_def.eDir == OMX_DirInput) {
if (g_atomic_int_get (&comp->have_pending_reconfigure_outports)) {
g_mutex_unlock (port->port_lock);
g_mutex_lock (comp->state_lock);
while (g_atomic_int_get (&comp->have_pending_reconfigure_outports) &&
(err = comp->last_error) == OMX_ErrorNone && !port->flushing) {
GST_DEBUG_OBJECT (comp->parent,
"Waiting for output ports to reconfigure");
g_cond_wait (comp->state_cond, comp->state_lock);
}
g_mutex_unlock (comp->state_lock);
g_mutex_lock (port->port_lock);
goto retry;
}
/* Only if this port needs to be reconfigured too notify
* the caller about it */
if (port->settings_cookie != port->configured_settings_cookie) {
ret = GST_OMX_ACQUIRE_BUFFER_RECONFIGURE;
port->settings_changed = TRUE;
goto done;
}
}
/* If we have an output port that needs to be reconfigured
* and it still has buffers pending for the old configuration
* we first return them.
* NOTE: If buffers for this configuration arrive later
* we have to drop them... */
if (port->port_def.eDir == OMX_DirOutput &&
port->settings_cookie != port->configured_settings_cookie) {
if (!g_queue_is_empty (port->pending_buffers)) {
GST_DEBUG_OBJECT (comp->parent,
"Output port %u needs reconfiguration but has buffers pending",
port->index);
_buf = g_queue_pop_head (port->pending_buffers);
g_assert (_buf != NULL);
ret = GST_OMX_ACQUIRE_BUFFER_OK;
goto done;
}
ret = GST_OMX_ACQUIRE_BUFFER_RECONFIGURE;
port->settings_changed = TRUE;
goto done;
}
if (port->settings_changed) {
GST_DEBUG_OBJECT (comp->parent,
"Port %u has settings changed, need new caps", port->index);
ret = GST_OMX_ACQUIRE_BUFFER_RECONFIGURED;
port->settings_changed = FALSE;
goto done;
}
/*
* At this point we have no error or flushing port
* and a properly configured port.
*
*/
/* If the queue is empty we wait until a buffer
* arrives, an error happens, the port is flushing
* or the port needs to be reconfigured.
*/
if (g_queue_is_empty (port->pending_buffers)) {
GST_DEBUG_OBJECT (comp->parent, "Queue of port %u is empty", port->index);
g_cond_wait (port->port_cond, port->port_lock);
} else {
GST_DEBUG_OBJECT (comp->parent, "Port %u has pending buffers");
_buf = g_queue_pop_head (port->pending_buffers);
ret = GST_OMX_ACQUIRE_BUFFER_OK;
goto done;
}
/* And now check everything again and maybe get a buffer */
goto retry;
done:
g_mutex_unlock (port->port_lock);
if (_buf) {
g_assert (_buf == _buf->omx_buf->pAppPrivate);
*buf = _buf;
}
GST_DEBUG_OBJECT (comp->parent, "Acquired buffer %p from port %u: %d", _buf,
port->index, ret);
return ret;
}
OMX_ERRORTYPE
gst_omx_port_release_buffer (GstOMXPort * port, GstOMXBuffer * buf)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (buf != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (buf->port == port, OMX_ErrorUndefined);
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Releasing buffer %p to port %u",
buf, port->index);
g_mutex_lock (port->port_lock);
if (port->port_def.eDir == OMX_DirInput) {
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
buf->omx_buf->nFlags = 0;
}
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component is in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
g_queue_push_tail (port->pending_buffers, buf);
g_cond_broadcast (port->port_cond);
goto done;
}
if (port->flushing) {
GST_DEBUG_OBJECT (comp->parent, "Port %u is flushing, not releasing buffer",
port->index);
g_queue_push_tail (port->pending_buffers, buf);
g_cond_broadcast (port->port_cond);
goto done;
}
g_assert (buf == buf->omx_buf->pAppPrivate);
/* FIXME: What if the settings cookies don't match? */
buf->used = TRUE;
if (port->port_def.eDir == OMX_DirInput) {
err = OMX_EmptyThisBuffer (comp->handle, buf->omx_buf);
} else {
err = OMX_FillThisBuffer (comp->handle, buf->omx_buf);
}
GST_DEBUG_OBJECT (comp->parent, "Released buffer %p to port %u: %s (0x%08x)",
buf, port->index, gst_omx_error_to_string (err), err);
done:
g_mutex_unlock (port->port_lock);
return err;
}
OMX_ERRORTYPE
gst_omx_port_set_flushing (GstOMXPort * port, gboolean flush)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Setting port %d to %sflushing",
port->index, (flush ? "" : "not "));
g_mutex_lock (port->port_lock);
if (! !flush == ! !port->flushing) {
GST_DEBUG_OBJECT (comp->parent, "Port %u was %sflushing already",
port->index, (flush ? "" : "not "));
goto done;
}
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component is in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
g_mutex_lock (comp->state_lock);
if (comp->state != OMX_StateIdle && comp->state != OMX_StateExecuting) {
GST_DEBUG_OBJECT (comp->parent, "Component is in wrong state: %d",
comp->state);
err = OMX_ErrorUndefined;
g_mutex_unlock (comp->state_lock);
goto done;
}
g_mutex_unlock (comp->state_lock);
port->flushing = flush;
if (flush) {
GTimeVal abstimeout, *timeval;
gboolean signalled;
OMX_ERRORTYPE last_error;
g_cond_broadcast (port->port_cond);
/* We also need to signal the state cond because
* an input port might wait on this for the output
* ports to reconfigure. This will not confuse
* other waiters on the state cond because they will
* additionally check if the condition they're waiting
* for is true after waking up.
*/
g_mutex_lock (comp->state_lock);
g_cond_broadcast (comp->state_cond);
g_mutex_unlock (comp->state_lock);
/* Now flush the port */
port->flushed = FALSE;
/* Unlock because this could call one of the callbacks which
* also take the lock */
g_mutex_unlock (port->port_lock);
err = OMX_SendCommand (comp->handle, OMX_CommandFlush, port->index, NULL);
g_mutex_lock (port->port_lock);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Error sending flush command to port %u: %s (0x%08x)", port->index,
gst_omx_error_to_string (err), err);
goto done;
}
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Component is in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
if (! !port->flushing != ! !flush) {
GST_ERROR_OBJECT (comp->parent, "Another flush happened in the meantime");
goto done;
}
g_get_current_time (&abstimeout);
g_time_val_add (&abstimeout, 5 * G_USEC_PER_SEC);
timeval = &abstimeout;
GST_DEBUG_OBJECT (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 */
signalled = TRUE;
last_error = OMX_ErrorNone;
while (signalled && last_error == OMX_ErrorNone && !port->flushed
&& port->buffers->len != g_queue_get_length (port->pending_buffers)) {
signalled = g_cond_timed_wait (port->port_cond, port->port_lock, timeval);
last_error = gst_omx_component_get_last_error (comp);
}
port->flushed = FALSE;
GST_DEBUG_OBJECT (comp->parent, "Port %d flushed", port->index);
if (last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Got error while flushing port %u: %s (0x%08x)", port->index,
gst_omx_error_to_string (last_error), last_error);
err = last_error;
goto done;
} else if (!signalled) {
GST_ERROR_OBJECT (comp->parent, "Timeout while flushing port %u",
port->index);
err = OMX_ErrorTimeout;
goto done;
}
} else {
if (port->port_def.eDir == OMX_DirOutput && port->buffers) {
GstOMXBuffer *buf;
/* Enqueue all buffers for the component to fill */
while ((buf = g_queue_pop_head (port->pending_buffers))) {
g_assert (!buf->used);
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
buf->omx_buf->nFlags = 0;
err = OMX_FillThisBuffer (comp->handle, buf->omx_buf);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to pass buffer %p to port %u: %s (0x%08x)", buf,
port->index, gst_omx_error_to_string (err), err);
goto error;
}
}
}
}
done:
GST_DEBUG_OBJECT (comp->parent, "Set port %u to %sflushing: %s (0x%08x)",
port->index, (flush ? "" : "not "), gst_omx_error_to_string (err), err);
g_mutex_unlock (port->port_lock);
return err;
error:
{
/* Need to unlock the port lock here because
* set_last_error() needs all port locks.
* This is safe here because we're just going
* to error out anyway */
g_mutex_unlock (port->port_lock);
gst_omx_component_set_last_error (comp, err);
g_mutex_lock (port->port_lock);
goto done;
}
}
gboolean
gst_omx_port_is_flushing (GstOMXPort * port)
{
GstOMXComponent *comp;
gboolean flushing;
g_return_val_if_fail (port != NULL, FALSE);
comp = port->comp;
g_mutex_lock (port->port_lock);
flushing = port->flushing;
g_mutex_unlock (port->port_lock);
GST_DEBUG_OBJECT (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)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
gint i, n;
g_assert (!port->buffers || port->buffers->len == 0);
comp = port->comp;
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
/* Update the port definition to check if we need more
* buffers after the port configuration was done and to
* update the buffer size
*/
gst_omx_component_get_parameter (comp, 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;
err = gst_omx_component_set_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
}
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to configure number of buffers of port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
goto error;
}
n = port->port_def.nBufferCountActual;
GST_DEBUG_OBJECT (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;
buf->settings_cookie = port->settings_cookie;
g_ptr_array_add (port->buffers, buf);
err =
OMX_AllocateBuffer (comp->handle, &buf->omx_buf, port->index, buf,
port->port_def.nBufferSize);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to allocate buffer for port %u: %s (0x%08x)", port->index,
gst_omx_error_to_string (err), err);
goto error;
}
g_assert (buf->omx_buf->pAppPrivate == buf);
/* In the beginning all buffers are not owned by the component */
g_queue_push_tail (port->pending_buffers, buf);
}
done:
GST_DEBUG_OBJECT (comp->parent, "Allocated buffers for port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
return err;
error:
{
/* Need to unlock the port lock here because
* set_last_error() needs all port locks.
* This is safe here because we're just going
* to error out anyway */
g_mutex_unlock (port->port_lock);
gst_omx_component_set_last_error (comp, err);
g_mutex_lock (port->port_lock);
goto done;
}
}
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)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
gint i, n;
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Deallocating buffers of port %u",
port->index);
if (!port->buffers) {
GST_DEBUG_OBJECT (comp->parent, "No buffers allocated for port %u",
port->index);
goto done;
}
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
/* We still try to deallocate all buffers */
}
/* We only allow deallocation of buffers after they
* were all released from the port, either by flushing
* the port or by disabling it.
*/
n = port->buffers->len;
for (i = 0; i < n; i++) {
GstOMXBuffer *buf = g_ptr_array_index (port->buffers, i);
OMX_ERRORTYPE tmp = OMX_ErrorNone;
if (buf->used)
GST_ERROR_OBJECT (comp->parent,
"Trying to free used buffer %p of port %u", buf, port->index);
/* 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) {
g_assert (buf == buf->omx_buf->pAppPrivate);
buf->omx_buf->pAppPrivate = NULL;
tmp = OMX_FreeBuffer (comp->handle, port->index, buf->omx_buf);
if (tmp != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to deallocate buffer %d of port %u: %s (0x%08x)", i,
port->index, gst_omx_error_to_string (tmp), tmp);
if (err == OMX_ErrorNone)
err = tmp;
}
}
g_slice_free (GstOMXBuffer, buf);
}
g_queue_clear (port->pending_buffers);
#if GLIB_CHECK_VERSION(2,22,0)
g_ptr_array_unref (port->buffers);
#else
g_ptr_array_free (port->buffers, TRUE);
#endif
port->buffers = NULL;
done:
GST_DEBUG_OBJECT (comp->parent, "Deallocated buffers of port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), 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)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
GTimeVal abstimeout, *timeval;
gboolean signalled;
OMX_ERRORTYPE last_error;
comp = port->comp;
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
GST_DEBUG_OBJECT (comp->parent, "Setting port %u to %s", port->index,
(enabled ? "enabled" : "disabled"));
/* Check if the port is already enabled/disabled first */
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
if (! !port->port_def.bEnabled == ! !enabled)
goto done;
port->enabled_changed = FALSE;
g_mutex_unlock (port->port_lock);
if (enabled)
err =
OMX_SendCommand (comp->handle, OMX_CommandPortEnable, port->index,
NULL);
else
err =
OMX_SendCommand (comp->handle, OMX_CommandPortDisable,
port->index, NULL);
g_mutex_lock (port->port_lock);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to send enable/disable command to port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
goto error;
}
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component in error state: %s (0x%08x)",
gst_omx_error_to_string (err), err);
goto done;
}
if (port->enabled_changed) {
GST_ERROR_OBJECT (comp->parent, "Port enabled/disabled in the meantime");
goto done;
}
g_get_current_time (&abstimeout);
g_time_val_add (&abstimeout, 5 * G_USEC_PER_SEC);
timeval = &abstimeout;
GST_DEBUG_OBJECT (comp->parent, "Waiting for 5s");
/* 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);
last_error = gst_omx_component_get_last_error (comp);
}
if (last_error != OMX_ErrorNone) {
err = last_error;
GST_ERROR_OBJECT (comp->parent,
"Got error while waiting for port %u to release all buffers: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
goto done;
} else if (!signalled) {
GST_ERROR_OBJECT (comp->parent,
"Timeout waiting for port %u to release all buffers", port->index);
err = OMX_ErrorTimeout;
goto error;
}
/* 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) {
goto error;
}
} else {
/* If deallocation fails this component can't really be used anymore */
if ((err =
gst_omx_port_deallocate_buffers_unlocked (port)) != OMX_ErrorNone) {
goto error;
}
}
/* And now wait until the enable/disable command is finished */
signalled = TRUE;
last_error = OMX_ErrorNone;
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
while (signalled && last_error == OMX_ErrorNone
&& (! !port->port_def.bEnabled != ! !enabled || !port->enabled_changed)) {
signalled = g_cond_timed_wait (port->port_cond, port->port_lock, timeval);
last_error = gst_omx_component_get_last_error (comp);
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
}
if (!signalled) {
GST_ERROR_OBJECT (comp->parent,
"Timeout waiting for port %u to be %s", port->index,
(enabled ? "enabled" : "disabled"));
err = OMX_ErrorTimeout;
goto error;
} else if (last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Got error while waiting for port %u to be %s: %s (0x%08x)",
port->index, (enabled ? "enabled" : "disabled"),
gst_omx_error_to_string (err), err);
err = last_error;
} else {
/* If everything went fine and we have an output port we
* should provide all newly allocated buffers to the port
*/
if (enabled && port->port_def.eDir == OMX_DirOutput) {
GstOMXBuffer *buf;
/* Enqueue all buffers for the component to fill */
while ((buf = g_queue_pop_head (port->pending_buffers))) {
g_assert (!buf->used);
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
buf->omx_buf->nFlags = 0;
err = OMX_FillThisBuffer (comp->handle, buf->omx_buf);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to pass buffer %p to port %u: %s (0x%08x)", buf,
port->index, gst_omx_error_to_string (err), err);
goto error;
}
}
}
}
done:
GST_DEBUG_OBJECT (comp->parent, "Port %u is %s%s: %s (0x%08x)", port->index,
(err == OMX_ErrorNone ? "" : "not "),
(enabled ? "enabled" : "disabled"), gst_omx_error_to_string (err), err);
return err;
error:
{
/* Need to unlock the port lock here because
* set_last_error() needs all port locks.
* This is safe here because we're just going
* to error out anyway */
g_mutex_unlock (port->port_lock);
gst_omx_component_set_last_error (comp, err);
g_mutex_lock (port->port_lock);
goto done;
}
}
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)
{
GstOMXComponent *comp;
gboolean enabled;
g_return_val_if_fail (port != NULL, FALSE);
comp = port->comp;
g_mutex_lock (port->port_lock);
gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
enabled = port->port_def.bEnabled;
g_mutex_unlock (port->port_lock);
GST_DEBUG_OBJECT (comp->parent, "Port %u is enabled: %d", port->index,
enabled);
return enabled;
}
OMX_ERRORTYPE
gst_omx_port_reconfigure (GstOMXPort * port)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Reconfiguring port %u", port->index);
g_mutex_lock (port->port_lock);
if (!port->settings_changed)
goto done;
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone)
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->configured_settings_cookie = port->settings_cookie;
/* If this is an output port, notify all input ports
* that might wait for us to reconfigure in
* acquire_buffer()
*/
if (port->port_def.eDir == OMX_DirOutput) {
GList *l;
g_mutex_lock (comp->state_lock);
for (l = comp->pending_reconfigure_outports; l; l = l->next) {
if (l->data == (gpointer) port) {
comp->pending_reconfigure_outports =
g_list_delete_link (comp->pending_reconfigure_outports, l);
break;
}
}
if (!comp->pending_reconfigure_outports) {
g_atomic_int_set (&comp->have_pending_reconfigure_outports, 0);
g_cond_broadcast (comp->state_cond);
}
g_mutex_unlock (comp->state_lock);
}
done:
GST_DEBUG_OBJECT (comp->parent, "Reconfigured port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
g_mutex_unlock (port->port_lock);
return err;
}
OMX_ERRORTYPE
gst_omx_port_manual_reconfigure (GstOMXPort * port, gboolean start)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Manual reconfigure of port %u %s",
port->index, (start ? "start" : "stsop"));
g_mutex_lock (port->port_lock);
if ((err = gst_omx_component_get_last_error (comp)) != OMX_ErrorNone)
goto done;
if (start)
port->settings_cookie++;
else
port->configured_settings_cookie = port->settings_cookie;
if (port->port_def.eDir == OMX_DirOutput) {
GList *l;
if (start) {
g_mutex_lock (comp->state_lock);
for (l = comp->pending_reconfigure_outports; l; l = l->next) {
if (l->data == (gpointer) port)
break;
}
if (!l) {
comp->pending_reconfigure_outports =
g_list_prepend (comp->pending_reconfigure_outports, port);
g_atomic_int_set (&comp->have_pending_reconfigure_outports, 1);
}
g_mutex_unlock (comp->state_lock);
} else {
g_mutex_lock (comp->state_lock);
for (l = comp->pending_reconfigure_outports; l; l = l->next) {
if (l->data == (gpointer) port) {
comp->pending_reconfigure_outports =
g_list_delete_link (comp->pending_reconfigure_outports, l);
break;
}
}
if (!comp->pending_reconfigure_outports) {
g_atomic_int_set (&comp->have_pending_reconfigure_outports, 0);
g_cond_broadcast (comp->state_cond);
}
g_mutex_unlock (comp->state_lock);
}
}
done:
g_mutex_unlock (port->port_lock);
GST_DEBUG_OBJECT (comp->parent, "Manual reconfigure of port %u: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
return err;
}
GQuark gst_omx_element_name_quark = 0;
static GType (*types[]) (void) = {
gst_omx_mpeg4_video_dec_get_type, gst_omx_h264_dec_get_type,
gst_omx_h263_dec_get_type, gst_omx_wmv_dec_get_type,
gst_omx_mpeg4_video_enc_get_type, gst_omx_h264_enc_get_type,
gst_omx_h263_enc_get_type, gst_omx_aac_enc_get_type};
static GKeyFile *config = NULL;
GKeyFile *
gst_omx_get_configuration (void)
{
return config;
}
const gchar *
gst_omx_error_to_string (OMX_ERRORTYPE err)
{
switch (err) {
case OMX_ErrorNone:
return "None";
case OMX_ErrorInsufficientResources:
return "Insufficient resources";
case OMX_ErrorUndefined:
return "Undefined";
case OMX_ErrorInvalidComponentName:
return "Invalid component name";
case OMX_ErrorComponentNotFound:
return "Component not found";
case OMX_ErrorInvalidComponent:
return "Invalid component";
case OMX_ErrorBadParameter:
return "Bad parameter";
case OMX_ErrorNotImplemented:
return "Not implemented";
case OMX_ErrorUnderflow:
return "Underflow";
case OMX_ErrorOverflow:
return "Overflow";
case OMX_ErrorHardware:
return "Hardware";
case OMX_ErrorInvalidState:
return "Invalid state";
case OMX_ErrorStreamCorrupt:
return "Stream corrupt";
case OMX_ErrorPortsNotCompatible:
return "Ports not compatible";
case OMX_ErrorResourcesLost:
return "Resources lost";
case OMX_ErrorNoMore:
return "No more";
case OMX_ErrorVersionMismatch:
return "Version mismatch";
case OMX_ErrorNotReady:
return "Not ready";
case OMX_ErrorTimeout:
return "Timeout";
case OMX_ErrorSameState:
return "Same state";
case OMX_ErrorResourcesPreempted:
return "Resources preempted";
case OMX_ErrorPortUnresponsiveDuringAllocation:
return "Port unresponsive during allocation";
case OMX_ErrorPortUnresponsiveDuringDeallocation:
return "Port unresponsive during deallocation";
case OMX_ErrorPortUnresponsiveDuringStop:
return "Port unresponsive during stop";
case OMX_ErrorIncorrectStateTransition:
return "Incorrect state transition";
case OMX_ErrorIncorrectStateOperation:
return "Incorrect state operation";
case OMX_ErrorUnsupportedSetting:
return "Unsupported setting";
case OMX_ErrorUnsupportedIndex:
return "Unsupported index";
case OMX_ErrorBadPortIndex:
return "Bad port index";
case OMX_ErrorPortUnpopulated:
return "Port unpopulated";
case OMX_ErrorComponentSuspended:
return "Component suspended";
case OMX_ErrorDynamicResourcesUnavailable:
return "Dynamic resources unavailable";
case OMX_ErrorMbErrorsInFrame:
return "Macroblock errors in frame";
case OMX_ErrorFormatNotDetected:
return "Format not detected";
case OMX_ErrorContentPipeOpenFailed:
return "Content pipe open failed";
case OMX_ErrorContentPipeCreationFailed:
return "Content pipe creation failed";
case OMX_ErrorSeperateTablesUsed:
return "Seperate tables used";
case OMX_ErrorTunnelingUnsupported:
return "Tunneling unsupported";
default:
if (err >= (guint32) OMX_ErrorKhronosExtensions
&& err < (guint32) OMX_ErrorVendorStartUnused) {
return "Khronos extension error";
} else if (err >= (guint32) OMX_ErrorVendorStartUnused
&& err < (guint32) OMX_ErrorMax) {
return "Vendor specific error";
} else {
return "Unknown error";
}
}
}
guint64
gst_omx_parse_hacks (gchar ** hacks)
{
guint64 hacks_flags = 0;
if (!hacks)
return 0;
while (*hacks) {
if (g_str_equal (*hacks,
"event-port-settings-changed-ndata-parameter-swap"))
hacks_flags |=
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_NDATA_PARAMETER_SWAP;
else if (g_str_equal (*hacks, "event-port-settings-changed-port-0-to-1"))
hacks_flags |= GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_PORT_0_TO_1;
else if (g_str_equal (*hacks, "video-framerate-integer"))
hacks_flags |= GST_OMX_HACK_VIDEO_FRAMERATE_INTEGER;
else if (g_str_equal (*hacks, "syncframe-flag-not-used"))
hacks_flags |= GST_OMX_HACK_SYNCFRAME_FLAG_NOT_USED;
else if (g_str_equal (*hacks, "no-component-reconfigure"))
hacks_flags |= GST_OMX_HACK_NO_COMPONENT_RECONFIGURE;
else
GST_WARNING ("Unknown hack: %s", *hacks);
hacks++;
}
return hacks_flags;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean ret = FALSE;
GError *err = NULL;
gchar **config_dirs;
gchar **elements;
gchar *env_config_dir;
const gchar *user_config_dir;
const gchar *const *system_config_dirs;
gint i, j;
gsize n_elements;
static const gchar *config_name[] = { "gstomx.conf", NULL };
static const gchar *env_config_name[] = { "GST_OMX_CONFIG_DIR", NULL };
GST_DEBUG_CATEGORY_INIT (gstomx_debug, "omx", 0, "gst-omx");
gst_omx_element_name_quark =
g_quark_from_static_string ("gst-omx-element-name");
/* Read configuration file gstomx.conf from the preferred
* configuration directories */
env_config_dir = g_strdup (g_getenv (*env_config_name));
user_config_dir = g_get_user_config_dir ();
system_config_dirs = g_get_system_config_dirs ();
config_dirs =
g_new (gchar *, g_strv_length ((gchar **) system_config_dirs) + 3);
i = 0;
j = 0;
if (env_config_dir)
config_dirs[i++] = (gchar *) env_config_dir;
config_dirs[i++] = (gchar *) user_config_dir;
while (system_config_dirs[j])
config_dirs[i++] = (gchar *) system_config_dirs[j++];
config_dirs[i++] = NULL;
gst_plugin_add_dependency (plugin, env_config_name,
(const gchar **) (config_dirs + (env_config_dir ? 1 : 0)), config_name,
GST_PLUGIN_DEPENDENCY_FLAG_NONE);
config = g_key_file_new ();
if (!g_key_file_load_from_dirs (config, *config_name,
(const gchar **) config_dirs, NULL, G_KEY_FILE_NONE, &err)) {
GST_ERROR ("Failed to load configuration file: %s", err->message);
g_error_free (err);
goto done;
}
/* Initialize all types */
for (i = 0; i < G_N_ELEMENTS (types); i++)
types[i] ();
elements = g_key_file_get_groups (config, &n_elements);
for (i = 0; i < n_elements; i++) {
GTypeQuery type_query;
GTypeInfo type_info = { 0, };
GType type, subtype;
gchar *type_name, *core_name, *component_name;
gint rank;
GST_DEBUG ("Registering element '%s'", elements[i]);
err = NULL;
if (!(type_name =
g_key_file_get_string (config, elements[i], "type-name", &err))) {
GST_ERROR
("Unable to read 'type-name' configuration for element '%s': %s",
elements[i], err->message);
g_error_free (err);
continue;
}
type = g_type_from_name (type_name);
if (type == G_TYPE_INVALID) {
GST_ERROR ("Invalid type name '%s' for element '%s'", type_name,
elements[i]);
g_free (type_name);
continue;
}
if (!g_type_is_a (type, GST_TYPE_ELEMENT)) {
GST_ERROR ("Type '%s' is no GstElement subtype for element '%s'",
type_name, elements[i]);
g_free (type_name);
continue;
}
g_free (type_name);
/* And now some sanity checking */
err = NULL;
if (!(core_name =
g_key_file_get_string (config, elements[i], "core-name", &err))) {
GST_ERROR
("Unable to read 'core-name' configuration for element '%s': %s",
elements[i], err->message);
g_error_free (err);
continue;
}
if (!g_file_test (core_name, G_FILE_TEST_IS_REGULAR)) {
GST_ERROR ("Core '%s' does not exist for element '%s'", core_name,
elements[i]);
g_free (core_name);
continue;
}
g_free (core_name);
err = NULL;
if (!(component_name =
g_key_file_get_string (config, elements[i], "component-name",
&err))) {
GST_ERROR
("Unable to read 'component-name' configuration for element '%s': %s",
elements[i], err->message);
g_error_free (err);
continue;
}
g_free (component_name);
err = NULL;
rank = g_key_file_get_integer (config, elements[i], "rank", &err);
if (err != NULL) {
GST_ERROR ("No rank set for element '%s': %s", elements[i], err->message);
g_error_free (err);
continue;
}
/* And now register the type, all other configuration will
* be handled by the type itself */
g_type_query (type, &type_query);
memset (&type_info, 0, sizeof (type_info));
type_info.class_size = type_query.class_size;
type_info.instance_size = type_query.instance_size;
type_name = g_strdup_printf ("%s-%s", g_type_name (type), elements[i]);
if (g_type_from_name (type_name) != G_TYPE_INVALID) {
GST_ERROR ("Type '%s' already exists for element '%s'", type_name,
elements[i]);
g_free (type_name);
continue;
}
subtype = g_type_register_static (type, type_name, &type_info, 0);
g_free (type_name);
g_type_set_qdata (subtype, gst_omx_element_name_quark,
g_strdup (elements[i]));
ret |= gst_element_register (plugin, elements[i], rank, subtype);
}
g_strfreev (elements);
done:
g_free (env_config_dir);
g_free (config_dirs);
return ret;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"openmax",
"GStreamer OpenMAX Plug-ins",
plugin_init,
PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)