gstreamer/omx/gstomx.c
Guillaume Desmottes 27c1aa544e omx: don't log error when failing to load conf file with generic target
The generic target is meant to only test building gst-omx. It doesn't
provide any configuration file and so is not supposed to register any
element.
I'm not aware of any user building gst-omx with this target and
providing their own conf file to actually register elements. But best to
not break this use case anyway so let's just downgrade the log message.

Fix GST_ERROR in the 'check fedora' CI job.
2020-02-25 10:51:39 +05:30

4159 lines
129 KiB
C

/*
* Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
* Copyright (C) 2013, Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/allocators/gstdmabuf.h>
#include <string.h>
#include "gstomx.h"
#include "gstomxmjpegdec.h"
#include "gstomxmpeg2videodec.h"
#include "gstomxmpeg4videodec.h"
#include "gstomxh264dec.h"
#include "gstomxh263dec.h"
#include "gstomxh265dec.h"
#include "gstomxvp8dec.h"
#include "gstomxtheoradec.h"
#include "gstomxwmvdec.h"
#include "gstomxmpeg4videoenc.h"
#include "gstomxh264enc.h"
#include "gstomxh263enc.h"
#include "gstomxh265enc.h"
#include "gstomxaacdec.h"
#include "gstomxmp3dec.h"
#include "gstomxmp3enc.h"
#include "gstomxaacenc.h"
#include "gstomxamrdec.h"
#include "gstomxanalogaudiosink.h"
#include "gstomxhdmiaudiosink.h"
GST_DEBUG_CATEGORY (gstomx_debug);
#define GST_CAT_DEFAULT gstomx_debug
GST_DEBUG_CATEGORY_STATIC (OMX_API_TRACE);
/* Macros used to log result of OMX calls. Use the requested debug level if the
* operation succeeded and GST_LEVEL_ERROR if not.
* Don't consider OMX_ErrorNoMore as an error as it means we're done iterating. */
#define DEBUG_IF_OK(obj,err,...) \
GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, (err == OMX_ErrorNone || err == OMX_ErrorNoMore) ? GST_LEVEL_DEBUG : GST_LEVEL_ERROR, obj, __VA_ARGS__)
#define INFO_IF_OK(obj,err,...) \
GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, (err == OMX_ErrorNone || err == OMX_ErrorNoMore) ? GST_LEVEL_INFO : GST_LEVEL_ERROR, obj, __VA_ARGS__)
G_LOCK_DEFINE_STATIC (core_handles);
static GHashTable *core_handles;
/* Cache used by gst_omx_buffer_flags_to_string() */
G_LOCK_DEFINE_STATIC (buffer_flags_str);
static GHashTable *buffer_flags_str;
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);
g_mutex_init (&core->lock);
core->user_count = 0;
g_hash_table_insert (core_handles, g_strdup (filename), core);
/* Hack for the Broadcom OpenMAX IL implementation */
#ifdef USE_OMX_TARGET_RPI
{
#else
if (g_str_has_suffix (filename, "vc/lib/libopenmaxil.so")) {
#endif
gchar *bcm_host_filename;
gchar *bcm_host_path;
GModule *bcm_host_module;
void (*bcm_host_init) (void);
bcm_host_path = g_path_get_dirname (filename);
bcm_host_filename =
g_build_filename (bcm_host_path, "libbcm_host.so", NULL);
bcm_host_module =
g_module_open (bcm_host_filename,
G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
g_free (bcm_host_filename);
g_free (bcm_host_path);
if (!bcm_host_module) {
/* Retry without an absolute path */
bcm_host_module =
g_module_open ("libbcm_host.so",
G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
if (!bcm_host_module) {
GST_ERROR ("Failed to load libbcm_host.so");
goto error;
}
}
if (!g_module_symbol (bcm_host_module, "bcm_host_init",
(gpointer *) & bcm_host_init)) {
GST_ERROR ("Failed to load symbol 'bcm_host_init' from libbcm_host.so");
goto error;
}
bcm_host_init ();
}
core->module =
g_module_open (filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
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;
if (!g_module_symbol (core->module, "OMX_SetupTunnel",
(gpointer *) & core->setup_tunnel))
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_clear (&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_LOCK (buffer_flags_str);
g_clear_pointer (&buffer_flags_str, g_hash_table_unref);
G_UNLOCK (buffer_flags_str);
}
g_mutex_unlock (&core->lock);
G_UNLOCK (core_handles);
}
/* NOTE: comp->messages_lock will be used */
static void
gst_omx_component_flush_messages (GstOMXComponent * comp)
{
GstOMXMessage *msg;
g_mutex_lock (&comp->messages_lock);
while ((msg = g_queue_pop_head (&comp->messages))) {
g_slice_free (GstOMXMessage, msg);
}
g_mutex_unlock (&comp->messages_lock);
}
static void
gst_omx_buffer_reset (GstOMXBuffer * buf)
{
buf->omx_buf->nFlags = 0;
buf->omx_buf->nOffset = 0;
buf->omx_buf->nFilledLen = 0;
GST_OMX_SET_TICKS (buf->omx_buf->nTimeStamp, G_GUINT64_CONSTANT (0));
}
static void gst_omx_buffer_unmap (GstOMXBuffer * buffer);
/* NOTE: Call with comp->lock, comp->messages_lock will be used */
static void
gst_omx_component_handle_messages (GstOMXComponent * comp)
{
GstOMXMessage *msg;
g_mutex_lock (&comp->messages_lock);
while ((msg = g_queue_pop_head (&comp->messages))) {
g_mutex_unlock (&comp->messages_lock);
switch (msg->type) {
case GST_OMX_MESSAGE_STATE_SET:{
GST_INFO_OBJECT (comp->parent, "%s state change to %s finished",
comp->name, gst_omx_state_to_string (msg->content.state_set.state));
comp->state = msg->content.state_set.state;
if (comp->state == comp->pending_state)
comp->pending_state = OMX_StateInvalid;
break;
}
case GST_OMX_MESSAGE_FLUSH:{
GstOMXPort *port = NULL;
OMX_U32 index = msg->content.flush.port;
port = gst_omx_component_get_port (comp, index);
if (!port)
break;
GST_DEBUG_OBJECT (comp->parent, "%s port %u flushed", comp->name,
port->index);
if (port->flushing) {
port->flushed = TRUE;
} else {
GST_ERROR_OBJECT (comp->parent, "%s port %u was not flushing",
comp->name, port->index);
}
break;
}
case GST_OMX_MESSAGE_ERROR:{
OMX_ERRORTYPE error = msg->content.error.error;
if (error == OMX_ErrorNone)
break;
GST_ERROR_OBJECT (comp->parent, "%s got error: %s (0x%08x)", comp->name,
gst_omx_error_to_string (error), error);
/* We only set the first error ever from which
* we can't recover anymore.
*/
if (comp->last_error == OMX_ErrorNone)
comp->last_error = error;
g_cond_broadcast (&comp->messages_cond);
break;
}
case GST_OMX_MESSAGE_PORT_ENABLE:{
GstOMXPort *port = NULL;
OMX_U32 index = msg->content.port_enable.port;
OMX_BOOL enable = msg->content.port_enable.enable;
port = gst_omx_component_get_port (comp, index);
if (!port)
break;
GST_DEBUG_OBJECT (comp->parent, "%s port %u %s", comp->name,
port->index, (enable ? "enabled" : "disabled"));
if (enable)
port->enabled_pending = FALSE;
else
port->disabled_pending = FALSE;
break;
}
case GST_OMX_MESSAGE_PORT_SETTINGS_CHANGED:{
gint i, n;
OMX_U32 index = msg->content.port_settings_changed.port;
GList *outports = NULL, *l, *k;
GST_DEBUG_OBJECT (comp->parent, "%s settings changed (port %u)",
comp->name, (guint) index);
/* FIXME: This probably can be done better */
/* 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);
if (index == OMX_ALL || index == port->index) {
port->settings_cookie++;
gst_omx_port_update_port_definition (port, NULL);
if (port->port_def.eDir == OMX_DirOutput && !port->tunneled)
outports = g_list_prepend (outports, port);
}
}
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);
}
g_list_free (outports);
break;
}
case GST_OMX_MESSAGE_BUFFER_FLAG:{
GstOMXPort *port = NULL;
OMX_U32 index = msg->content.buffer_flag.port;
OMX_U32 flags = msg->content.buffer_flag.flags;
port = gst_omx_component_get_port (comp, index);
if (!port)
break;
GST_DEBUG_OBJECT (comp->parent,
"%s port %u got buffer flags 0x%08x (%s)", comp->name, port->index,
(guint) flags, gst_omx_buffer_flags_to_string (flags));
if ((flags & OMX_BUFFERFLAG_EOS)
&& port->port_def.eDir == OMX_DirOutput && !port->eos) {
GST_DEBUG_OBJECT (comp->parent, "%s port %u is EOS", comp->name,
port->index);
port->eos = TRUE;
}
break;
}
case GST_OMX_MESSAGE_BUFFER_DONE:{
GstOMXBuffer *buf = msg->content.buffer_done.buffer->pAppPrivate;
GstOMXPort *port;
port = buf->port;
buf->used = FALSE;
if (msg->content.buffer_done.empty) {
/* Input buffer is empty again and can be used to contain new input */
GST_LOG_OBJECT (port->comp->parent,
"%s port %u emptied buffer %p (%p)", port->comp->name,
port->index, buf, buf->omx_buf->pBuffer);
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
gst_omx_buffer_reset (buf);
/* Release and unmap the parent buffer, if any */
gst_omx_buffer_unmap (buf);
} else {
/* Output buffer contains output now or
* the port was flushed */
GST_LOG_OBJECT (port->comp->parent,
"%s port %u filled buffer %p (%p)", port->comp->name, port->index,
buf, buf->omx_buf->pBuffer);
if ((buf->omx_buf->nFlags & OMX_BUFFERFLAG_EOS)
&& port->port_def.eDir == OMX_DirOutput && !port->eos) {
GST_DEBUG_OBJECT (comp->parent, "%s port %u is EOS", comp->name,
port->index);
port->eos = TRUE;
}
}
/* If an input port is managed by a pool, the buffer will be ready to be
* filled again once it's been released to the pool. */
if (port->port_def.eDir == OMX_DirOutput || !port->using_pool) {
g_queue_push_tail (&port->pending_buffers, buf);
}
break;
}
default:{
g_assert_not_reached ();
break;
}
}
g_slice_free (GstOMXMessage, msg);
g_mutex_lock (&comp->messages_lock);
}
g_mutex_unlock (&comp->messages_lock);
}
/* NOTE: comp->messages_lock will be used */
static void
gst_omx_component_send_message (GstOMXComponent * comp, GstOMXMessage * msg)
{
g_mutex_lock (&comp->messages_lock);
if (msg)
g_queue_push_tail (&comp->messages, msg);
g_cond_broadcast (&comp->messages_cond);
g_mutex_unlock (&comp->messages_lock);
}
/* NOTE: Call with comp->lock, comp->messages_lock will be used */
static gboolean
gst_omx_component_wait_message (GstOMXComponent * comp, GstClockTime timeout)
{
gboolean signalled;
gint64 wait_until = -1;
if (timeout != GST_CLOCK_TIME_NONE) {
gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND);
if (add == 0)
return FALSE;
wait_until = g_get_monotonic_time () + add;
GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us",
comp->name, add);
} else {
GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name);
}
g_mutex_lock (&comp->messages_lock);
g_mutex_unlock (&comp->lock);
if (!g_queue_is_empty (&comp->messages)) {
signalled = TRUE;
} else if (timeout == GST_CLOCK_TIME_NONE) {
g_cond_wait (&comp->messages_cond, &comp->messages_lock);
signalled = TRUE;
} else {
signalled =
g_cond_wait_until (&comp->messages_cond, &comp->messages_lock,
wait_until);
}
g_mutex_unlock (&comp->messages_lock);
g_mutex_lock (&comp->lock);
return signalled;
}
static const gchar *
omx_event_type_to_str (OMX_EVENTTYPE event)
{
switch (event) {
case OMX_EventCmdComplete:
return "EventCmdComplete";
case OMX_EventError:
return "EventError";
case OMX_EventMark:
return "EventMark";
case OMX_EventPortSettingsChanged:
return "EventPortSettingsChanged";
case OMX_EventBufferFlag:
return "EventBufferFlag";
case OMX_EventResourcesAcquired:
return "EventResourcesAcquired";
case OMX_EventComponentResumed:
return "EventComponentResumed";
case OMX_EventDynamicResourcesAvailable:
return "EventDynamicResourcesAvailable";
case OMX_EventPortFormatDetected:
return "EventPortFormatDetected";
#ifdef OMX_EventIndexSettingChanged
case OMX_EventIndexSettingChanged:
return "EventIndexSettingChanged";
#endif
#ifdef OMX_EventPortNeedsDisable
case OMX_EventPortNeedsDisable:
return "EventPortNeedsDisable";
#endif
#ifdef OMX_EventPortNeedsFlush
case OMX_EventPortNeedsFlush:
return "EventPortNeedsFlush";
#endif
case OMX_EventKhronosExtensions:
case OMX_EventVendorStartUnused:
case OMX_EventMax:
default:
break;
}
return NULL;
}
/* See "Table 3-11: Event Parameter Usage" */
static GstStructure *
omx_event_to_debug_struct (OMX_EVENTTYPE event,
guint32 data1, guint32 data2, gpointer event_data)
{
const gchar *name;
name = omx_event_type_to_str (event);
switch (event) {
case OMX_EventCmdComplete:
{
const gchar *cmd = gst_omx_command_to_string (data1);
if (!cmd)
break;
switch (data1) {
case OMX_CommandStateSet:
return gst_structure_new (name,
"command", G_TYPE_STRING, cmd,
"state-reached", G_TYPE_STRING, gst_omx_state_to_string (data2),
NULL);
case OMX_CommandFlush:
case OMX_CommandPortDisable:
case OMX_CommandPortEnable:
case OMX_CommandMarkBuffer:
return gst_structure_new (name,
"command", G_TYPE_STRING, cmd, "port", G_TYPE_UINT, data2,
"error", G_TYPE_STRING,
gst_omx_error_to_string (GPOINTER_TO_UINT (event_data)), NULL);
case OMX_CommandKhronosExtensions:
case OMX_CommandVendorStartUnused:
case OMX_CommandMax:
break;
}
}
break;
case OMX_EventError:
return gst_structure_new (name, "error", G_TYPE_STRING,
gst_omx_error_to_string (data1), "extra-info", G_TYPE_STRING,
gst_omx_error_to_string (data2), NULL);
case OMX_EventMark:
case OMX_EventComponentResumed:
case OMX_EventResourcesAcquired:
case OMX_EventDynamicResourcesAvailable:
case OMX_EventPortFormatDetected:
return gst_structure_new_empty (name);
case OMX_EventPortSettingsChanged:
#ifdef OMX_EventIndexSettingChanged
case OMX_EventIndexSettingChanged:
#endif
#ifdef OMX_EventPortNeedsDisable
case OMX_EventPortNeedsDisable:
#endif
#ifdef OMX_EventPortNeedsFlush
case OMX_EventPortNeedsFlush:
#endif
return gst_structure_new (name, "port", G_TYPE_UINT,
data1, "param-config", G_TYPE_UINT, data2, NULL);
case OMX_EventBufferFlag:
return gst_structure_new (name, "port", G_TYPE_UINT,
data1, "flags", G_TYPE_STRING, gst_omx_buffer_flags_to_string (data2),
NULL);
case OMX_EventKhronosExtensions:
case OMX_EventVendorStartUnused:
case OMX_EventMax:
default:
break;
}
return NULL;
}
static void
log_omx_api_trace_event (GstOMXComponent * comp, OMX_EVENTTYPE event,
guint32 data1, guint32 data2, gpointer event_data)
{
#ifndef GST_DISABLE_GST_DEBUG
GstStructure *s;
/* Don't bother creating useless structs if not needed */
if (gst_debug_category_get_threshold (OMX_API_TRACE) < GST_LEVEL_DEBUG)
return;
s = omx_event_to_debug_struct (event, data1, data2, event_data);
if (!s) {
GST_CAT_WARNING_OBJECT (OMX_API_TRACE, comp->parent,
"invalid event 0x%08x Data1 %u Data2 %u EventData %p", event, data1,
data2, event_data);
return;
}
GST_CAT_DEBUG_OBJECT (OMX_API_TRACE, comp->parent, "%" GST_PTR_FORMAT, s);
gst_structure_free (s);
#endif /* GST_DISABLE_GST_DEBUG */
}
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;
log_omx_api_trace_event (comp, eEvent, nData1, nData2, pEventData);
switch (eEvent) {
case OMX_EventCmdComplete:
{
OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE) nData1;
GST_DEBUG_OBJECT (comp->parent, "%s %s command complete (%d)",
comp->name, gst_omx_command_to_string (cmd), cmd);
switch (cmd) {
case OMX_CommandStateSet:{
GstOMXMessage *msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_STATE_SET;
msg->content.state_set.state = nData2;
GST_DEBUG_OBJECT (comp->parent, "%s state change to %s finished",
comp->name,
gst_omx_state_to_string (msg->content.state_set.state));
gst_omx_component_send_message (comp, msg);
break;
}
case OMX_CommandFlush:{
GstOMXMessage *msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_FLUSH;
msg->content.flush.port = nData2;
GST_DEBUG_OBJECT (comp->parent, "%s port %u flushed", comp->name,
(guint) msg->content.flush.port);
gst_omx_component_send_message (comp, msg);
break;
}
case OMX_CommandPortEnable:
case OMX_CommandPortDisable:{
GstOMXMessage *msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_PORT_ENABLE;
msg->content.port_enable.port = nData2;
msg->content.port_enable.enable = (cmd == OMX_CommandPortEnable);
GST_DEBUG_OBJECT (comp->parent, "%s port %u %s", comp->name,
(guint) msg->content.port_enable.port,
(msg->content.port_enable.enable ? "enabled" : "disabled"));
gst_omx_component_send_message (comp, msg);
break;
}
default:
break;
}
break;
}
case OMX_EventError:
{
GstOMXMessage *msg;
OMX_ERRORTYPE error_type = nData1;
/* Yes, this really happens... */
if (error_type == OMX_ErrorNone)
break;
/* Always ignore PortUnpopulated error. This error is informational
* at best but it is useful for debugging some strange scenarios.
*/
if (error_type == OMX_ErrorPortUnpopulated) {
GST_DEBUG_OBJECT (comp->parent, "%s got error: %s (0x%08x)",
comp->name, gst_omx_error_to_string (error_type), error_type);
break;
}
msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_ERROR;
msg->content.error.error = error_type;
GST_ERROR_OBJECT (comp->parent, "%s got error: %s (0x%08x)", comp->name,
gst_omx_error_to_string (msg->content.error.error),
msg->content.error.error);
gst_omx_component_send_message (comp, msg);
break;
}
case OMX_EventPortSettingsChanged:
{
GstOMXMessage *msg = g_slice_new (GstOMXMessage);
OMX_U32 index;
if (!(comp->hacks &
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_NDATA_PARAMETER_SWAP)) {
index = nData1;
} else {
index = nData2;
}
if (index == 0
&& (comp->hacks &
GST_OMX_HACK_EVENT_PORT_SETTINGS_CHANGED_PORT_0_TO_1))
index = 1;
msg->type = GST_OMX_MESSAGE_PORT_SETTINGS_CHANGED;
msg->content.port_settings_changed.port = index;
GST_DEBUG_OBJECT (comp->parent, "%s settings changed (port index: %u)",
comp->name, (guint) msg->content.port_settings_changed.port);
gst_omx_component_send_message (comp, msg);
break;
}
case OMX_EventBufferFlag:{
GstOMXMessage *msg;
msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_BUFFER_FLAG;
msg->content.buffer_flag.port = nData1;
msg->content.buffer_flag.flags = nData2;
GST_DEBUG_OBJECT (comp->parent, "%s port %u got buffer flags 0x%08x (%s)",
comp->name, (guint) msg->content.buffer_flag.port,
(guint) msg->content.buffer_flag.flags,
gst_omx_buffer_flags_to_string (msg->content.buffer_flag.flags));
gst_omx_component_send_message (comp, msg);
break;
}
case OMX_EventPortFormatDetected:
default:
GST_DEBUG_OBJECT (comp->parent, "%s unknown event 0x%08x", comp->name,
eEvent);
break;
}
return OMX_ErrorNone;
}
static void
gst_omx_buffer_unmap (GstOMXBuffer * buffer)
{
g_return_if_fail (buffer != NULL);
if (buffer->input_frame_mapped) {
g_assert (!buffer->input_mem);
g_assert (!buffer->input_buffer);
g_assert (!buffer->input_buffer_mapped);
gst_video_frame_unmap (&buffer->input_frame);
buffer->input_frame_mapped = FALSE;
} else if (buffer->input_mem) {
g_assert (!buffer->input_buffer);
g_assert (!buffer->input_buffer_mapped);
gst_memory_unmap (buffer->input_mem, &buffer->map);
g_clear_pointer (&buffer->input_mem, gst_memory_unref);
} else if (buffer->input_buffer) {
if (buffer->input_buffer_mapped)
gst_buffer_unmap (buffer->input_buffer, &buffer->map);
buffer->input_buffer_mapped = FALSE;
g_clear_pointer (&buffer->input_buffer, gst_buffer_unref);
}
}
static void
log_omx_api_trace_buffer (GstOMXComponent * comp, const gchar * event,
GstOMXBuffer * buf)
{
#ifndef GST_DISABLE_GST_DEBUG
GstStructure *s;
/* Don't bother creating useless structs if not needed */
if (gst_debug_category_get_threshold (OMX_API_TRACE) < GST_LEVEL_TRACE)
return;
if (buf) {
gchar *buf_str, *omx_buf_str, *pbuffer_str;
/* GST_PTR_FORMAT won't serialize G_TYPE_POINTER fields so stringify pointers */
buf_str = g_strdup_printf ("%p", buf);
omx_buf_str = g_strdup_printf ("%p", buf->omx_buf);
pbuffer_str = g_strdup_printf ("%p", buf->omx_buf->pBuffer);
/* *INDENT-OFF* */
s = gst_structure_new (event,
"GstOMXBuffer", G_TYPE_STRING, buf_str,
"OMX-buffer", G_TYPE_STRING, omx_buf_str,
"pBuffer", G_TYPE_STRING, pbuffer_str,
"TimeStamp", G_TYPE_UINT64, GST_OMX_GET_TICKS (buf->omx_buf->nTimeStamp),
"AllocLen", G_TYPE_UINT, buf->omx_buf->nAllocLen,
"FilledLen", G_TYPE_UINT, buf->omx_buf->nFilledLen,
"flags", G_TYPE_UINT, buf->omx_buf->nFlags,
"flags-str", G_TYPE_STRING, gst_omx_buffer_flags_to_string (buf->omx_buf->nFlags),
NULL);
/* *INDENT-ON* */
g_free (buf_str);
g_free (omx_buf_str);
g_free (pbuffer_str);
} else {
s = gst_structure_new_empty (event);
}
GST_CAT_TRACE_OBJECT (OMX_API_TRACE, comp->parent, "%" GST_PTR_FORMAT, s);
gst_structure_free (s);
#endif /* GST_DISABLE_GST_DEBUG */
}
static OMX_ERRORTYPE
EmptyBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE * pBuffer)
{
GstOMXBuffer *buf;
GstOMXComponent *comp;
GstOMXMessage *msg;
buf = pBuffer->pAppPrivate;
if (!buf) {
GST_ERROR ("Have unknown or deallocated buffer %p", pBuffer);
return OMX_ErrorNone;
}
g_assert (buf->omx_buf == pBuffer);
if (buf->port->tunneled) {
GST_ERROR ("EmptyBufferDone on tunneled port");
return OMX_ErrorBadParameter;
}
comp = buf->port->comp;
msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_BUFFER_DONE;
msg->content.buffer_done.component = hComponent;
msg->content.buffer_done.app_data = pAppData;
msg->content.buffer_done.buffer = pBuffer;
msg->content.buffer_done.empty = OMX_TRUE;
log_omx_api_trace_buffer (comp, "EmptyBufferDone", buf);
GST_LOG_OBJECT (comp->parent, "%s port %u emptied buffer %p (%p)",
comp->name, buf->port->index, buf, buf->omx_buf->pBuffer);
gst_omx_component_send_message (comp, msg);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE
FillBufferDone (OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
OMX_BUFFERHEADERTYPE * pBuffer)
{
GstOMXBuffer *buf;
GstOMXComponent *comp;
GstOMXMessage *msg;
buf = pBuffer->pAppPrivate;
if (!buf) {
GST_ERROR ("Have unknown or deallocated buffer %p", pBuffer);
return OMX_ErrorNone;
}
g_assert (buf->omx_buf == pBuffer);
if (buf->port->tunneled) {
GST_ERROR ("FillBufferDone on tunneled port");
return OMX_ErrorBadParameter;
}
comp = buf->port->comp;
msg = g_slice_new (GstOMXMessage);
msg->type = GST_OMX_MESSAGE_BUFFER_DONE;
msg->content.buffer_done.component = hComponent;
msg->content.buffer_done.app_data = pAppData;
msg->content.buffer_done.buffer = pBuffer;
msg->content.buffer_done.empty = OMX_FALSE;
log_omx_api_trace_buffer (comp, "FillBufferDone", buf);
GST_LOG_OBJECT (comp->parent, "%s port %u filled buffer %p (%p)", comp->name,
buf->port->index, buf, buf->omx_buf->pBuffer);
gst_omx_component_send_message (comp, msg);
return OMX_ErrorNone;
}
static OMX_CALLBACKTYPE callbacks =
{ EventHandler, EmptyBufferDone, FillBufferDone };
GST_DEFINE_MINI_OBJECT_TYPE (GstOMXComponent, gst_omx_component);
static void gst_omx_component_free (GstOMXComponent * comp);
/* NOTE: Uses comp->lock and comp->messages_lock */
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;
const gchar *dot;
core = gst_omx_core_acquire (core_name);
if (!core)
return NULL;
comp = g_slice_new0 (GstOMXComponent);
comp->core = core;
gst_mini_object_init (GST_MINI_OBJECT_CAST (comp), 0,
gst_omx_component_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) gst_omx_component_free);
if ((dot = g_strrstr (component_name, ".")))
comp->name = g_strdup (dot + 1);
else
comp->name = g_strdup (component_name);
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_free (comp->name);
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;
g_mutex_init (&comp->lock);
g_mutex_init (&comp->messages_lock);
g_cond_init (&comp->messages_cond);
g_queue_init (&comp->messages);
comp->pending_state = OMX_StateInvalid;
comp->last_error = OMX_ErrorNone;
/* Set component role if any */
if (component_role && !(hacks & GST_OMX_HACK_NO_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);
DEBUG_IF_OK (comp->parent, err,
"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);
g_mutex_lock (&comp->lock);
gst_omx_component_handle_messages (comp);
g_mutex_unlock (&comp->lock);
return comp;
}
/* NOTE: Uses comp->messages_lock */
static void
gst_omx_component_free (GstOMXComponent * comp)
{
gint i, n;
g_return_if_fail (comp != NULL);
GST_INFO_OBJECT (comp->parent, "Unloading component %p %s", comp, comp->name);
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_assert (port->buffers == NULL);
g_assert (g_queue_get_length (&port->pending_buffers) == 0);
g_slice_free (GstOMXPort, port);
}
g_ptr_array_unref (comp->ports);
comp->ports = NULL;
}
comp->core->free_handle (comp->handle);
gst_omx_core_release (comp->core);
gst_omx_component_flush_messages (comp);
g_cond_clear (&comp->messages_cond);
g_mutex_clear (&comp->messages_lock);
g_mutex_clear (&comp->lock);
gst_object_unref (comp->parent);
g_free (comp->name);
comp->name = NULL;
g_slice_free (GstOMXComponent, comp);
}
GstOMXComponent *
gst_omx_component_ref (GstOMXComponent * comp)
{
g_return_val_if_fail (comp, NULL);
gst_mini_object_ref (GST_MINI_OBJECT_CAST (comp));
return comp;
}
void
gst_omx_component_unref (GstOMXComponent * comp)
{
g_return_if_fail (comp);
gst_mini_object_unref (GST_MINI_OBJECT_CAST (comp));
}
static GstStructure *
omx_command_to_debug_struct (OMX_COMMANDTYPE cmd,
guint32 param, gpointer cmd_data)
{
const gchar *cmd_str;
cmd_str = gst_omx_command_to_string (cmd);
switch (cmd) {
case OMX_CommandStateSet:
return gst_structure_new ("SendCommand",
"command", G_TYPE_STRING, cmd_str,
"state", G_TYPE_STRING, gst_omx_state_to_string (param), NULL);
case OMX_CommandFlush:
case OMX_CommandPortDisable:
case OMX_CommandPortEnable:
return gst_structure_new ("SendCommand",
"command", G_TYPE_STRING, cmd_str, "port", G_TYPE_UINT, param, NULL);
case OMX_CommandMarkBuffer:
return gst_structure_new ("SendCommand",
"command", G_TYPE_STRING, cmd_str,
"mark-type", G_TYPE_POINTER, cmd_data, NULL);
case OMX_CommandKhronosExtensions:
case OMX_CommandVendorStartUnused:
case OMX_CommandMax:
default:
break;
}
return NULL;
}
static void
log_omx_api_trace_send_command (GstOMXComponent * comp, OMX_COMMANDTYPE cmd,
guint32 param, gpointer cmd_data)
{
#ifndef GST_DISABLE_GST_DEBUG
GstStructure *s;
/* Don't bother creating useless structs if not needed */
if (gst_debug_category_get_threshold (OMX_API_TRACE) < GST_LEVEL_DEBUG)
return;
s = omx_command_to_debug_struct (cmd, param, cmd_data);
if (!s) {
GST_CAT_WARNING_OBJECT (OMX_API_TRACE, comp->parent,
"invalid command 0x%08x Param %u CmdData %p", cmd, param, cmd_data);
return;
}
GST_CAT_DEBUG_OBJECT (OMX_API_TRACE, comp->parent, "%" GST_PTR_FORMAT, s);
gst_structure_free (s);
#endif /* GST_DISABLE_GST_DEBUG */
}
static OMX_ERRORTYPE
gst_omx_component_send_command (GstOMXComponent * comp, OMX_COMMANDTYPE cmd,
guint32 param, gpointer cmd_data)
{
OMX_ERRORTYPE err;
log_omx_api_trace_send_command (comp, cmd, param, cmd_data);
err = OMX_SendCommand (comp->handle, cmd, param, cmd_data);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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->lock);
gst_omx_component_handle_messages (comp);
old_state = comp->state;
GST_INFO_OBJECT (comp->parent, "Setting %s state from %s to %s", comp->name,
gst_omx_state_to_string (old_state), gst_omx_state_to_string (state));
if ((err = comp->last_error) != OMX_ErrorNone && state > old_state) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (err), err);
goto done;
}
if (old_state == state || comp->pending_state == state) {
GST_DEBUG_OBJECT (comp->parent, "Component %s already in state %s",
comp->name, gst_omx_state_to_string (state));
goto done;
}
comp->pending_state = state;
/* Reset some things */
if ((old_state == OMX_StateExecuting || old_state == OMX_StatePause)
&& state < old_state) {
g_list_free (comp->pending_reconfigure_outports);
comp->pending_reconfigure_outports = NULL;
/* Notify all inports that are still waiting */
gst_omx_component_send_message (comp, NULL);
}
err = gst_omx_component_send_command (comp, OMX_CommandStateSet, state, NULL);
/* No need to check if anything has changed here */
done:
gst_omx_component_handle_messages (comp);
if (err != OMX_ErrorNone && comp->last_error == OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Last operation returned an error. Setting last_error manually.");
comp->last_error = err;
}
g_mutex_unlock (&comp->lock);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Error setting %s state from %s to %s: %s (0x%08x)", comp->name,
gst_omx_state_to_string (old_state), gst_omx_state_to_string (state),
gst_omx_error_to_string (err), err);
}
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_STATETYPE
gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout)
{
OMX_STATETYPE ret;
gboolean signalled = TRUE;
g_return_val_if_fail (comp != NULL, OMX_StateInvalid);
GST_DEBUG_OBJECT (comp->parent, "Getting state of %s", comp->name);
g_mutex_lock (&comp->lock);
gst_omx_component_handle_messages (comp);
if (comp->last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (comp->last_error),
comp->last_error);
ret = OMX_StateInvalid;
goto done;
}
ret = comp->state;
if (comp->pending_state == OMX_StateInvalid)
goto done;
while (signalled && comp->last_error == OMX_ErrorNone
&& comp->pending_state != OMX_StateInvalid) {
signalled = gst_omx_component_wait_message (comp, timeout);
if (signalled)
gst_omx_component_handle_messages (comp);
};
if (signalled) {
if (comp->last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"%s got error while waiting for state change: %s (0x%08x)",
comp->name, 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, "%s timeout while waiting for state "
"change", comp->name);
}
done:
g_mutex_unlock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "%s returning state %s", comp->name,
gst_omx_state_to_string (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, "%s adding port %u", comp->name, 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, "%s failed to add port %u: %s (0x%08x)",
comp->name, index, gst_omx_error_to_string (err), err);
return NULL;
}
port = g_slice_new0 (GstOMXPort);
port->comp = comp;
port->index = index;
port->tunneled = FALSE;
port->port_def = port_def;
g_queue_init (&port->pending_buffers);
port->flushing = TRUE;
port->flushed = FALSE;
port->enabled_pending = FALSE;
port->disabled_pending = FALSE;
port->eos = FALSE;
port->using_pool = 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;
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;
}
/* NOTE: Uses comp->lock and comp->messages_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->lock);
gst_omx_component_handle_messages (comp);
err = comp->last_error;
g_mutex_unlock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "Returning last %s error: %s (0x%08x)",
comp->name, 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));
}
#ifndef GST_DISABLE_GST_DEBUG
static const gchar *
omx_index_type_to_str (OMX_INDEXTYPE index)
{
switch (index) {
case OMX_IndexComponentStartUnused:
return "OMX_IndexComponentStartUnused";
case OMX_IndexParamPriorityMgmt:
return "OMX_IndexParamPriorityMgmt";
case OMX_IndexParamAudioInit:
return "OMX_IndexParamAudioInit";
case OMX_IndexParamImageInit:
return "OMX_IndexParamImageInit";
case OMX_IndexParamVideoInit:
return "OMX_IndexParamVideoInit";
case OMX_IndexParamOtherInit:
return "OMX_IndexParamOtherInit";
case OMX_IndexParamNumAvailableStreams:
return "OMX_IndexParamNumAvailableStreams";
case OMX_IndexParamActiveStream:
return "OMX_IndexParamActiveStream";
case OMX_IndexParamSuspensionPolicy:
return "OMX_IndexParamSuspensionPolicy";
case OMX_IndexParamComponentSuspended:
return "OMX_IndexParamComponentSuspended";
case OMX_IndexConfigCapturing:
return "OMX_IndexConfigCapturing";
case OMX_IndexConfigCaptureMode:
return "OMX_IndexConfigCaptureMode";
case OMX_IndexAutoPauseAfterCapture:
return "OMX_IndexAutoPauseAfterCapture";
case OMX_IndexParamContentURI:
return "OMX_IndexParamContentURI";
case OMX_IndexParamDisableResourceConcealment:
return "OMX_IndexParamDisableResourceConcealment";
case OMX_IndexConfigMetadataItemCount:
return "OMX_IndexConfigMetadataItemCount";
case OMX_IndexConfigContainerNodeCount:
return "OMX_IndexConfigContainerNodeCount";
case OMX_IndexConfigMetadataItem:
return "OMX_IndexConfigMetadataItem";
case OMX_IndexConfigCounterNodeID:
return "OMX_IndexConfigCounterNodeID";
case OMX_IndexParamMetadataFilterType:
return "OMX_IndexParamMetadataFilterType";
case OMX_IndexParamMetadataKeyFilter:
return "OMX_IndexParamMetadataKeyFilter";
case OMX_IndexConfigPriorityMgmt:
return "OMX_IndexConfigPriorityMgmt";
case OMX_IndexParamStandardComponentRole:
return "OMX_IndexParamStandardComponentRole";
case OMX_IndexPortStartUnused:
return "OMX_IndexPortStartUnused";
case OMX_IndexParamPortDefinition:
return "OMX_IndexParamPortDefinition";
case OMX_IndexParamCompBufferSupplier:
return "OMX_IndexParamCompBufferSupplier";
case OMX_IndexReservedStartUnused:
return "OMX_IndexReservedStartUnused";
case OMX_IndexAudioStartUnused:
return "OMX_IndexAudioStartUnused";
case OMX_IndexParamAudioPortFormat:
return "OMX_IndexParamAudioPortFormat";
case OMX_IndexParamAudioPcm:
return "OMX_IndexParamAudioPcm";
case OMX_IndexParamAudioAac:
return "OMX_IndexParamAudioAac";
case OMX_IndexParamAudioRa:
return "OMX_IndexParamAudioRa";
case OMX_IndexParamAudioMp3:
return "OMX_IndexParamAudioMp3";
case OMX_IndexParamAudioAdpcm:
return "OMX_IndexParamAudioAdpcm";
case OMX_IndexParamAudioG723:
return "OMX_IndexParamAudioG723";
case OMX_IndexParamAudioG729:
return "OMX_IndexParamAudioG729";
case OMX_IndexParamAudioAmr:
return "OMX_IndexParamAudioAmr";
case OMX_IndexParamAudioWma:
return "OMX_IndexParamAudioWma";
case OMX_IndexParamAudioSbc:
return "OMX_IndexParamAudioSbc";
case OMX_IndexParamAudioMidi:
return "OMX_IndexParamAudioMidi";
case OMX_IndexParamAudioGsm_FR:
return "OMX_IndexParamAudioGsm_FR";
case OMX_IndexParamAudioMidiLoadUserSound:
return "OMX_IndexParamAudioMidiLoadUserSound";
case OMX_IndexParamAudioG726:
return "OMX_IndexParamAudioG726";
case OMX_IndexParamAudioGsm_EFR:
return "OMX_IndexParamAudioGsm_EFR";
case OMX_IndexParamAudioGsm_HR:
return "OMX_IndexParamAudioGsm_HR";
case OMX_IndexParamAudioPdc_FR:
return "OMX_IndexParamAudioPdc_FR";
case OMX_IndexParamAudioPdc_EFR:
return "OMX_IndexParamAudioPdc_EFR";
case OMX_IndexParamAudioPdc_HR:
return "OMX_IndexParamAudioPdc_HR";
case OMX_IndexParamAudioTdma_FR:
return "OMX_IndexParamAudioTdma_FR";
case OMX_IndexParamAudioTdma_EFR:
return "OMX_IndexParamAudioTdma_EFR";
case OMX_IndexParamAudioQcelp8:
return "OMX_IndexParamAudioQcelp8";
case OMX_IndexParamAudioQcelp13:
return "OMX_IndexParamAudioQcelp13";
case OMX_IndexParamAudioEvrc:
return "OMX_IndexParamAudioEvrc";
case OMX_IndexParamAudioSmv:
return "OMX_IndexParamAudioSmv";
case OMX_IndexParamAudioVorbis:
return "OMX_IndexParamAudioVorbis";
case OMX_IndexConfigAudioMidiImmediateEvent:
return "OMX_IndexConfigAudioMidiImmediateEvent";
case OMX_IndexConfigAudioMidiControl:
return "OMX_IndexConfigAudioMidiControl";
case OMX_IndexConfigAudioMidiSoundBankProgram:
return "OMX_IndexConfigAudioMidiSoundBankProgram";
case OMX_IndexConfigAudioMidiStatus:
return "OMX_IndexConfigAudioMidiStatus";
case OMX_IndexConfigAudioMidiMetaEvent:
return "OMX_IndexConfigAudioMidiMetaEvent";
case OMX_IndexConfigAudioMidiMetaEventData:
return "OMX_IndexConfigAudioMidiMetaEventData";
case OMX_IndexConfigAudioVolume:
return "OMX_IndexConfigAudioVolume";
case OMX_IndexConfigAudioBalance:
return "OMX_IndexConfigAudioBalance";
case OMX_IndexConfigAudioChannelMute:
return "OMX_IndexConfigAudioChannelMute";
case OMX_IndexConfigAudioMute:
return "OMX_IndexConfigAudioMute";
case OMX_IndexConfigAudioLoudness:
return "OMX_IndexConfigAudioLoudness";
case OMX_IndexConfigAudioEchoCancelation:
return "OMX_IndexConfigAudioEchoCancelation";
case OMX_IndexConfigAudioNoiseReduction:
return "OMX_IndexConfigAudioNoiseReduction";
case OMX_IndexConfigAudioBass:
return "OMX_IndexConfigAudioBass";
case OMX_IndexConfigAudioTreble:
return "OMX_IndexConfigAudioTreble";
case OMX_IndexConfigAudioStereoWidening:
return "OMX_IndexConfigAudioStereoWidening";
case OMX_IndexConfigAudioChorus:
return "OMX_IndexConfigAudioChorus";
case OMX_IndexConfigAudioEqualizer:
return "OMX_IndexConfigAudioEqualizer";
case OMX_IndexConfigAudioReverberation:
return "OMX_IndexConfigAudioReverberation";
case OMX_IndexConfigAudioChannelVolume:
return "OMX_IndexConfigAudioChannelVolume";
case OMX_IndexImageStartUnused:
return "OMX_IndexImageStartUnused";
case OMX_IndexParamImagePortFormat:
return "OMX_IndexParamImagePortFormat";
case OMX_IndexParamFlashControl:
return "OMX_IndexParamFlashControl";
case OMX_IndexConfigFocusControl:
return "OMX_IndexConfigFocusControl";
case OMX_IndexParamQFactor:
return "OMX_IndexParamQFactor";
case OMX_IndexParamQuantizationTable:
return "OMX_IndexParamQuantizationTable";
case OMX_IndexParamHuffmanTable:
return "OMX_IndexParamHuffmanTable";
case OMX_IndexConfigFlashControl:
return "OMX_IndexConfigFlashControl";
case OMX_IndexVideoStartUnused:
return "OMX_IndexVideoStartUnused";
case OMX_IndexParamVideoPortFormat:
return "OMX_IndexParamVideoPortFormat";
case OMX_IndexParamVideoQuantization:
return "OMX_IndexParamVideoQuantization";
case OMX_IndexParamVideoFastUpdate:
return "OMX_IndexParamVideoFastUpdate";
case OMX_IndexParamVideoBitrate:
return "OMX_IndexParamVideoBitrate";
case OMX_IndexParamVideoMotionVector:
return "OMX_IndexParamVideoMotionVector";
case OMX_IndexParamVideoIntraRefresh:
return "OMX_IndexParamVideoIntraRefresh";
case OMX_IndexParamVideoErrorCorrection:
return "OMX_IndexParamVideoErrorCorrection";
case OMX_IndexParamVideoVBSMC:
return "OMX_IndexParamVideoVBSMC";
case OMX_IndexParamVideoMpeg2:
return "OMX_IndexParamVideoMpeg2";
case OMX_IndexParamVideoMpeg4:
return "OMX_IndexParamVideoMpeg4";
case OMX_IndexParamVideoWmv:
return "OMX_IndexParamVideoWmv";
case OMX_IndexParamVideoRv:
return "OMX_IndexParamVideoRv";
case OMX_IndexParamVideoAvc:
return "OMX_IndexParamVideoAvc";
case OMX_IndexParamVideoH263:
return "OMX_IndexParamVideoH263";
case OMX_IndexParamVideoProfileLevelQuerySupported:
return "OMX_IndexParamVideoProfileLevelQuerySupported";
case OMX_IndexParamVideoProfileLevelCurrent:
return "OMX_IndexParamVideoProfileLevelCurrent";
case OMX_IndexConfigVideoBitrate:
return "OMX_IndexConfigVideoBitrate";
case OMX_IndexConfigVideoFramerate:
return "OMX_IndexConfigVideoFramerate";
case OMX_IndexConfigVideoIntraVOPRefresh:
return "OMX_IndexConfigVideoIntraVOPRefresh";
case OMX_IndexConfigVideoIntraMBRefresh:
return "OMX_IndexConfigVideoIntraMBRefresh";
case OMX_IndexConfigVideoMBErrorReporting:
return "OMX_IndexConfigVideoMBErrorReporting";
case OMX_IndexParamVideoMacroblocksPerFrame:
return "OMX_IndexParamVideoMacroblocksPerFrame";
case OMX_IndexConfigVideoMacroBlockErrorMap:
return "OMX_IndexConfigVideoMacroBlockErrorMap";
case OMX_IndexParamVideoSliceFMO:
return "OMX_IndexParamVideoSliceFMO";
case OMX_IndexConfigVideoAVCIntraPeriod:
return "OMX_IndexConfigVideoAVCIntraPeriod";
case OMX_IndexConfigVideoNalSize:
return "OMX_IndexConfigVideoNalSize";
case OMX_IndexCommonStartUnused:
return "OMX_IndexCommonStartUnused";
case OMX_IndexParamCommonDeblocking:
return "OMX_IndexParamCommonDeblocking";
case OMX_IndexParamCommonSensorMode:
return "OMX_IndexParamCommonSensorMode";
case OMX_IndexParamCommonInterleave:
return "OMX_IndexParamCommonInterleave";
case OMX_IndexConfigCommonColorFormatConversion:
return "OMX_IndexConfigCommonColorFormatConversion";
case OMX_IndexConfigCommonScale:
return "OMX_IndexConfigCommonScale";
case OMX_IndexConfigCommonImageFilter:
return "OMX_IndexConfigCommonImageFilter";
case OMX_IndexConfigCommonColorEnhancement:
return "OMX_IndexConfigCommonColorEnhancement";
case OMX_IndexConfigCommonColorKey:
return "OMX_IndexConfigCommonColorKey";
case OMX_IndexConfigCommonColorBlend:
return "OMX_IndexConfigCommonColorBlend";
case OMX_IndexConfigCommonFrameStabilisation:
return "OMX_IndexConfigCommonFrameStabilisation";
case OMX_IndexConfigCommonRotate:
return "OMX_IndexConfigCommonRotate";
case OMX_IndexConfigCommonMirror:
return "OMX_IndexConfigCommonMirror";
case OMX_IndexConfigCommonOutputPosition:
return "OMX_IndexConfigCommonOutputPosition";
case OMX_IndexConfigCommonInputCrop:
return "OMX_IndexConfigCommonInputCrop";
case OMX_IndexConfigCommonOutputCrop:
return "OMX_IndexConfigCommonOutputCrop";
case OMX_IndexConfigCommonDigitalZoom:
return "OMX_IndexConfigCommonDigitalZoom";
case OMX_IndexConfigCommonOpticalZoom:
return "OMX_IndexConfigCommonOpticalZoom";
case OMX_IndexConfigCommonWhiteBalance:
return "OMX_IndexConfigCommonWhiteBalance";
case OMX_IndexConfigCommonExposure:
return "OMX_IndexConfigCommonExposure";
case OMX_IndexConfigCommonContrast:
return "OMX_IndexConfigCommonContrast";
case OMX_IndexConfigCommonBrightness:
return "OMX_IndexConfigCommonBrightness";
case OMX_IndexConfigCommonBacklight:
return "OMX_IndexConfigCommonBacklight";
case OMX_IndexConfigCommonGamma:
return "OMX_IndexConfigCommonGamma";
case OMX_IndexConfigCommonSaturation:
return "OMX_IndexConfigCommonSaturation";
case OMX_IndexConfigCommonLightness:
return "OMX_IndexConfigCommonLightness";
case OMX_IndexConfigCommonExclusionRect:
return "OMX_IndexConfigCommonExclusionRect";
case OMX_IndexConfigCommonDithering:
return "OMX_IndexConfigCommonDithering";
case OMX_IndexConfigCommonPlaneBlend:
return "OMX_IndexConfigCommonPlaneBlend";
case OMX_IndexConfigCommonExposureValue:
return "OMX_IndexConfigCommonExposureValue";
case OMX_IndexConfigCommonOutputSize:
return "OMX_IndexConfigCommonOutputSize";
case OMX_IndexParamCommonExtraQuantData:
return "OMX_IndexParamCommonExtraQuantData";
case OMX_IndexConfigCommonTransitionEffect:
return "OMX_IndexConfigCommonTransitionEffect";
case OMX_IndexOtherStartUnused:
return "OMX_IndexOtherStartUnused";
case OMX_IndexParamOtherPortFormat:
return "OMX_IndexParamOtherPortFormat";
case OMX_IndexConfigOtherPower:
return "OMX_IndexConfigOtherPower";
case OMX_IndexConfigOtherStats:
return "OMX_IndexConfigOtherStats";
case OMX_IndexTimeStartUnused:
return "OMX_IndexTimeStartUnused";
case OMX_IndexConfigTimeScale:
return "OMX_IndexConfigTimeScale";
case OMX_IndexConfigTimeClockState:
return "OMX_IndexConfigTimeClockState";
case OMX_IndexConfigTimeCurrentMediaTime:
return "OMX_IndexConfigTimeCurrentMediaTime";
case OMX_IndexConfigTimeCurrentWallTime:
return "OMX_IndexConfigTimeCurrentWallTime";
case OMX_IndexConfigTimeMediaTimeRequest:
return "OMX_IndexConfigTimeMediaTimeRequest";
case OMX_IndexConfigTimeClientStartTime:
return "OMX_IndexConfigTimeClientStartTime";
case OMX_IndexConfigTimePosition:
return "OMX_IndexConfigTimePosition";
case OMX_IndexConfigTimeSeekMode:
return "OMX_IndexConfigTimeSeekMode";
case OMX_IndexKhronosExtensions:
return "OMX_IndexKhronosExtensions";
case OMX_IndexVendorStartUnused:
return "OMX_IndexVendorStartUnused";
case OMX_IndexMax:
return "OMX_IndexMax";
default:
break;
}
#if OMX_VERSION_MINOR == 1
switch (index) {
case OMX_IndexParamCustomContentPipe:
return "OMX_IndexParamCustomContentPipe";
case OMX_IndexConfigCommonFocusRegion:
return "OMX_IndexConfigCommonFocusRegion";
case OMX_IndexConfigCommonFocusStatus:
return "OMX_IndexConfigCommonFocusStatus";
case OMX_IndexConfigTimeActiveRefClock:
return "OMX_IndexConfigTimeActiveRefClock";
case OMX_IndexConfigTimeCurrentAudioReference:
return "OMX_IndexConfigTimeCurrentAudioReference";
case OMX_IndexConfigTimeCurrentVideoReference:
return "OMX_IndexConfigTimeCurrentVideoReference";
default:
break;
}
#endif
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
switch ((OMX_ALG_INDEXTYPE) index) {
case OMX_ALG_IndexVendorComponentStartUnused:
return "OMX_ALG_IndexVendorComponentStartUnused";
case OMX_ALG_IndexParamReportedLatency:
return "OMX_ALG_IndexParamReportedLatency";
case OMX_ALG_IndexParamPreallocation:
return "OMX_ALG_IndexParamPreallocation";
case OMX_ALG_IndexVendorPortStartUnused:
return "OMX_ALG_IndexVendorPortStartUnused";
case OMX_ALG_IndexPortParamBufferMode:
return "OMX_ALG_IndexPortParamBufferMode";
case OMX_ALG_IndexParamVendorVideoStartUnused:
return "OMX_ALG_IndexParamVendorVideoStartUnused";
case OMX_ALG_IndexParamVideoHevc:
return "OMX_ALG_IndexParamVideoHevc";
case OMX_ALG_IndexParamVideoVp9:
return "OMX_ALG_IndexParamVideoVp9";
case OMX_ALG_IndexParamVideoGopControl:
return "OMX_ALG_IndexParamVideoGopControl";
case OMX_ALG_IndexParamVideoSlices:
return "OMX_ALG_IndexParamVideoSlices";
case OMX_ALG_IndexParamVideoSceneChangeResilience:
return "OMX_ALG_IndexParamVideoSceneChangeResilience";
case OMX_ALG_IndexParamVideoPrefetchBuffer:
return "OMX_ALG_IndexParamVideoPrefetchBuffer";
case OMX_ALG_IndexParamVideoCodedPictureBuffer:
return "OMX_ALG_IndexParamVideoCodedPictureBuffer";
case OMX_ALG_IndexParamVideoQuantizationControl:
return "OMX_ALG_IndexParamVideoQuantizationControl";
case OMX_ALG_IndexParamVideoQuantizationExtension:
return "OMX_ALG_IndexParamVideoQuantizationExtension";
case OMX_ALG_IndexParamVideoScalingList:
return "OMX_ALG_IndexParamVideoScalingList";
case OMX_ALG_IndexParamVideoDecodedPictureBuffer:
return "OMX_ALG_IndexParamVideoDecodedPictureBuffer";
case OMX_ALG_IndexParamVideoInternalEntropyBuffers:
return "OMX_ALG_IndexParamVideoInternalEntropyBuffers";
case OMX_ALG_IndexParamVideoLowBandwidth:
return "OMX_ALG_IndexParamVideoLowBandwidth";
case OMX_ALG_IndexParamVideoAspectRatio:
return "OMX_ALG_IndexParamVideoAspectRatio";
case OMX_ALG_IndexParamVideoSubframe:
return "OMX_ALG_IndexParamVideoSubframe";
case OMX_ALG_IndexParamVideoInstantaneousDecodingRefresh:
return "OMX_ALG_IndexParamVideoInstantaneousDecodingRefresh";
case OMX_ALG_IndexParamVideoMaxBitrate:
return "OMX_ALG_IndexParamVideoMaxBitrate";
case OMX_ALG_IndexParamVideoFillerData:
return "OMX_ALG_IndexParamVideoFillerData";
case OMX_ALG_IndexParamVideoBufferMode:
return "OMX_ALG_IndexParamVideoBufferMode";
case OMX_ALG_IndexParamVideoInterlaceFormatCurrent:
return "OMX_ALG_IndexParamVideoInterlaceFormatCurrent";
case OMX_ALG_IndexParamVideoLongTerm:
return "OMX_ALG_IndexParamVideoLongTerm";
case OMX_ALG_IndexParamVideoLookAhead:
return "OMX_ALG_IndexParamVideoLookAhead";
case OMX_ALG_IndexConfigVendorVideoStartUnused:
return "OMX_ALG_IndexConfigVendorVideoStartUnused";
case OMX_ALG_IndexConfigVideoInsertInstantaneousDecodingRefresh:
return "OMX_ALG_IndexConfigVideoInsertInstantaneousDecodingRefresh";
case OMX_ALG_IndexConfigVideoGroupOfPictures:
return "OMX_ALG_IndexConfigVideoGroupOfPictures";
case OMX_ALG_IndexConfigVideoRegionOfInterest:
return "OMX_ALG_IndexConfigVideoRegionOfInterest";
case OMX_ALG_IndexConfigVideoNotifySceneChange:
return "OMX_ALG_IndexConfigVideoNotifySceneChange";
case OMX_ALG_IndexConfigVideoInsertLongTerm:
return "OMX_ALG_IndexConfigVideoInsertLongTerm";
case OMX_ALG_IndexConfigVideoUseLongTerm:
return "OMX_ALG_IndexConfigVideoUseLongTerm";
case OMX_ALG_IndexVendorCommonStartUnused:
return "OMX_ALG_IndexVendorCommonStartUnused";
case OMX_ALG_IndexParamCommonSequencePictureModeCurrent:
return "OMX_ALG_IndexParamCommonSequencePictureModeCurrent";
case OMX_ALG_IndexParamCommonSequencePictureModeQuerySupported:
return "OMX_ALG_IndexParamCommonSequencePictureModeQuerySupported";
case OMX_ALG_IndexParamVideoTwoPass:
return "OMX_ALG_IndexParamVideoTwoPass";
case OMX_ALG_IndexParamVideoColorPrimaries:
return "OMX_ALG_IndexParamVideoColorPrimaries";
case OMX_ALG_IndexParamVideoSkipFrame:
return "OMX_ALG_IndexParamVideoSkipFrame";
case OMX_ALG_IndexConfigVideoNotifyResolutionChange:
return "OMX_ALG_IndexConfigVideoNotifyResolutionChange";
case OMX_ALG_IndexConfigVideoInsertPrefixSEI:
return "OMX_ALG_IndexConfigVideoInsertPrefixSEI";
case OMX_ALG_IndexConfigVideoInsertSuffixSEI:
return "OMX_ALG_IndexConfigVideoInsertSuffixSEI";
case OMX_ALG_IndexConfigVideoQuantizationParameterTable:
return "OMX_ALG_IndexConfigVideoQuantizationParameterTable";
case OMX_ALG_IndexParamVideoTransferCharacteristics:
return "OMX_ALG_IndexParamVideoTransferCharacteristics";
case OMX_ALG_IndexParamVideoColorMatrix:
return "OMX_ALG_IndexParamVideoColorMatrix";
case OMX_ALG_IndexParamVideoInputParsed:
return "OMX_ALG_IndexParamVideoInputParsed";
case OMX_ALG_IndexParamVideoMaxPictureSize:
return "OMX_ALG_IndexParamVideoMaxPictureSize";
case OMX_ALG_IndexParamVideoMaxPictureSizes:
return "OMX_ALG_IndexParamVideoMaxPictureSizes";
case OMX_ALG_IndexParamVideoLoopFilterBeta:
return "OMX_ALG_IndexParamVideoLoopFilterBeta";
case OMX_ALG_IndexParamVideoLoopFilterTc:
return "OMX_ALG_IndexParamVideoLoopFilterTc";
case OMX_ALG_IndexParamVideoQuantizationTable:
return "OMX_ALG_IndexParamVideoQuantizationTable";
case OMX_ALG_IndexConfigVideoTransferCharacteristics:
return "OMX_ALG_IndexConfigVideoTransferCharacteristics";
case OMX_ALG_IndexConfigVideoColorMatrix:
return "OMX_ALG_IndexConfigVideoColorMatrix";
case OMX_ALG_IndexConfigVideoLoopFilterBeta:
return "OMX_ALG_IndexConfigVideoLoopFilterBeta";
case OMX_ALG_IndexConfigVideoLoopFilterTc:
return "OMX_ALG_IndexConfigVideoLoopFilterTc";
case OMX_ALG_IndexConfigVideoHighDynamicRangeSEIs:
return "OMX_ALG_IndexConfigVideoHighDynamicRangeSEIs";
case OMX_ALG_IndexConfigVideoMaxResolutionChange:
return "OMX_ALG_IndexConfigVideoMaxResolutionChange";
case OMX_ALG_IndexPortParamEarlyCallback:
return "OMX_ALG_IndexPortParamEarlyCallback";
case OMX_ALG_IndexMaxEnum:
return "OMX_ALG_IndexMaxEnum";
}
#endif
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
/* Not part of the enum in OMX_IndexAlg.h */
if (index == OMX_ALG_IndexParamVideoInterlaceFormatSupported)
return "OMX_ALG_IndexParamVideoInterlaceFormatSupported";
#endif
return NULL;
}
#endif /* GST_DISABLE_GST_DEBUG */
static void
log_omx_api_trace_call (GstOMXComponent * comp, const gchar * function,
OMX_INDEXTYPE index, GstDebugLevel level)
{
#ifndef GST_DISABLE_GST_DEBUG
GstStructure *s;
const gchar *index_name;
/* Don't bother creating useless structs if not needed */
if (gst_debug_category_get_threshold (OMX_API_TRACE) < level)
return;
index_name = omx_index_type_to_str (index);
if (!index_name) {
GST_CAT_WARNING_OBJECT (OMX_API_TRACE, comp->parent,
"unknown call of %s with index 0x%08x", function, index);
return;
}
s = gst_structure_new (function, "index", G_TYPE_STRING, index_name, NULL);
GST_CAT_LEVEL_LOG (OMX_API_TRACE, level, comp->parent, "%" GST_PTR_FORMAT, s);
gst_structure_free (s);
#endif /* GST_DISABLE_GST_DEBUG */
}
/* comp->lock must be unlocked while calling this */
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 %s parameter at index 0x%08x",
comp->name, index);
log_omx_api_trace_call (comp, "GetParameter", index, GST_LEVEL_LOG);
err = OMX_GetParameter (comp->handle, index, param);
DEBUG_IF_OK (comp->parent, err, "Got %s parameter at index 0x%08x: %s "
"(0x%08x)", comp->name, index, gst_omx_error_to_string (err), err);
return err;
}
/* comp->lock must be unlocked while calling this */
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 %s parameter at index 0x%08x",
comp->name, index);
log_omx_api_trace_call (comp, "SetParameter", index, GST_LEVEL_DEBUG);
err = OMX_SetParameter (comp->handle, index, param);
DEBUG_IF_OK (comp->parent, err, "Set %s parameter at index 0x%08x: %s "
"(0x%08x)", comp->name, index, gst_omx_error_to_string (err), err);
return err;
}
/* comp->lock must be unlocked while calling this */
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 %s configuration at index 0x%08x",
comp->name, index);
log_omx_api_trace_call (comp, "GetConfig", index, GST_LEVEL_LOG);
err = OMX_GetConfig (comp->handle, index, config);
DEBUG_IF_OK (comp->parent, err, "Got %s parameter at index 0x%08x: %s "
"(0x%08x)", comp->name, index, gst_omx_error_to_string (err), err);
return err;
}
/* comp->lock must be unlocked while calling this */
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 %s configuration at index 0x%08x",
comp->name, index);
log_omx_api_trace_call (comp, "SetConfig", index, GST_LEVEL_DEBUG);
err = OMX_SetConfig (comp->handle, index, config);
DEBUG_IF_OK (comp->parent, err, "Set %s parameter at index 0x%08x: %s "
"(0x%08x)", comp->name, index, gst_omx_error_to_string (err), err);
return err;
}
OMX_ERRORTYPE
gst_omx_setup_tunnel (GstOMXPort * port1, GstOMXPort * port2)
{
GstOMXComponent *comp1;
GstOMXComponent *comp2;
OMX_ERRORTYPE err;
g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput,
OMX_ErrorUndefined);
comp1 = port1->comp;
g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput,
OMX_ErrorUndefined);
comp2 = port2->comp;
g_return_val_if_fail (comp1->core == comp2->core, OMX_ErrorUndefined);
g_mutex_lock (&comp1->lock);
g_mutex_lock (&comp2->lock);
GST_DEBUG_OBJECT (comp1->parent,
"Setup tunnel between %s port %u and %s port %u",
comp1->name, port1->index, comp2->name, port2->index);
err = comp1->core->setup_tunnel (comp1->handle, port1->index, comp2->handle,
port2->index);
if (err == OMX_ErrorNone) {
port1->tunneled = TRUE;
port2->tunneled = TRUE;
}
DEBUG_IF_OK (comp1->parent, err,
"Setup tunnel between %s port %u and %s port %u: %s (0x%08x)",
comp1->name, port1->index,
comp2->name, port2->index, gst_omx_error_to_string (err), err);
g_mutex_unlock (&comp2->lock);
g_mutex_unlock (&comp1->lock);
return err;
}
OMX_ERRORTYPE
gst_omx_close_tunnel (GstOMXPort * port1, GstOMXPort * port2)
{
GstOMXComponent *comp1;
GstOMXComponent *comp2;
OMX_ERRORTYPE err;
g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput,
OMX_ErrorUndefined);
comp1 = port1->comp;
g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput,
OMX_ErrorUndefined);
comp2 = port2->comp;
g_return_val_if_fail (comp1->core == comp2->core, OMX_ErrorUndefined);
g_return_val_if_fail (port1->tunneled && port2->tunneled, OMX_ErrorUndefined);
g_mutex_lock (&comp1->lock);
g_mutex_lock (&comp2->lock);
GST_DEBUG_OBJECT (comp1->parent,
"Closing tunnel between %s port %u and %s port %u",
comp1->name, port1->index, comp2->name, port2->index);
err = comp1->core->setup_tunnel (comp1->handle, port1->index, 0, 0);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp1->parent,
"Failed to close tunnel on output side %s (0x%08x)",
gst_omx_error_to_string (err), err);
}
err = comp2->core->setup_tunnel (0, 0, comp2->handle, port2->index);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp2->parent,
"Failed to close tunnel on input side %s (0x%08x)",
gst_omx_error_to_string (err), err);
}
port1->tunneled = FALSE;
port2->tunneled = FALSE;
GST_DEBUG_OBJECT (comp1->parent,
"Closed tunnel between %s port %u and %s port %u",
comp1->name, port1->index, comp2->name, port2->index);
g_mutex_unlock (&comp2->lock);
g_mutex_unlock (&comp1->lock);
return err;
}
OMX_ERRORTYPE
gst_omx_port_get_port_definition (GstOMXPort * port,
OMX_PARAM_PORTDEFINITIONTYPE * port_def)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err;
g_return_val_if_fail (port != NULL, OMX_ErrorBadParameter);
comp = port->comp;
GST_OMX_INIT_STRUCT (port_def);
port_def->nPortIndex = port->index;
err = gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
port_def);
return err;
}
OMX_ERRORTYPE
gst_omx_port_update_port_definition (GstOMXPort * port,
OMX_PARAM_PORTDEFINITIONTYPE * port_def)
{
OMX_ERRORTYPE err_get, err_set = OMX_ErrorNone;
GstOMXComponent *comp;
g_return_val_if_fail (port != NULL, FALSE);
comp = port->comp;
if (port_def)
err_set =
gst_omx_component_set_parameter (comp, OMX_IndexParamPortDefinition,
port_def);
err_get = gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition,
&port->port_def);
DEBUG_IF_OK (comp->parent, err_set,
"Updated %s port %u definition: %s (0x%08x)", comp->name, port->index,
gst_omx_error_to_string (err_set), err_set);
if (err_set != OMX_ErrorNone)
return err_set;
else
return err_get;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
GstOMXAcquireBufferReturn
gst_omx_port_acquire_buffer (GstOMXPort * port, GstOMXBuffer ** buf,
GstOMXWait wait)
{
GstOMXAcquireBufferReturn ret = GST_OMX_ACQUIRE_BUFFER_ERROR;
GstOMXComponent *comp;
OMX_ERRORTYPE err;
GstOMXBuffer *_buf = NULL;
gint64 timeout = GST_CLOCK_TIME_NONE;
g_return_val_if_fail (port != NULL, GST_OMX_ACQUIRE_BUFFER_ERROR);
g_return_val_if_fail (!port->tunneled, GST_OMX_ACQUIRE_BUFFER_ERROR);
g_return_val_if_fail (buf != NULL, GST_OMX_ACQUIRE_BUFFER_ERROR);
*buf = NULL;
comp = port->comp;
g_mutex_lock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "Acquiring %s buffer from port %u",
comp->name, port->index);
retry:
gst_omx_component_handle_messages (comp);
/* If we are in the case where we waited for a buffer after EOS,
* make sure we don't do that again */
if (timeout != -1)
timeout = -2;
/* Check if the component is in an error state */
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s is in error state: %s",
comp->name, gst_omx_error_to_string (err));
ret = GST_OMX_ACQUIRE_BUFFER_ERROR;
goto done;
}
/* Check if the port is flushing */
if (port->flushing) {
GST_DEBUG_OBJECT (comp->parent, "Component %s port %d is flushing",
comp->name, port->index);
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 (comp->pending_reconfigure_outports) {
gst_omx_component_handle_messages (comp);
while (comp->pending_reconfigure_outports &&
(err = comp->last_error) == OMX_ErrorNone && !port->flushing) {
GST_DEBUG_OBJECT (comp->parent,
"Waiting for %s output ports to reconfigure", comp->name);
gst_omx_component_wait_message (comp, GST_CLOCK_TIME_NONE);
gst_omx_component_handle_messages (comp);
}
goto retry;
}
/* Only if this port needs to be reconfigured too notify
* the caller about it */
if (port->settings_cookie != port->configured_settings_cookie) {
GST_DEBUG_OBJECT (comp->parent,
"Component %s port %d needs reconfiguring", comp->name, port->index);
ret = GST_OMX_ACQUIRE_BUFFER_RECONFIGURE;
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,
"%s output port %u needs reconfiguration but has buffers pending",
comp->name, port->index);
_buf = g_queue_pop_head (&port->pending_buffers);
ret = GST_OMX_ACQUIRE_BUFFER_OK;
goto done;
}
GST_DEBUG_OBJECT (comp->parent, "Component %s port %d needs reconfiguring",
comp->name, port->index);
ret = GST_OMX_ACQUIRE_BUFFER_RECONFIGURE;
goto done;
}
if (port->port_def.eDir == OMX_DirOutput && port->eos) {
if (!g_queue_is_empty (&port->pending_buffers)) {
GST_DEBUG_OBJECT (comp->parent, "%s output port %u is EOS but has "
"%d buffers pending", comp->name, port->index,
g_queue_get_length (&port->pending_buffers));
_buf = g_queue_pop_head (&port->pending_buffers);
ret = GST_OMX_ACQUIRE_BUFFER_OK;
goto done;
}
if (comp->hacks & GST_OMX_HACK_SIGNALS_PREMATURE_EOS && timeout != -2) {
timeout = 33 * GST_MSECOND;
GST_DEBUG_OBJECT (comp->parent, "%s output port %u is EOS but waiting "
"in case it spits out more buffers", comp->name, port->index);
} else {
GST_DEBUG_OBJECT (comp->parent, "Component %s port %d signalled EOS",
comp->name, port->index);
ret = GST_OMX_ACQUIRE_BUFFER_EOS;
port->eos = FALSE;
goto done;
}
}
/*
* At this point we have no error or flushing/eos 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 %s port %u is empty",
comp->name, port->index);
if (wait == GST_OMX_WAIT) {
gst_omx_component_wait_message (comp,
timeout == -2 ? GST_CLOCK_TIME_NONE : timeout);
/* And now check everything again and maybe get a buffer */
goto retry;
} else {
ret = GST_OMX_ACQUIRE_BUFFER_NO_AVAILABLE;
goto done;
}
}
GST_DEBUG_OBJECT (comp->parent, "%s port %u has pending buffers",
comp->name, port->index);
_buf = g_queue_pop_head (&port->pending_buffers);
ret = GST_OMX_ACQUIRE_BUFFER_OK;
done:
g_mutex_unlock (&comp->lock);
if (_buf) {
g_assert (_buf == _buf->omx_buf->pAppPrivate);
*buf = _buf;
}
GST_DEBUG_OBJECT (comp->parent, "Acquired buffer %p (%p) from %s port %u: %d",
_buf, (_buf ? _buf->omx_buf->pBuffer : NULL), comp->name, port->index,
ret);
return ret;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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 (!port->tunneled, OMX_ErrorUndefined);
g_return_val_if_fail (buf != NULL, OMX_ErrorUndefined);
g_return_val_if_fail (buf->port == port, OMX_ErrorUndefined);
comp = port->comp;
g_mutex_lock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "Releasing buffer %p (%p) to %s port %u",
buf, buf->omx_buf->pBuffer, comp->name, port->index);
gst_omx_component_handle_messages (comp);
if (port->port_def.eDir == OMX_DirOutput) {
/* Reset all flags, some implementations don't
* reset them themselves and the flags are not
* valid anymore after the buffer was consumed
*/
gst_omx_buffer_reset (buf);
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s is in error state: %s "
"(0x%08x)", comp->name, gst_omx_error_to_string (err), err);
g_queue_push_tail (&port->pending_buffers, buf);
gst_omx_component_send_message (comp, NULL);
goto done;
}
if (port->flushing || port->disabled_pending || !port->port_def.bEnabled) {
GST_DEBUG_OBJECT (comp->parent,
"%s port %u is flushing or disabled, not releasing " "buffer",
comp->name, port->index);
g_queue_push_tail (&port->pending_buffers, buf);
gst_omx_component_send_message (comp, NULL);
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) {
log_omx_api_trace_buffer (comp, "EmptyThisBuffer", buf);
err = OMX_EmptyThisBuffer (comp->handle, buf->omx_buf);
} else {
log_omx_api_trace_buffer (comp, "FillThisBuffer", buf);
err = OMX_FillThisBuffer (comp->handle, buf->omx_buf);
}
DEBUG_IF_OK (comp->parent, err, "Released buffer %p to %s port %u: %s "
"(0x%08x)", buf, comp->name, port->index, gst_omx_error_to_string (err),
err);
done:
gst_omx_component_handle_messages (comp);
g_mutex_unlock (&comp->lock);
return err;
}
/* NOTE: Must be called while holding comp->lock */
static gboolean
should_wait_until_flushed (GstOMXPort * port)
{
if (!port->flushed)
/* Flush command hasn't been completed yet by OMX */
return TRUE;
if (port->buffers) {
guint i;
/* Wait for all the buffers used by OMX to be released */
for (i = 0; i < port->buffers->len; i++) {
GstOMXBuffer *buf = g_ptr_array_index (port->buffers, i);
if (buf->used)
return TRUE;
}
}
return FALSE;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_set_flushing (GstOMXPort * port, GstClockTime timeout,
gboolean flush)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
g_mutex_lock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "Setting %s port %d to %sflushing",
comp->name, port->index, (flush ? "" : "not "));
gst_omx_component_handle_messages (comp);
if (flush == port->flushing) {
GST_DEBUG_OBJECT (comp->parent, "%s port %u was %sflushing already",
comp->name, port->index, (flush ? "" : "not "));
goto done;
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s is in error state: %s "
"(0x%08x)", comp->name, gst_omx_error_to_string (err), err);
goto done;
}
port->flushing = flush;
if (flush) {
gboolean signalled;
OMX_ERRORTYPE last_error;
gst_omx_component_send_message (comp, NULL);
/* Now flush the port */
port->flushed = FALSE;
err =
gst_omx_component_send_command (comp, OMX_CommandFlush, port->index,
NULL);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Error sending flush command to %s port %u: %s (0x%08x)", comp->name,
port->index, gst_omx_error_to_string (err), err);
goto done;
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Component %s is in error state: %s (0x%08x)", comp->name,
gst_omx_error_to_string (err), err);
goto done;
}
if (port->flushing != flush) {
GST_ERROR_OBJECT (comp->parent, "%s: another flush happened in the "
" meantime", comp->name);
goto done;
}
if (timeout == 0) {
if (should_wait_until_flushed (port))
err = OMX_ErrorTimeout;
goto done;
}
/* 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;
gst_omx_component_handle_messages (comp);
while (should_wait_until_flushed (port)) {
signalled = gst_omx_component_wait_message (comp, timeout);
if (signalled)
gst_omx_component_handle_messages (comp);
last_error = comp->last_error;
if (!signalled || last_error != OMX_ErrorNone)
/* Something gone wrong or we timed out */
break;
}
port->flushed = FALSE;
GST_DEBUG_OBJECT (comp->parent, "%s port %d flushed", comp->name,
port->index);
if (last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Got error while flushing %s port %u: %s (0x%08x)", comp->name,
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 %s port %u",
comp->name, port->index);
err = OMX_ErrorTimeout;
goto done;
}
}
/* Reset EOS flag */
port->eos = FALSE;
done:
gst_omx_port_update_port_definition (port, NULL);
DEBUG_IF_OK (comp->parent, err, "Set %s port %u to %sflushing: %s (0x%08x)",
comp->name, port->index, (flush ? "" : "not "),
gst_omx_error_to_string (err), err);
gst_omx_component_handle_messages (comp);
g_mutex_unlock (&comp->lock);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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 (&comp->lock);
gst_omx_component_handle_messages (port->comp);
flushing = port->flushing;
g_mutex_unlock (&comp->lock);
GST_DEBUG_OBJECT (comp->parent, "%s port %u is flushing: %d", comp->name,
port->index, flushing);
return flushing;
}
static OMX_ERRORTYPE gst_omx_port_deallocate_buffers_unlocked (GstOMXPort *
port);
/* NOTE: Must be called while holding comp->lock, uses comp->messages_lock */
static OMX_ERRORTYPE
gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port,
const GList * buffers, const GList * images, guint n)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
gint i;
const GList *l;
g_assert (!port->buffers || port->buffers->len == 0);
g_return_val_if_fail (!port->tunneled, OMX_ErrorBadParameter);
comp = port->comp;
gst_omx_component_handle_messages (port->comp);
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, 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_port_update_port_definition (port, NULL);
g_return_val_if_fail (n != -1 || (!buffers
&& !images), OMX_ErrorBadParameter);
if (n == -1)
n = port->port_def.nBufferCountActual;
g_return_val_if_fail (n == port->port_def.nBufferCountActual,
OMX_ErrorBadParameter);
GST_INFO_OBJECT (comp->parent,
"Allocating %d buffers of size %" G_GSIZE_FORMAT " for %s port %u", n,
(size_t) port->port_def.nBufferSize, comp->name, (guint) port->index);
if (!port->buffers)
port->buffers = g_ptr_array_sized_new (n);
l = (buffers ? buffers : images);
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);
if (buffers) {
err =
OMX_UseBuffer (comp->handle, &buf->omx_buf, port->index, buf,
port->port_def.nBufferSize, l->data);
buf->eglimage = FALSE;
} else if (images) {
err =
OMX_UseEGLImage (comp->handle, &buf->omx_buf, port->index, buf,
l->data);
buf->eglimage = TRUE;
} else {
err =
OMX_AllocateBuffer (comp->handle, &buf->omx_buf, port->index, buf,
port->port_def.nBufferSize);
buf->eglimage = FALSE;
}
/* Let the caller decide to print an error when OMX_UseBuffer or
* OMX_UseEGLImage fail. Indeed it can be part of a trial path. So
* it is not necessary to warn the user if the fallback path succeeds.
*/
if (err != OMX_ErrorNone) {
GST_CAT_LEVEL_LOG (GST_CAT_DEFAULT, (buffers
|| images) ? GST_LEVEL_INFO : GST_LEVEL_ERROR, comp->parent,
"Failed to allocate buffer for %s port %u: %s (0x%08x)", comp->name,
port->index, gst_omx_error_to_string (err), err);
gst_omx_port_deallocate_buffers_unlocked (port);
goto done;
}
GST_DEBUG_OBJECT (comp->parent, "%s: allocated buffer %p (%p)",
comp->name, buf, buf->omx_buf->pBuffer);
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);
if (buffers || images)
l = l->next;
}
gst_omx_component_handle_messages (comp);
done:
gst_omx_port_update_port_definition (port, NULL);
INFO_IF_OK (comp->parent, err, "Allocated buffers for %s port %u: %s "
"(0x%08x)", comp->name, port->index, gst_omx_error_to_string (err), err);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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->comp->lock);
err = gst_omx_port_allocate_buffers_unlocked (port, NULL, NULL, -1);
port->allocation = GST_OMX_BUFFER_ALLOCATION_ALLOCATE_BUFFER;
g_mutex_unlock (&port->comp->lock);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_use_buffers (GstOMXPort * port, const GList * buffers)
{
OMX_ERRORTYPE err;
guint n;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_mutex_lock (&port->comp->lock);
n = g_list_length ((GList *) buffers);
err = gst_omx_port_allocate_buffers_unlocked (port, buffers, NULL, n);
port->allocation = GST_OMX_BUFFER_ALLOCATION_USE_BUFFER;
g_mutex_unlock (&port->comp->lock);
return err;
}
gboolean
gst_omx_is_dynamic_allocation_supported (void)
{
/* The Zynqultrascaleplus stack implements OMX 1.1.0 but supports the dynamic
* allocation mode from 1.2.0 as an extension. */
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
return TRUE;
#endif
#if OMX_VERSION_MINOR == 2
return TRUE;
#else
return FALSE;
#endif
}
/* OMX 1.2.0 introduced a dynamic allocation mode where only buffer headers are
* being allocated during component's initialization. The actual buffers are
* allocated upstream and passed to OMX by setting the pBuffer dynamically
* for each input buffer.
*
* This function takes care of allocating the buffer headers. Element should
* then use one of the gst_omx_buffer_map_*() method to update buffer's pBuffer
* pointers for each incoming buffer.
*
* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_use_dynamic_buffers (GstOMXPort * port)
{
OMX_ERRORTYPE err;
GList *buffers = NULL;
guint i, n;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
n = port->port_def.nBufferCountActual;
for (i = 0; i < port->port_def.nBufferCountActual; i++)
/* Pass NULL to UseBuffer() as the buffer is dynamic and so its payload
* will be set each time before being passed to OMX. */
buffers = g_list_prepend (buffers, GUINT_TO_POINTER (NULL));
g_mutex_lock (&port->comp->lock);
err = gst_omx_port_allocate_buffers_unlocked (port, buffers, NULL, n);
port->allocation = GST_OMX_BUFFER_ALLOCATION_USE_BUFFER_DYNAMIC;
g_mutex_unlock (&port->comp->lock);
g_list_free (buffers);
return err;
}
/* gst_omx_buffer_map_* methods are used in dynamic buffer mode to map
* a frame/memory/buffer and update @buffer so its pBuffer points to the
* mapped data. It also ensures that the input will stay alive until
* gst_omx_buffer_unmap() is called.
* This is used in OMX 1.2.0 dynamic allocation mode so an OMX component can
* safely process @buffer's content without having to copy it.
* The input will be automatically unmapped when @buffer is released by OMX.
*/
gboolean
gst_omx_buffer_map_frame (GstOMXBuffer * buffer, GstBuffer * input,
GstVideoInfo * info)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
g_return_val_if_fail (!buffer->input_buffer_mapped, FALSE);
if (!gst_video_frame_map (&buffer->input_frame, info, input, GST_MAP_READ))
return FALSE;
buffer->input_frame_mapped = TRUE;
buffer->omx_buf->pBuffer =
GST_VIDEO_FRAME_PLANE_DATA (&buffer->input_frame, 0);
buffer->omx_buf->nAllocLen = gst_buffer_get_size (input);
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
gboolean
gst_omx_buffer_map_memory (GstOMXBuffer * buffer, GstMemory * mem)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (mem != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
g_return_val_if_fail (!buffer->input_buffer_mapped, FALSE);
if (!gst_memory_map (mem, &buffer->map, GST_MAP_READ))
return FALSE;
buffer->input_mem = gst_memory_ref (mem);
buffer->omx_buf->pBuffer = buffer->map.data;
buffer->omx_buf->nAllocLen = buffer->map.size;
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
gboolean
gst_omx_buffer_import_fd (GstOMXBuffer * buffer, GstBuffer * input)
{
gint fd;
GstMemory *mem;
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (input != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
g_return_val_if_fail (!buffer->input_buffer_mapped, FALSE);
mem = gst_buffer_peek_memory (input, 0);
g_return_val_if_fail (gst_is_dmabuf_memory (mem), FALSE);
fd = gst_dmabuf_memory_get_fd (mem);
buffer->input_buffer = gst_buffer_ref (input);
buffer->omx_buf->pBuffer = GUINT_TO_POINTER (fd);
buffer->omx_buf->nAllocLen = gst_memory_get_sizes (mem, NULL, NULL);
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
gboolean
gst_omx_buffer_map_buffer (GstOMXBuffer * buffer, GstBuffer * input)
{
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (input != NULL, FALSE);
g_return_val_if_fail (!buffer->input_frame_mapped, FALSE);
g_return_val_if_fail (!buffer->input_mem, FALSE);
g_return_val_if_fail (!buffer->input_buffer, FALSE);
g_return_val_if_fail (!buffer->input_buffer_mapped, FALSE);
if (!gst_buffer_map (input, &buffer->map, GST_MAP_READ))
return FALSE;
buffer->input_buffer_mapped = TRUE;
buffer->input_buffer = gst_buffer_ref (input);
buffer->omx_buf->pBuffer = buffer->map.data;
buffer->omx_buf->nAllocLen = buffer->map.size;
buffer->omx_buf->nFilledLen = buffer->omx_buf->nAllocLen;
return TRUE;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_use_eglimages (GstOMXPort * port, const GList * images)
{
OMX_ERRORTYPE err;
guint n;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_mutex_lock (&port->comp->lock);
n = g_list_length ((GList *) images);
err = gst_omx_port_allocate_buffers_unlocked (port, NULL, images, n);
g_mutex_unlock (&port->comp->lock);
return err;
}
/* NOTE: Must be called while holding comp->lock, uses comp->messages_lock */
static OMX_ERRORTYPE
gst_omx_port_deallocate_buffers_unlocked (GstOMXPort * port)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
gint i, n;
g_return_val_if_fail (!port->tunneled, OMX_ErrorBadParameter);
comp = port->comp;
GST_INFO_OBJECT (comp->parent, "Deallocating buffers of %s port %u",
comp->name, port->index);
gst_omx_component_handle_messages (port->comp);
if (!port->buffers) {
GST_DEBUG_OBJECT (comp->parent, "No buffers allocated for %s port %u",
comp->name, port->index);
goto done;
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, 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 %s "
"port %u", buf, comp->name, 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;
GST_DEBUG_OBJECT (comp->parent, "%s: deallocating buffer %p (%p)",
comp->name, buf, buf->omx_buf->pBuffer);
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 %s port %u: %s (0x%08x)", i,
comp->name, 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);
g_ptr_array_unref (port->buffers);
port->buffers = NULL;
gst_omx_component_handle_messages (comp);
done:
gst_omx_port_update_port_definition (port, NULL);
DEBUG_IF_OK (comp->parent, err, "Deallocated buffers of %s port %u: %s "
"(0x%08x)", comp->name, port->index, gst_omx_error_to_string (err), err);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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->comp->lock);
err = gst_omx_port_deallocate_buffers_unlocked (port);
g_mutex_unlock (&port->comp->lock);
return err;
}
/* NOTE: Must be called while holding comp->lock, uses comp->messages_lock */
static OMX_ERRORTYPE
gst_omx_port_set_enabled_unlocked (GstOMXPort * port, gboolean enabled)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
comp = port->comp;
gst_omx_component_handle_messages (comp);
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (err), err);
goto done;
}
if (port->enabled_pending || port->disabled_pending) {
GST_ERROR_OBJECT (comp->parent, "%s port %d enabled/disabled pending "
"already", comp->name, port->index);
#if OMX_VERSION_MINOR == 2
err = OMX_ErrorBadParameter;
#else
err = OMX_ErrorInvalidState;
#endif
goto done;
}
GST_INFO_OBJECT (comp->parent, "Setting %s port %u to %s", comp->name,
port->index, (enabled ? "enabled" : "disabled"));
/* Check if the port is already enabled/disabled first */
gst_omx_port_update_port_definition (port, NULL);
if (! !port->port_def.bEnabled == ! !enabled)
goto done;
if (enabled)
port->enabled_pending = TRUE;
else
port->disabled_pending = TRUE;
if (enabled)
err =
gst_omx_component_send_command (comp, OMX_CommandPortEnable,
port->index, NULL);
else
err =
gst_omx_component_send_command (comp, OMX_CommandPortDisable,
port->index, NULL);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to send enable/disable command to %s port %u: %s (0x%08x)",
comp->name, port->index, gst_omx_error_to_string (err), err);
goto done;
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (err), err);
goto done;
}
done:
gst_omx_component_handle_messages (comp);
gst_omx_port_update_port_definition (port, NULL);
INFO_IF_OK (comp->parent, err, "Set %s port %u to %s%s: %s (0x%08x)",
comp->name, port->index, (err == OMX_ErrorNone ? "" : "not "),
(enabled ? "enabled" : "disabled"), gst_omx_error_to_string (err), err);
return err;
}
static OMX_ERRORTYPE
gst_omx_port_wait_buffers_released_unlocked (GstOMXPort * port,
GstClockTime timeout)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
OMX_ERRORTYPE last_error;
gboolean signalled;
comp = port->comp;
gst_omx_component_handle_messages (comp);
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (err), err);
goto done;
}
GST_INFO_OBJECT (comp->parent, "Waiting for %s port %u to release all "
"buffers", comp->name, port->index);
if (timeout == 0) {
if (!port->flushed || (port->buffers
&& port->buffers->len >
g_queue_get_length (&port->pending_buffers)))
err = OMX_ErrorTimeout;
goto done;
}
/* Wait until all buffers are released by the port */
signalled = TRUE;
last_error = OMX_ErrorNone;
gst_omx_component_handle_messages (comp);
while (signalled && last_error == OMX_ErrorNone && (port->buffers
&& port->buffers->len >
g_queue_get_length (&port->pending_buffers))) {
signalled = gst_omx_component_wait_message (comp, timeout);
if (signalled)
gst_omx_component_handle_messages (comp);
last_error = comp->last_error;
}
if (last_error != OMX_ErrorNone) {
err = last_error;
GST_ERROR_OBJECT (comp->parent,
"Got error while waiting for %s port %u to release all buffers: %s "
"(0x%08x)", comp->name, port->index, gst_omx_error_to_string (err),
err);
goto done;
} else if (!signalled) {
GST_ERROR_OBJECT (comp->parent, "Timeout waiting for %s port %u to "
"release all buffers", comp->name, port->index);
err = OMX_ErrorTimeout;
goto done;
}
done:
gst_omx_component_handle_messages (comp);
gst_omx_port_update_port_definition (port, NULL);
DEBUG_IF_OK (comp->parent, err,
"Waited for %s port %u to release all buffers: %s (0x%08x)", comp->name,
port->index, gst_omx_error_to_string (err), err);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_wait_buffers_released (GstOMXPort * port, GstClockTime timeout)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_mutex_lock (&port->comp->lock);
err = gst_omx_port_wait_buffers_released_unlocked (port, timeout);
g_mutex_unlock (&port->comp->lock);
return err;
}
void
gst_omx_port_requeue_buffer (GstOMXPort * port, GstOMXBuffer * buf)
{
g_mutex_lock (&port->comp->lock);
g_queue_push_tail (&port->pending_buffers, buf);
g_mutex_unlock (&port->comp->lock);
/* awake gst_omx_port_acquire_buffer() */
gst_omx_component_send_message (port->comp, NULL);
}
/* NOTE: Uses comp->lock and comp->messages_lock */
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->comp->lock);
err = gst_omx_port_set_enabled_unlocked (port, enabled);
g_mutex_unlock (&port->comp->lock);
return err;
}
static OMX_ERRORTYPE
gst_omx_port_populate_unlocked (GstOMXPort * port)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
GstOMXBuffer *buf;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
GST_DEBUG_OBJECT (comp->parent, "Populating %s port %d", comp->name,
port->index);
gst_omx_component_handle_messages (comp);
if (port->flushing || port->disabled_pending || !port->port_def.bEnabled) {
GST_DEBUG_OBJECT (comp->parent, "%s port %u is flushing or disabled",
comp->name, port->index);
err = OMX_ErrorIncorrectStateOperation;
goto done;
}
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s is in error state: %s"
"(0x%08x)", comp->name, gst_omx_error_to_string (err), err);
goto done;
}
if (port->port_def.eDir == OMX_DirOutput && port->buffers && !port->tunneled) {
/* 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.
* Also reset nFilledLen as FillThisBuffer() expects an empty buffer.
*/
gst_omx_buffer_reset (buf);
log_omx_api_trace_buffer (comp, "FillThisBuffer", buf);
err = OMX_FillThisBuffer (comp->handle, buf->omx_buf);
if (err != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Failed to pass buffer %p (%p) to %s port %u: %s (0x%08x)", buf,
buf->omx_buf->pBuffer, comp->name, port->index,
gst_omx_error_to_string (err), err);
goto done;
}
GST_DEBUG_OBJECT (comp->parent, "Passed buffer %p (%p) to component %s",
buf, buf->omx_buf->pBuffer, comp->name);
}
}
done:
gst_omx_port_update_port_definition (port, NULL);
DEBUG_IF_OK (comp->parent, err, "Populated %s port %u: %s (0x%08x)",
comp->name, port->index, gst_omx_error_to_string (err), err);
gst_omx_component_handle_messages (comp);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_populate (GstOMXPort * port)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_mutex_lock (&port->comp->lock);
err = gst_omx_port_populate_unlocked (port);
g_mutex_unlock (&port->comp->lock);
return err;
}
/* NOTE: Must be called while holding comp->lock, uses comp->messages_lock */
static OMX_ERRORTYPE
gst_omx_port_wait_enabled_unlocked (GstOMXPort * port, GstClockTime timeout)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
gboolean signalled;
OMX_ERRORTYPE last_error;
gboolean enabled;
comp = port->comp;
/* Check the current port status */
gst_omx_port_update_port_definition (port, NULL);
if (port->enabled_pending)
enabled = TRUE;
else if (port->disabled_pending)
enabled = FALSE;
else
enabled = port->port_def.bEnabled;
gst_omx_component_handle_messages (comp);
if ((err = comp->last_error) != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent, "Component %s in error state: %s (0x%08x)",
comp->name, gst_omx_error_to_string (err), err);
goto done;
}
GST_INFO_OBJECT (comp->parent, "Waiting for %s port %u to be %s",
comp->name, port->index, (enabled ? "enabled" : "disabled"));
if (timeout == 0) {
if (port->enabled_pending || port->disabled_pending)
err = OMX_ErrorTimeout;
goto done;
}
/* And now wait until the enable/disable command is finished */
signalled = TRUE;
last_error = OMX_ErrorNone;
gst_omx_port_update_port_definition (port, NULL);
gst_omx_component_handle_messages (comp);
while (signalled && last_error == OMX_ErrorNone &&
(! !port->port_def.bEnabled != ! !enabled || port->enabled_pending
|| port->disabled_pending)) {
signalled = gst_omx_component_wait_message (comp, timeout);
if (signalled)
gst_omx_component_handle_messages (comp);
last_error = comp->last_error;
gst_omx_port_update_port_definition (port, NULL);
}
port->enabled_pending = FALSE;
port->disabled_pending = FALSE;
if (!signalled) {
GST_ERROR_OBJECT (comp->parent,
"Timeout waiting for %s port %u to be %s", comp->name, port->index,
(enabled ? "enabled" : "disabled"));
err = OMX_ErrorTimeout;
goto done;
} else if (last_error != OMX_ErrorNone) {
GST_ERROR_OBJECT (comp->parent,
"Got error while waiting for %s port %u to be %s: %s (0x%08x)",
comp->name, port->index, (enabled ? "enabled" : "disabled"),
gst_omx_error_to_string (err), err);
err = last_error;
} else {
if (enabled) {
/* Reset EOS flag */
port->eos = FALSE;
}
}
gst_omx_component_handle_messages (comp);
done:
gst_omx_port_update_port_definition (port, NULL);
GST_INFO_OBJECT (comp->parent, "%s port %u is %s%s: %s (0x%08x)", comp->name,
port->index, (err == OMX_ErrorNone ? "" : "not "),
(enabled ? "enabled" : "disabled"), gst_omx_error_to_string (err), err);
return err;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_wait_enabled (GstOMXPort * port, GstClockTime timeout)
{
OMX_ERRORTYPE err;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
g_mutex_lock (&port->comp->lock);
err = gst_omx_port_wait_enabled_unlocked (port, timeout);
g_mutex_unlock (&port->comp->lock);
return err;
}
gboolean
gst_omx_port_is_enabled (GstOMXPort * port)
{
gboolean enabled;
g_return_val_if_fail (port != NULL, FALSE);
gst_omx_port_update_port_definition (port, NULL);
enabled = ! !port->port_def.bEnabled;
GST_DEBUG_OBJECT (port->comp->parent, "%s port %u is enabled: %d",
port->comp->name, port->index, enabled);
return enabled;
}
/* NOTE: Uses comp->lock and comp->messages_lock */
OMX_ERRORTYPE
gst_omx_port_mark_reconfigured (GstOMXPort * port)
{
GstOMXComponent *comp;
OMX_ERRORTYPE err = OMX_ErrorNone;
g_return_val_if_fail (port != NULL, OMX_ErrorUndefined);
comp = port->comp;
g_mutex_lock (&comp->lock);
GST_INFO_OBJECT (comp->parent, "Marking %s port %u is reconfigured",
comp->name, port->index);
gst_omx_component_handle_messages (comp);
if ((err = comp->last_error) != OMX_ErrorNone)
goto done;
port->configured_settings_cookie = port->settings_cookie;
if (port->port_def.eDir == OMX_DirOutput) {
GList *l;
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)
gst_omx_component_send_message (comp, NULL);
}
done:
gst_omx_port_update_port_definition (port, NULL);
INFO_IF_OK (comp->parent, err, "Marked %s port %u as reconfigured: %s "
"(0x%08x)", comp->name, port->index, gst_omx_error_to_string (err), err);
g_mutex_unlock (&comp->lock);
return err;
}
/* The OMX specs states that the nBufferCountActual of a port has to default
* to its nBufferCountMin. If we don't change nBufferCountActual we purely rely
* on this default. But in some cases, OMX may change nBufferCountMin before we
* allocate buffers. Like for example when configuring the input ports with the
* actual format, it may decrease the number of minimal buffers required.
* This method checks this and update nBufferCountActual if needed so we'll use
* less buffers than the worst case in such scenarios.
*/
gboolean
gst_omx_port_ensure_buffer_count_actual (GstOMXPort * port, guint extra)
{
OMX_PARAM_PORTDEFINITIONTYPE port_def;
guint nb;
gst_omx_port_get_port_definition (port, &port_def);
nb = port_def.nBufferCountMin + extra;
if (port_def.nBufferCountActual != nb) {
port_def.nBufferCountActual = nb;
GST_DEBUG_OBJECT (port->comp->parent,
"set port %d nBufferCountActual to %d", (guint) port->index, nb);
if (gst_omx_port_update_port_definition (port, &port_def) != OMX_ErrorNone)
return FALSE;
}
return TRUE;
}
gboolean
gst_omx_port_update_buffer_count_actual (GstOMXPort * port, guint nb)
{
OMX_PARAM_PORTDEFINITIONTYPE port_def;
gst_omx_port_get_port_definition (port, &port_def);
if (nb < port_def.nBufferCountMin) {
GST_DEBUG_OBJECT (port->comp->parent,
"Requested to use %d buffers on port %d but it's minimum is %d", nb,
(guint) port->index, (guint) port_def.nBufferCountMin);
nb = port_def.nBufferCountMin;
}
if (port_def.nBufferCountActual != nb) {
port_def.nBufferCountActual = nb;
GST_DEBUG_OBJECT (port->comp->parent,
"set port %d nBufferCountActual to %d", (guint) port->index, nb);
if (gst_omx_port_update_port_definition (port, &port_def) != OMX_ErrorNone)
return FALSE;
}
return TRUE;
}
gboolean
gst_omx_port_set_dmabuf (GstOMXPort * port, gboolean dmabuf)
{
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
OMX_ALG_PORT_PARAM_BUFFER_MODE buffer_mode;
OMX_ERRORTYPE err;
GST_OMX_INIT_STRUCT (&buffer_mode);
buffer_mode.nPortIndex = port->index;
if (dmabuf)
buffer_mode.eMode = OMX_ALG_BUF_DMA;
else
buffer_mode.eMode = OMX_ALG_BUF_NORMAL;
err =
gst_omx_component_set_parameter (port->comp,
(OMX_INDEXTYPE) OMX_ALG_IndexPortParamBufferMode, &buffer_mode);
if (err != OMX_ErrorNone) {
GST_WARNING_OBJECT (port->comp->parent,
"Failed to set port %d in %sdmabuf mode: %s (0x%08x)",
port->index, dmabuf ? "" : "non-", gst_omx_error_to_string (err), err);
return FALSE;
}
return TRUE;
#else
/* dmabuf not supported for this platform */
return FALSE;
#endif
}
gboolean
gst_omx_port_set_subframe (GstOMXPort * port, gboolean enabled)
{
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
OMX_ALG_VIDEO_PARAM_SUBFRAME subframe_mode;
OMX_ERRORTYPE err;
GST_OMX_INIT_STRUCT (&subframe_mode);
subframe_mode.nPortIndex = port->index;
subframe_mode.bEnableSubframe = enabled;
err = gst_omx_component_set_parameter (port->comp,
(OMX_INDEXTYPE) OMX_ALG_IndexParamVideoSubframe, &subframe_mode);
if (err != OMX_ErrorNone) {
GST_WARNING_OBJECT (port->comp->parent,
"Failed to %s subframe mode on port %d: %s (0x%08x)",
enabled ? "enable" : "disable", port->index,
gst_omx_error_to_string (err), err);
return FALSE;
}
return TRUE;
#else
/* subframe mode is not supported on this platform */
return FALSE;
#endif
}
gboolean
gst_omx_port_get_subframe (GstOMXPort * port)
{
#ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
OMX_ALG_VIDEO_PARAM_SUBFRAME subframe_mode;
OMX_ERRORTYPE err;
GST_OMX_INIT_STRUCT (&subframe_mode);
subframe_mode.nPortIndex = port->index;
err = gst_omx_component_get_parameter (port->comp,
(OMX_INDEXTYPE) OMX_ALG_IndexParamVideoSubframe, &subframe_mode);
if (err != OMX_ErrorNone) {
GST_WARNING_OBJECT (port->comp->parent,
"Failed to get subframe mode on port %d: %s (0x%08x)",
port->index, gst_omx_error_to_string (err), err);
return FALSE;
}
return subframe_mode.bEnableSubframe;
#else
/* subframe mode is not supported on this platform */
return FALSE;
#endif
}
typedef GType (*GGetTypeFunction) (void);
static const GGetTypeFunction types[] = {
gst_omx_analog_audio_sink_get_type, gst_omx_hdmi_audio_sink_get_type,
gst_omx_mpeg2_video_dec_get_type, 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, gst_omx_mjpeg_dec_get_type,
gst_omx_aac_dec_get_type, gst_omx_mp3_dec_get_type,
gst_omx_aac_dec_get_type, gst_omx_mp3_enc_get_type,
gst_omx_amr_dec_get_type
#ifdef HAVE_VP8
, gst_omx_vp8_dec_get_type
#endif
#ifdef HAVE_THEORA
, gst_omx_theora_dec_get_type
#endif
#ifdef HAVE_HEVC
, gst_omx_h265_enc_get_type, gst_omx_h265_dec_get_type
#endif
};
struct TypeOffest
{
GType (*get_type) (void);
glong offset;
};
static const struct TypeOffest base_types[] = {
{gst_omx_audio_sink_get_type, G_STRUCT_OFFSET (GstOMXAudioSinkClass, cdata)},
{gst_omx_video_dec_get_type, G_STRUCT_OFFSET (GstOMXVideoDecClass, cdata)},
{gst_omx_video_enc_get_type, G_STRUCT_OFFSET (GstOMXVideoEncClass, cdata)},
{gst_omx_audio_dec_get_type, G_STRUCT_OFFSET (GstOMXAudioDecClass, cdata)},
{gst_omx_audio_enc_get_type, G_STRUCT_OFFSET (GstOMXAudioEncClass, cdata)},
};
static GKeyFile *config = NULL;
GKeyFile *
gst_omx_get_configuration (void)
{
return config;
}
const gchar *
gst_omx_error_to_string (OMX_ERRORTYPE err)
{
guint err_u = (guint) err;
switch (err_u) {
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_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_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_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_ErrorSeperateTablesUsed:
return "Separate tables used";
case OMX_ErrorTunnelingUnsupported:
return "Tunneling unsupported";
#if OMX_VERSION_MINOR == 1
case OMX_ErrorInvalidComponent:
return "Invalid component";
case OMX_ErrorInvalidState:
return "Invalid state";
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_ErrorContentPipeOpenFailed:
return "Content pipe open failed";
case OMX_ErrorContentPipeCreationFailed:
return "Content pipe creation failed";
#endif
default:
if (err_u >= (guint) OMX_ErrorKhronosExtensions
&& err_u < (guint) OMX_ErrorVendorStartUnused) {
return "Khronos extension error";
} else if (err_u >= (guint) OMX_ErrorVendorStartUnused
&& err_u < (guint) OMX_ErrorMax) {
return "Vendor specific error";
} else {
return "Unknown error";
}
}
}
const gchar *
gst_omx_state_to_string (OMX_STATETYPE state)
{
switch (state) {
case OMX_StateInvalid:
return "Invalid";
case OMX_StateLoaded:
return "Loaded";
case OMX_StateIdle:
return "Idle";
case OMX_StateExecuting:
return "Executing";
case OMX_StatePause:
return "Pause";
case OMX_StateWaitForResources:
return "WaitForResources";
default:
if (state >= OMX_StateKhronosExtensions
&& state < OMX_StateVendorStartUnused)
return "KhronosExtensionState";
else if (state >= OMX_StateVendorStartUnused && state < OMX_StateMax)
return "CustomVendorState";
break;
}
return "Unknown state";
}
const gchar *
gst_omx_command_to_string (OMX_COMMANDTYPE cmd)
{
switch (cmd) {
case OMX_CommandStateSet:
return "SetState";
case OMX_CommandFlush:
return "Flush";
case OMX_CommandPortDisable:
return "DisablePort";
case OMX_CommandPortEnable:
return "EnablePort";
case OMX_CommandMarkBuffer:
return "MarkBuffer";
default:
if (cmd >= OMX_CommandKhronosExtensions
&& cmd < OMX_CommandVendorStartUnused)
return "KhronosExtensionCommand";
if (cmd >= OMX_CommandVendorStartUnused && cmd < OMX_CommandMax)
return "VendorExtensionCommand";
break;
}
return "Unknown command";
}
struct BufferFlagString
{
guint32 flag;
const gchar *str;
};
struct BufferFlagString buffer_flags_map[] = {
{OMX_BUFFERFLAG_EOS, "eos"},
{OMX_BUFFERFLAG_STARTTIME, "start-time"},
{OMX_BUFFERFLAG_DECODEONLY, "decode-only"},
{OMX_BUFFERFLAG_DATACORRUPT, "data-corrupt"},
{OMX_BUFFERFLAG_ENDOFFRAME, "end-of-frame"},
{OMX_BUFFERFLAG_SYNCFRAME, "sync-frame"},
{OMX_BUFFERFLAG_EXTRADATA, "extra-data"},
{OMX_BUFFERFLAG_CODECCONFIG, "codec-config"},
/* Introduced in OMX 1.2.0 */
#ifdef OMX_BUFFERFLAG_TIMESTAMPINVALID
{OMX_BUFFERFLAG_TIMESTAMPINVALID, "timestamp-invalid"},
#endif
#ifdef OMX_BUFFERFLAG_READONLY
{OMX_BUFFERFLAG_READONLY, "read-only"},
#endif
#ifdef OMX_BUFFERFLAG_ENDOFSUBFRAME
{OMX_BUFFERFLAG_ENDOFSUBFRAME, "end-of-subframe"},
#endif
#ifdef OMX_BUFFERFLAG_SKIPFRAME
{OMX_BUFFERFLAG_SKIPFRAME, "skip-frame"},
#endif
{0, NULL},
};
const gchar *
gst_omx_buffer_flags_to_string (guint32 flags)
{
GString *s = NULL;
guint i;
const gchar *str;
if (flags == 0)
return "";
/* Keep a cache of the string representation of the flags so we don't allocate
* and free strings for each buffer. In practice we should only have a handfull
* of flags so the cache won't consume much memory. */
if (!buffer_flags_str) {
G_LOCK (buffer_flags_str);
buffer_flags_str = g_hash_table_new_full (NULL, NULL, NULL, g_free);
G_UNLOCK (buffer_flags_str);
}
str = g_hash_table_lookup (buffer_flags_str, GUINT_TO_POINTER (flags));
if (str)
return str;
for (i = 0; buffer_flags_map[i].str != NULL; i++) {
if ((flags & buffer_flags_map[i].flag) == 0)
continue;
if (!s)
s = g_string_new (buffer_flags_map[i].str);
else
g_string_append_printf (s, ", %s", buffer_flags_map[i].str);
}
if (!s)
return "<unknown>";
str = g_string_free (s, FALSE);
G_LOCK (buffer_flags_str);
/* Transfer ownership of str to hash table */
g_hash_table_insert (buffer_flags_str, GUINT_TO_POINTER (flags),
(gchar *) str);
G_UNLOCK (buffer_flags_str);
return str;
}
#if defined(USE_OMX_TARGET_RPI)
#define DEFAULT_HACKS (GST_OMX_HACK_NO_COMPONENT_ROLE | GST_OMX_HACK_HEIGHT_MULTIPLE_16)
#else
#define DEFAULT_HACKS (0)
#endif
guint64
gst_omx_parse_hacks (gchar ** hacks)
{
guint64 hacks_flags = DEFAULT_HACKS;
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 if (g_str_equal (*hacks, "no-empty-eos-buffer"))
hacks_flags |= GST_OMX_HACK_NO_EMPTY_EOS_BUFFER;
else if (g_str_equal (*hacks, "drain-may-not-return"))
hacks_flags |= GST_OMX_HACK_DRAIN_MAY_NOT_RETURN;
else if (g_str_equal (*hacks, "no-component-role"))
hacks_flags |= GST_OMX_HACK_NO_COMPONENT_ROLE;
else if (g_str_equal (*hacks, "no-disable-outport"))
hacks_flags |= GST_OMX_HACK_NO_DISABLE_OUTPORT;
else if (g_str_equal (*hacks, "signals-premature-eos"))
hacks_flags |= GST_OMX_HACK_SIGNALS_PREMATURE_EOS;
else if (g_str_equal (*hacks, "height-multiple-16"))
hacks_flags |= GST_OMX_HACK_HEIGHT_MULTIPLE_16;
else if (g_str_equal (*hacks, "pass-profile-to-decoder"))
hacks_flags |= GST_OMX_HACK_PASS_PROFILE_TO_DECODER;
else if (g_str_equal (*hacks, "pass-color-format-to-decoder"))
hacks_flags |= GST_OMX_HACK_PASS_COLOR_FORMAT_TO_DECODER;
else if (g_str_equal (*hacks, "ensure-buffer-count-actual"))
hacks_flags |= GST_OMX_HACK_ENSURE_BUFFER_COUNT_ACTUAL;
else
GST_WARNING ("Unknown hack: %s", *hacks);
hacks++;
}
return hacks_flags;
}
void
gst_omx_set_default_role (GstOMXClassData * class_data,
const gchar * default_role)
{
if (!class_data->component_role)
class_data->component_role = default_role;
}
static void
_class_init (gpointer g_class, gpointer data)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstOMXClassData *class_data = NULL;
GKeyFile *config;
const gchar *element_name = data;
GError *err;
gchar *core_name, *component_name, *component_role;
gint in_port_index, out_port_index;
gchar *template_caps;
GstPadTemplate *templ;
GstCaps *caps;
gchar **hacks;
int i;
if (!element_name)
return;
/* Find the GstOMXClassData for this class */
for (i = 0; i < G_N_ELEMENTS (base_types); i++) {
GType gtype = base_types[i].get_type ();
if (G_TYPE_CHECK_CLASS_TYPE (g_class, gtype)) {
class_data = (GstOMXClassData *)
(((guint8 *) g_class) + base_types[i].offset);
break;
}
}
g_assert (class_data != NULL);
config = gst_omx_get_configuration ();
/* This will alwaxys succeed, see check in plugin_init */
core_name = g_key_file_get_string (config, element_name, "core-name", NULL);
g_assert (core_name != NULL);
class_data->core_name = core_name;
component_name =
g_key_file_get_string (config, element_name, "component-name", NULL);
g_assert (component_name != NULL);
class_data->component_name = component_name;
/* If this fails we simply don't set a role */
if ((component_role =
g_key_file_get_string (config, element_name, "component-role",
NULL))) {
GST_DEBUG ("Using component-role '%s' for element '%s'", component_role,
element_name);
class_data->component_role = component_role;
}
/* Now set the inport/outport indizes and assume sane defaults */
err = NULL;
in_port_index =
g_key_file_get_integer (config, element_name, "in-port-index", &err);
if (err != NULL) {
GST_DEBUG ("No 'in-port-index' set for element '%s', auto-detecting: %s",
element_name, err->message);
in_port_index = -1;
g_error_free (err);
}
class_data->in_port_index = in_port_index;
err = NULL;
out_port_index =
g_key_file_get_integer (config, element_name, "out-port-index", &err);
if (err != NULL) {
GST_DEBUG ("No 'out-port-index' set for element '%s', auto-detecting: %s",
element_name, err->message);
out_port_index = -1;
g_error_free (err);
}
class_data->out_port_index = out_port_index;
/* Add pad templates */
err = NULL;
if (class_data->type != GST_OMX_COMPONENT_TYPE_SOURCE) {
if (!(template_caps =
g_key_file_get_string (config, element_name, "sink-template-caps",
&err))) {
GST_DEBUG
("No sink template caps specified for element '%s', using default '%s'",
element_name, class_data->default_sink_template_caps);
caps = gst_caps_from_string (class_data->default_sink_template_caps);
g_assert (caps != NULL);
g_error_free (err);
} else {
caps = gst_caps_from_string (template_caps);
if (!caps) {
GST_DEBUG
("Could not parse sink template caps '%s' for element '%s', using default '%s'",
template_caps, element_name,
class_data->default_sink_template_caps);
caps = gst_caps_from_string (class_data->default_sink_template_caps);
g_assert (caps != NULL);
}
}
templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
g_free (template_caps);
gst_element_class_add_pad_template (element_class, templ);
gst_caps_unref (caps);
}
err = NULL;
if (class_data->type != GST_OMX_COMPONENT_TYPE_SINK) {
if (!(template_caps =
g_key_file_get_string (config, element_name, "src-template-caps",
&err))) {
GST_DEBUG
("No src template caps specified for element '%s', using default '%s'",
element_name, class_data->default_src_template_caps);
caps = gst_caps_from_string (class_data->default_src_template_caps);
g_assert (caps != NULL);
g_error_free (err);
} else {
caps = gst_caps_from_string (template_caps);
if (!caps) {
GST_DEBUG
("Could not parse src template caps '%s' for element '%s', using default '%s'",
template_caps, element_name, class_data->default_src_template_caps);
caps = gst_caps_from_string (class_data->default_src_template_caps);
g_assert (caps != NULL);
}
}
templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
g_free (template_caps);
gst_element_class_add_pad_template (element_class, templ);
gst_caps_unref (caps);
}
if ((hacks =
g_key_file_get_string_list (config, element_name, "hacks", NULL,
NULL))) {
#ifndef GST_DISABLE_GST_DEBUG
gchar **walk = hacks;
while (*walk) {
GST_DEBUG ("Using hack: %s", *walk);
walk++;
}
#endif
class_data->hacks = gst_omx_parse_hacks (hacks);
g_strfreev (hacks);
}
}
static gboolean
plugin_init (GstPlugin * plugin)
{
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 };
static const gchar *gst_omx_config_dir = GST_OMX_CONFIG_DIR;
GST_DEBUG_CATEGORY_INIT (gstomx_debug, "omx", 0, "gst-omx");
GST_DEBUG_CATEGORY_INIT (gst_omx_video_debug_category, "omxvideo", 0,
"gst-omx-video");
GST_DEBUG_CATEGORY_INIT (OMX_API_TRACE, "OMX_API_TRACE", 0,
"gst-omx performace");
/* 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) + 4);
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++] = (gchar *) gst_omx_config_dir;
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)) {
#ifdef USE_OMX_TARGET_GENERIC
GST_INFO ("No configuration file found; "
"ignore as gst-omx has been built with the generic target used only for testing");
#else
{
gchar *paths;
paths = g_strjoinv (":", config_dirs);
GST_ERROR
("Failed to load configuration file: %s (searched in: %s as per "
"GST_OMX_CONFIG_DIR environment variable, the xdg user config "
"directory (or XDG_CONFIG_HOME) and the system config directory "
"(or XDG_CONFIG_DIRS)", err->message, paths);
g_free (paths);
}
#endif /* USE_OMX_TARGET_GENERIC */
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_info.class_init = _class_init;
type_info.class_data = g_strdup (elements[i]);
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);
gst_element_register (plugin, elements[i], rank, subtype);
}
g_strfreev (elements);
done:
g_free (env_config_dir);
g_free (config_dirs);
return TRUE;
}
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)