/* * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. * Author: Sebastian Dröge , Collabora Ltd. * Copyright (C) 2013, Collabora Ltd. * Author: Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #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 (¶m); g_strlcpy ((gchar *) param.cRole, component_role, sizeof (param.cRole)); err = gst_omx_component_set_parameter (comp, OMX_IndexParamStandardComponentRole, ¶m); 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 #ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS {OMX_ALG_BUFFERFLAG_TOP_FIELD, "top-field"}, {OMX_ALG_BUFFERFLAG_BOT_FIELD, "bottom-field"}, #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 ""; 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)