diff options
author | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:20:51 +0100 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:20:51 +0100 |
commit | d2ed6de22829288c5cfd3a52b1da090e96d3329d (patch) | |
tree | ef84cba3610e96cc4c58359da3c8a69882aa03c9 /ilclient/ilclient.c | |
download | vdr-plugin-rpihddevice-0.0.1.tar.gz vdr-plugin-rpihddevice-0.0.1.tar.bz2 |
2013-09-27: Version 0.0.10.0.1
Initial prototype
- limitations:
- video codec hard coded to MPEG2, output on HDMI
- audio codec hard coded to MP3, output on phone jack
- tested:
- OSD
- SDTV live view and replay
- missing:
- dynamic switching between MPEG2 and H264 video codec
- trick speeds
- much more...
Diffstat (limited to 'ilclient/ilclient.c')
-rw-r--r-- | ilclient/ilclient.c | 1836 |
1 files changed, 1836 insertions, 0 deletions
diff --git a/ilclient/ilclient.c b/ilclient/ilclient.c new file mode 100644 index 0000000..da08ad0 --- /dev/null +++ b/ilclient/ilclient.c @@ -0,0 +1,1836 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief This API defines helper functions for writing IL clients. + * + * This file defines an IL client side library. This is useful when + * writing IL clients, since there tends to be much repeated and + * common code across both single and multiple clients. This library + * seeks to remove that common code and abstract some of the + * interactions with components. There is a wrapper around a + * component and tunnel, and some operations can be done on lists of + * these. The callbacks from components are handled, and specific + * events can be checked or waited for. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "interface/vmcs_host/vchost.h" + +#include "IL/OMX_Broadcom.h" +#include "ilclient.h" + +#define VCOS_LOG_CATEGORY (&ilclient_log_category) + +#ifndef ILCLIENT_THREAD_DEFAULT_STACK_SIZE +#define ILCLIENT_THREAD_DEFAULT_STACK_SIZE (6<<10) +#endif + +static VCOS_LOG_CAT_T ilclient_log_category; + +/****************************************************************************** +Static data and types used only in this file. +******************************************************************************/ + +struct _ILEVENT_T { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; + OMX_PTR pEventData; + struct _ILEVENT_T *next; +}; + +#define NUM_EVENTS 100 +struct _ILCLIENT_T { + ILEVENT_T *event_list; + VCOS_SEMAPHORE_T event_sema; + ILEVENT_T event_rep[NUM_EVENTS]; + + ILCLIENT_CALLBACK_T port_settings_callback; + void *port_settings_callback_data; + ILCLIENT_CALLBACK_T eos_callback; + void *eos_callback_data; + ILCLIENT_CALLBACK_T error_callback; + void *error_callback_data; + ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback; + void *fill_buffer_done_callback_data; + ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback; + void *empty_buffer_done_callback_data; + ILCLIENT_CALLBACK_T configchanged_callback; + void *configchanged_callback_data; +}; + +struct _COMPONENT_T { + OMX_HANDLETYPE comp; + ILCLIENT_CREATE_FLAGS_T flags; + VCOS_SEMAPHORE_T sema; + VCOS_EVENT_FLAGS_T event; + struct _COMPONENT_T *related; + OMX_BUFFERHEADERTYPE *out_list; + OMX_BUFFERHEADERTYPE *in_list; + char name[32]; + char bufname[32]; + unsigned int error_mask; + unsigned int private; + ILEVENT_T *list; + ILCLIENT_T *client; +}; + +#define random_wait() +static char *states[] = {"Invalid", "Loaded", "Idle", "Executing", "Pause", "WaitingForResources"}; + +typedef enum { + ILCLIENT_ERROR_UNPOPULATED = 0x1, + ILCLIENT_ERROR_SAMESTATE = 0x2, + ILCLIENT_ERROR_BADPARAMETER = 0x4 +} ILERROR_MASK_T; + +/****************************************************************************** +Static functions. +******************************************************************************/ + +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static void ilclient_lock_events(ILCLIENT_T *st); +static void ilclient_unlock_events(ILCLIENT_T *st); + +/****************************************************************************** +Global functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_init + * + * Description: Creates ilclient pointer + * + * Returns: pointer to client structure + ***********************************************************/ +ILCLIENT_T *ilclient_init() +{ + ILCLIENT_T *st = vcos_malloc(sizeof(ILCLIENT_T), "ilclient"); + int i; + + if (!st) + return NULL; + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_WARN); + vcos_log_register("ilclient", VCOS_LOG_CATEGORY); + + memset(st, 0, sizeof(ILCLIENT_T)); + + i = vcos_semaphore_create(&st->event_sema, "il:event", 1); + vc_assert(i == VCOS_SUCCESS); + + ilclient_lock_events(st); + st->event_list = NULL; + for (i=0; i<NUM_EVENTS; i++) + { + st->event_rep[i].eEvent = -1; // mark as unused + st->event_rep[i].next = st->event_list; + st->event_list = st->event_rep+i; + } + ilclient_unlock_events(st); + return st; +} + +/*********************************************************** + * Name: ilclient_destroy + * + * Description: frees client state + * + * Returns: void + ***********************************************************/ +void ilclient_destroy(ILCLIENT_T *st) +{ + vcos_semaphore_delete(&st->event_sema); + vcos_free(st); + vcos_log_unregister(VCOS_LOG_CATEGORY); +} + +/*********************************************************** + * Name: ilclient_set_port_settings_callback + * + * Description: sets the callback used when receiving port settings + * changed messages. The data field in the callback function will be + * the port index reporting the message. + * + * Returns: void + ***********************************************************/ +void ilclient_set_port_settings_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->port_settings_callback = func; + st->port_settings_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_eos_callback + * + * Description: sets the callback used when receiving eos flags. The + * data parameter in the callback function will be the port index + * reporting an eos flag. + * + * Returns: void + ***********************************************************/ +void ilclient_set_eos_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->eos_callback = func; + st->eos_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_error_callback + * + * Description: sets the callback used when receiving error events. + * The data parameter in the callback function will be the error code + * being reported. + * + * Returns: void + ***********************************************************/ +void ilclient_set_error_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->error_callback = func; + st->error_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_fill_buffer_done_callback + * + * Description: sets the callback used when receiving + * fill_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_fill_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->fill_buffer_done_callback = func; + st->fill_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_empty_buffer_done_callback + * + * Description: sets the callback used when receiving + * empty_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_empty_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->empty_buffer_done_callback = func; + st->empty_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_configchanged_callback + * + * Description: sets the callback used when a config changed + * event is received + * + * Returns: void + ***********************************************************/ +void ilclient_set_configchanged_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->configchanged_callback = func; + st->configchanged_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_create_component + * + * Description: initialises a component state structure and creates + * the IL component. + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name, + ILCLIENT_CREATE_FLAGS_T flags) +{ + OMX_CALLBACKTYPE callbacks; + OMX_ERRORTYPE error; + char component_name[128]; + int32_t status; + + *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp"); + if(!*comp) + return -1; + + memset(*comp, 0, sizeof(COMPONENT_T)); + +#define COMP_PREFIX "OMX.broadcom." + + status = vcos_event_flags_create(&(*comp)->event,"il:comp"); + vc_assert(status == VCOS_SUCCESS); + status = vcos_semaphore_create(&(*comp)->sema, "il:comp", 1); + vc_assert(status == VCOS_SUCCESS); + (*comp)->client = client; + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", name); + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", name); + vcos_snprintf(component_name, sizeof(component_name), "%s%s", COMP_PREFIX, name); + + (*comp)->flags = flags; + + callbacks.EventHandler = ilclient_event_handler; + callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error; + callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error; + + error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks); + + if (error == OMX_ErrorNone) + { + OMX_UUIDTYPE uid; + char name[128]; + OMX_VERSIONTYPE compVersion, specVersion; + + if(OMX_GetComponentVersion((*comp)->comp, name, &compVersion, &specVersion, &uid) == OMX_ErrorNone) + { + char *p = (char *) uid + strlen(COMP_PREFIX); + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", p); + (*comp)->name[sizeof((*comp)->name)-1] = 0; + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", p); + (*comp)->bufname[sizeof((*comp)->bufname)-1] = 0; + } + + if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS)) + { + OMX_PORT_PARAM_TYPE ports; + OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit}; + int i; + + ports.nSize = sizeof(OMX_PORT_PARAM_TYPE); + ports.nVersion.nVersion = OMX_VERSION; + + for(i=0; i<4; i++) + { + OMX_ERRORTYPE error = OMX_GetParameter((*comp)->comp, types[i], &ports); + if(error == OMX_ErrorNone) + { + uint32_t j; + for(j=0; j<ports.nPorts; j++) + { + if(flags & ILCLIENT_DISABLE_ALL_PORTS) + ilclient_disable_port(*comp, ports.nStartPortNumber+j); + + if(flags & ILCLIENT_OUTPUT_ZERO_BUFFERS) + { + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = ports.nStartPortNumber+j; + if(OMX_GetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef) == OMX_ErrorNone) + { + if(portdef.eDir == OMX_DirOutput && portdef.nBufferCountActual > 0) + { + portdef.nBufferCountActual = 0; + OMX_SetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef); + } + } + } + } + } + } + } + return 0; + } + else + { + vcos_event_flags_delete(&(*comp)->event); + vcos_semaphore_delete(&(*comp)->sema); + vcos_free(*comp); + *comp = NULL; + return -1; + } +} + +/*********************************************************** + * Name: ilclient_remove_event + * + * Description: Removes an event from a component event list. ignore1 + * and ignore2 are flags indicating whether to not match on nData1 and + * nData2 respectively. + * + * Returns: 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +int ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2) +{ + ILEVENT_T *cur, *prev; + uint32_t set; + ilclient_lock_events(st->client); + + cur = st->list; + prev = NULL; + + while (cur && !(cur->eEvent == eEvent && (ignore1 || cur->nData1 == nData1) && (ignore2 || cur->nData2 == nData2))) + { + prev = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ilclient_unlock_events(st->client); + return -1; + } + + if (prev == NULL) + st->list = cur->next; + else + prev->next = cur->next; + + // add back into spare list + cur->next = st->client->event_list; + st->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + // if we're removing an OMX_EventError or OMX_EventParamOrConfigChanged event, then clear the error bit from the eventgroup, + // since the user might have been notified through the error callback, and then + // can't clear the event bit - this will then cause problems the next time they + // wait for an error. + if(eEvent == OMX_EventError) + vcos_event_flags_get(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + else if(eEvent == OMX_EventParamOrConfigChanged) + vcos_event_flags_get(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR_CONSUME, 0, &set); + + ilclient_unlock_events(st->client); + return 0; +} + +/*********************************************************** + * Name: ilclient_state_transition + * + * Description: Transitions a null terminated list of IL components to + * a given state. All components are told to transition in a random + * order before any are checked for transition completion. + * + * Returns: void + ***********************************************************/ +void ilclient_state_transition(COMPONENT_T *list[], OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + int i, num; + uint32_t set; + + num=0; + while (list[num]) + num++; + + // if we transition the supplier port first, it will call freebuffer on the non + // supplier, which will correctly signal a port unpopulated error. We want to + // ignore these errors. + if (state == OMX_StateLoaded) + for (i=0; i<num; i++) + list[i]->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + for (i=0; i<num; i++) + list[i]->private = ((rand() >> 13) & 0xff)+1; + + for (i=0; i<num; i++) + { + // transition the components in a random order + int j, min = -1; + for (j=0; j<num; j++) + if (list[j]->private && (min == -1 || list[min]->private > list[j]->private)) + min = j; + + list[min]->private = 0; + + random_wait(); + //Clear error event for this component + vcos_event_flags_get(&list[min]->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + error = OMX_SendCommand(list[min]->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + } + + random_wait(); + + for (i=0; i<num; i++) + if(ilclient_wait_for_command_complete(list[i], OMX_CommandStateSet, state) < 0) + vc_assert(0); + + if (state == OMX_StateLoaded) + for (i=0; i<num; i++) + list[i]->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_teardown_tunnels + * + * Description: tears down a null terminated list of tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_teardown_tunnels(TUNNEL_T *tunnel) +{ + int i; + OMX_ERRORTYPE error; + + i=0;; + while (tunnel[i].source) + { + error = OMX_SetupTunnel(tunnel[i].source->comp, tunnel[i].source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SetupTunnel(tunnel[i].sink->comp, tunnel[i].sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + i++; + } +} + +/*********************************************************** + * Name: ilclient_disable_tunnel + * + * Description: disables a tunnel by disabling the ports. Allows + * ports to signal same state error if they were already disabled. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_tunnel(TUNNEL_T *tunnel) +{ + OMX_ERRORTYPE error; + + if(tunnel->source == 0 || tunnel->sink == 0) + return; + + tunnel->source->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortDisable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortDisable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortDisable, tunnel->source_port) < 0) + vc_assert(0); + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortDisable, tunnel->sink_port) < 0) + vc_assert(0); + + tunnel->source->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_enable_tunnel + * + * Description: enables a tunnel by enabling the ports + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_enable_tunnel(TUNNEL_T *tunnel) +{ + OMX_STATETYPE state; + OMX_ERRORTYPE error; + + ilclient_debug_output("ilclient: enable tunnel from %x/%d to %x/%d", + tunnel->source, tunnel->source_port, + tunnel->sink, tunnel->sink_port); + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortEnable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortEnable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + // to complete, the sink component can't be in loaded state + error = OMX_GetState(tunnel->sink->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded) + { + int ret = 0; + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0 || + OMX_SendCommand(tunnel->sink->comp, OMX_CommandStateSet, OMX_StateIdle, NULL) != OMX_ErrorNone || + (ret = ilclient_wait_for_command_complete_dual(tunnel->sink, OMX_CommandStateSet, OMX_StateIdle, tunnel->source)) < 0) + { + if(ret == -2) + { + // the error was reported fom the source component: clear this error and disable the sink component + ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port); + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + } + + ilclient_debug_output("ilclient: could not change component state to IDLE"); + ilclient_disable_port(tunnel->source, tunnel->source_port); + return -1; + } + } + else + { + if (ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0) + { + ilclient_debug_output("ilclient: could not change sink port %d to enabled", tunnel->sink_port); + + //Oops failed to enable the sink port + ilclient_disable_port(tunnel->source, tunnel->source_port); + //Clean up the port enable event from the source port. + ilclient_wait_for_event(tunnel->source, OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, tunnel->source_port, 0, + ILCLIENT_PORT_ENABLED | ILCLIENT_EVENT_ERROR, VCOS_EVENT_FLAGS_SUSPEND); + return -1; + } + } + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port) != 0) + { + ilclient_debug_output("ilclient: could not change source port %d to enabled", tunnel->source_port); + + //Failed to enable the source port + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + return -1; + } + + return 0; +} + + +/*********************************************************** + * Name: ilclient_flush_tunnels + * + * Description: flushes all ports used in a null terminated list of + * tunnels. max specifies the maximum number of tunnels to flush from + * the list, where max=0 means all tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_flush_tunnels(TUNNEL_T *tunnel, int max) +{ + OMX_ERRORTYPE error; + int i; + + i=0; + while (tunnel[i].source && (max == 0 || i < max)) + { + error = OMX_SendCommand(tunnel[i].source->comp, OMX_CommandFlush, tunnel[i].source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel[i].sink->comp, OMX_CommandFlush, tunnel[i].sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + ilclient_wait_for_event(tunnel[i].source, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].source_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + ilclient_wait_for_event(tunnel[i].sink, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].sink_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + i++; + } +} + + +/*********************************************************** + * Name: ilclient_return_events + * + * Description: Returns all events from a component event list to the + * list of unused event structures. + * + * Returns: void + ***********************************************************/ +void ilclient_return_events(COMPONENT_T *comp) +{ + ilclient_lock_events(comp->client); + while (comp->list) + { + ILEVENT_T *next = comp->list->next; + comp->list->next = comp->client->event_list; + comp->client->event_list = comp->list; + comp->list = next; + } + ilclient_unlock_events(comp->client); +} + +/*********************************************************** + * Name: ilclient_cleanup_components + * + * Description: frees all components from a null terminated list and + * deletes resources used in component state structure. + * + * Returns: void + ***********************************************************/ +void ilclient_cleanup_components(COMPONENT_T *list[]) +{ + int i; + OMX_ERRORTYPE error; + + i=0; + while (list[i]) + { + ilclient_return_events(list[i]); + if (list[i]->comp) + { + error = OMX_FreeHandle(list[i]->comp); + + vc_assert(error == OMX_ErrorNone); + } + i++; + } + + i=0; + while (list[i]) + { + vcos_event_flags_delete(&list[i]->event); + vcos_semaphore_delete(&list[i]->sema); + vcos_free(list[i]); + list[i] = NULL; + i++; + } +} + +/*********************************************************** + * Name: ilclient_change_component_state + * + * Description: changes the state of a single component. Note: this + * may not be suitable if the component is tunnelled and requires + * connected components to also change state. + * + * Returns: 0 on success, -1 on failure (note - trying to change to + * the same state which causes a OMX_ErrorSameState is treated as + * success) + ***********************************************************/ +int ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state) < 0) + { + ilclient_debug_output("ilclient: could not change component state to %d", state); + ilclient_remove_event(comp, OMX_EventError, 0, 1, 0, 1); + return -1; + } + return 0; +} + +/*********************************************************** + * Name: ilclient_disable_port + * + * Description: disables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + +/*********************************************************** + * Name: ilclient_enabled_port + * + * Description: enables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_enable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_enable_port_buffers + * + * Description: enables a port on a given component which requires + * buffers to be supplied by the client. + * + * Returns: void + ***********************************************************/ +int ilclient_enable_port_buffers(COMPONENT_T *comp, int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_BUFFERHEADERTYPE *list = NULL, **end = &list; + OMX_STATETYPE state; + int i; + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_FALSE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return -1; + + // check component is in the right state to accept buffers + error = OMX_GetState(comp->comp, &state); + if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause)) + return -1; + + // send the command + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + for (i=0; i != portdef.nBufferCountActual; i++) + { + unsigned char *buf; + if(ilclient_malloc) + buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + else + buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + + if(!buf) + break; + + error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf); + if(error != OMX_ErrorNone) + { + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + break; + } + end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate); + } + + // queue these buffers + vcos_semaphore_wait(&comp->sema); + + if(portdef.eDir == OMX_DirInput) + { + *end = comp->in_list; + comp->in_list = list; + } + else + { + *end = comp->out_list; + comp->out_list = list; + } + + vcos_semaphore_post(&comp->sema); + + if(i != portdef.nBufferCountActual || + ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + { + ilclient_disable_port_buffers(comp, portIndex, NULL, ilclient_free, private); + + // at this point the first command might have terminated with an error, which means that + // the port is disabled before the disable_port_buffers function is called, so we're left + // with the error bit set and an error event in the queue. Clear these now if they exist. + ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0); + + return -1; + } + + // success + return 0; +} + + +/*********************************************************** + * Name: ilclient_disable_port_buffers + * + * Description: disables a port on a given component which has + * buffers supplied by the client. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port_buffers(COMPONENT_T *comp, int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_BUFFERHEADERTYPE *list = bufferList; + OMX_BUFFERHEADERTYPE **head, *clist, *prev; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + int num; + + // get the buffers off the relevant queue + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_TRUE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return; + + num = portdef.nBufferCountActual; + + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + while(num > 0) + { + VCOS_UNSIGNED set; + + if(list == NULL) + { + vcos_semaphore_wait(&comp->sema); + + // take buffers for this port off the relevant queue + head = portdef.eDir == OMX_DirInput ? &comp->in_list : &comp->out_list; + clist = *head; + prev = NULL; + + while(clist) + { + if((portdef.eDir == OMX_DirInput ? clist->nInputPortIndex : clist->nOutputPortIndex) == portIndex) + { + OMX_BUFFERHEADERTYPE *pBuffer = clist; + + if(!prev) + clist = *head = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + else + clist = prev->pAppPrivate = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + + pBuffer->pAppPrivate = list; + list = pBuffer; + } + else + { + prev = clist; + clist = (OMX_BUFFERHEADERTYPE *) &(clist->pAppPrivate); + } + } + + vcos_semaphore_post(&comp->sema); + } + + while(list) + { + void *buf = list->pBuffer; + OMX_BUFFERHEADERTYPE *next = list->pAppPrivate; + + error = OMX_FreeBuffer(comp->comp, portIndex, list); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + num--; + list = next; + } + + if(num) + { + OMX_U32 mask = ILCLIENT_PORT_DISABLED | ILCLIENT_EVENT_ERROR; + mask |= (portdef.eDir == OMX_DirInput ? ILCLIENT_EMPTY_BUFFER_DONE : ILCLIENT_FILL_BUFFER_DONE); + + // also wait for command complete/error in case we didn't have all the buffers allocated + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, -1, &set); + + if((set & ILCLIENT_EVENT_ERROR) && ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0) >= 0) + return; + + if((set & ILCLIENT_PORT_DISABLED) && ilclient_remove_event(comp, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, portIndex, 0) >= 0) + return; + } + } + + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_setup_tunnel + * + * Description: creates a tunnel between components that require that + * ports be inititially disabled, then enabled after tunnel setup. If + * timeout is non-zero, it will initially wait until a port settings + * changes message has been received by the output port. If port + * streams are supported by the output port, the requested port stream + * will be selected. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout waiting for the parameter changed + * -2: an error was returned instead of parameter changed + * -3: no streams are available from this port + * -4: requested stream is not available from this port + * -5: the data format was not acceptable to the sink + ***********************************************************/ +int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout) +{ + OMX_ERRORTYPE error; + OMX_PARAM_U32TYPE param; + OMX_STATETYPE state; + int32_t status; + int enable_error; + + // source component must at least be idle, not loaded + error = OMX_GetState(tunnel->source->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded && ilclient_change_component_state(tunnel->source, OMX_StateIdle) < 0) + return -2; + + // wait for the port parameter changed from the source port + if(timeout) + { + status = ilclient_wait_for_event(tunnel->source, OMX_EventPortSettingsChanged, + tunnel->source_port, 0, -1, 1, + ILCLIENT_PARAMETER_CHANGED | ILCLIENT_EVENT_ERROR, timeout); + + if (status < 0) + { + ilclient_debug_output( + "ilclient: timed out waiting for port settings changed on port %d", tunnel->source_port); + return status; + } + } + + // disable ports + ilclient_disable_tunnel(tunnel); + + // if this source port uses port streams, we need to select one of them before proceeding + // if getparameter causes an error that's fine, nothing needs selecting + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = tunnel->source_port; + if (OMX_GetParameter(tunnel->source->comp, OMX_IndexParamNumAvailableStreams, ¶m) == OMX_ErrorNone) + { + if (param.nU32 == 0) + { + // no streams available + // leave the source port disabled, and return a failure + return -3; + } + if (param.nU32 <= portStream) + { + // requested stream not available + // no streams available + // leave the source port disabled, and return a failure + return -4; + } + + param.nU32 = portStream; + error = OMX_SetParameter(tunnel->source->comp, OMX_IndexParamActiveStream, ¶m); + vc_assert(error == OMX_ErrorNone); + } + + // now create the tunnel + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, tunnel->sink->comp, tunnel->sink_port); + + enable_error = 0; + + if (error != OMX_ErrorNone || (enable_error=ilclient_enable_tunnel(tunnel)) < 0) + { + // probably format not compatible + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + if(enable_error) + { + //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/ + ilclient_remove_event(tunnel->sink, OMX_EventError, 0, 1, 0, 1); + ilclient_remove_event(tunnel->source, OMX_EventError, 0, 1, 0, 1); + } + + ilclient_debug_output("ilclient: could not setup/enable tunnel (setup=0x%x,enable=%d)", + error, enable_error); + return -5; + } + + return 0; +} + +/*********************************************************** + * Name: ilclient_wait_for_event + * + * Description: waits for a given event to appear on a component event + * list. If not immediately present, will wait on that components + * event group for the given event flag. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout was received. + * -2: an error event was received. + * -3: a config change event was received. + ***********************************************************/ +int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2, + int event_flag, int suspend) +{ + int32_t status; + uint32_t set; + + while (ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) < 0) + { + // if we want to be notified of errors, check the list for an error now + // before blocking, the event flag may have been cleared already. + if(event_flag & ILCLIENT_EVENT_ERROR) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventError) + cur = cur->next; + + if(cur) + { + // clear error flag + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + ilclient_unlock_events(comp->client); + return -2; + } + + ilclient_unlock_events(comp->client); + } + // check for config change event if we are asked to be notified of that + if(event_flag & ILCLIENT_CONFIG_CHANGED) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventParamOrConfigChanged) + cur = cur->next; + + ilclient_unlock_events(comp->client); + + if(cur) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + status = vcos_event_flags_get(&comp->event, event_flag, VCOS_OR_CONSUME, + suspend, &set); + if (status != 0) + return -1; + if (set & ILCLIENT_EVENT_ERROR) + return -2; + if (set & ILCLIENT_CONFIG_CHANGED) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + return 0; +} + + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete_dual + * + * Description: Waits for an event signalling command completion. In + * this version we may also return failure if there is an error event + * that has terminated a command on a second component. + * + * Returns: 0 on success, -1 on failure of comp, -2 on failure of other + ***********************************************************/ +int ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2, COMPONENT_T *other) +{ + OMX_U32 mask = ILCLIENT_EVENT_ERROR; + int ret = 0; + + switch(command) { + case OMX_CommandStateSet: mask |= ILCLIENT_STATE_CHANGED; break; + case OMX_CommandPortDisable: mask |= ILCLIENT_PORT_DISABLED; break; + case OMX_CommandPortEnable: mask |= ILCLIENT_PORT_ENABLED; break; + default: return -1; + } + + if(other) + other->related = comp; + + while(1) + { + ILEVENT_T *cur, *prev = NULL; + VCOS_UNSIGNED set; + + ilclient_lock_events(comp->client); + + cur = comp->list; + while(cur && + !(cur->eEvent == OMX_EventCmdComplete && cur->nData1 == command && cur->nData2 == nData2) && + !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + { + prev = cur; + cur = cur->next; + } + + if(cur) + { + if(prev == NULL) + comp->list = cur->next; + else + prev->next = cur->next; + + // work out whether this was a success or a fail event + ret = cur->eEvent == OMX_EventCmdComplete || cur->nData1 == OMX_ErrorSameState ? 0 : -1; + + if(cur->eEvent == OMX_EventError) + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + // add back into spare list + cur->next = comp->client->event_list; + comp->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + ilclient_unlock_events(comp->client); + break; + } + else if(other != NULL) + { + // check the other component for an error event that terminates a command + cur = other->list; + while(cur && !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + cur = cur->next; + + if(cur) + { + // we don't remove the event in this case, since the user + // can confirm that this event errored by calling wait_for_command on the + // other component + + ret = -2; + ilclient_unlock_events(comp->client); + break; + } + } + + ilclient_unlock_events(comp->client); + + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, VCOS_SUSPEND, &set); + } + + if(other) + other->related = NULL; + + return ret; +} + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete + * + * Description: Waits for an event signalling command completion. + * + * Returns: 0 on success, -1 on failure. + ***********************************************************/ +int ilclient_wait_for_command_complete(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2) +{ + return ilclient_wait_for_command_complete_dual(comp, command, nData2, NULL); +} + +/*********************************************************** + * Name: ilclient_get_output_buffer + * + * Description: Returns an output buffer returned from a component + * using the OMX_FillBufferDone callback from the output list for the + * given component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + VCOS_UNSIGNED set; + + do { + vcos_semaphore_wait(&comp->sema); + ret = comp->out_list; + while(ret != NULL && ret->nOutputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->out_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_get_input_buffer + * + * Description: Returns an input buffer return from a component using + * the OMX_EmptyBufferDone callback from the output list for the given + * component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + + do { + VCOS_UNSIGNED set; + + vcos_semaphore_wait(&comp->sema); + ret = comp->in_list; + while(ret != NULL && ret->nInputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->in_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_debug_output + * + * Description: prints a varg message to the log or the debug screen + * under win32 + * + * Returns: void + ***********************************************************/ +void ilclient_debug_output(char *format, ...) +{ + va_list args; + + va_start(args, format); + vcos_vlog_info(format, args); + va_end(args); +} + +/****************************************************************************** +Static functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_lock_events + * + * Description: locks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_lock_events(ILCLIENT_T *st) +{ + vcos_semaphore_wait(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_unlock_events + * + * Description: unlocks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_unlock_events(ILCLIENT_T *st) +{ + vcos_semaphore_post(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_event_handler + * + * Description: event handler passed to core to use as component + * callback + * + * Returns: success + ***********************************************************/ +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + ILEVENT_T *event; + OMX_ERRORTYPE error = OMX_ErrorNone; + + ilclient_lock_events(st->client); + + // go through the events on this component and remove any duplicates in the + // existing list, since the client probably doesn't need them. it's better + // than asserting when we run out. + event = st->list; + while(event != NULL) + { + ILEVENT_T **list = &(event->next); + while(*list != NULL) + { + if((*list)->eEvent == event->eEvent && + (*list)->nData1 == event->nData1 && + (*list)->nData2 == event->nData2) + { + // remove this duplicate + ILEVENT_T *rem = *list; + ilclient_debug_output("%s: removing %d/%d/%d", st->name, event->eEvent, event->nData1, event->nData2); + *list = rem->next; + rem->eEvent = -1; + rem->next = st->client->event_list; + st->client->event_list = rem; + } + else + list = &((*list)->next); + } + + event = event->next; + } + + vc_assert(st->client->event_list); + event = st->client->event_list; + + switch (eEvent) { + case OMX_EventCmdComplete: + switch (nData1) { + case OMX_CommandStateSet: + ilclient_debug_output("%s: callback state changed (%s)", st->name, states[nData2]); + vcos_event_flags_set(&st->event, ILCLIENT_STATE_CHANGED, VCOS_OR); + break; + case OMX_CommandPortDisable: + ilclient_debug_output("%s: callback port disable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_DISABLED, VCOS_OR); + break; + case OMX_CommandPortEnable: + ilclient_debug_output("%s: callback port enable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_ENABLED, VCOS_OR); + break; + case OMX_CommandFlush: + ilclient_debug_output("%s: callback port flush %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_FLUSH, VCOS_OR); + break; + case OMX_CommandMarkBuffer: + ilclient_debug_output("%s: callback mark buffer %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_MARKED_BUFFER, VCOS_OR); + break; + default: + vc_assert(0); + } + break; + case OMX_EventError: + { + // check if this component failed a command, and we have to notify another command + // of this failure + if(nData2 == 1 && st->related != NULL) + vcos_event_flags_set(&st->related->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + + error = nData1; + switch (error) { + case OMX_ErrorPortUnpopulated: + if (st->error_mask & ILCLIENT_ERROR_UNPOPULATED) + { + ilclient_debug_output("%s: ignore error: port unpopulated (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: port unpopulated %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorSameState: + if (st->error_mask & ILCLIENT_ERROR_SAMESTATE) + { + ilclient_debug_output("%s: ignore error: same state (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: same state %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadParameter: + if (st->error_mask & ILCLIENT_ERROR_BADPARAMETER) + { + ilclient_debug_output("%s: ignore error: bad parameter (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: bad parameter %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorIncorrectStateTransition: + ilclient_debug_output("%s: incorrect state transition %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadPortIndex: + ilclient_debug_output("%s: bad port index %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorStreamCorrupt: + ilclient_debug_output("%s: stream corrupt %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorInsufficientResources: + ilclient_debug_output("%s: insufficient resources %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorUnsupportedSetting: + ilclient_debug_output("%s: unsupported setting %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorOverflow: + ilclient_debug_output("%s: overflow %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDiskFull: + ilclient_debug_output("%s: disk full %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorMaxFileSize: + ilclient_debug_output("%s: max file size %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorDrmUnauthorised: + ilclient_debug_output("%s: drm file is unauthorised %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmExpired: + ilclient_debug_output("%s: drm file has expired %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmGeneral: + ilclient_debug_output("%s: drm library error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + default: + vc_assert(0); + ilclient_debug_output("%s: unexpected error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + } + } + break; + case OMX_EventBufferFlag: + ilclient_debug_output("%s: buffer flag %d/%x", st->name, nData1, nData2); + if (nData2 & OMX_BUFFERFLAG_EOS) + { + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_FLAG_EOS, VCOS_OR); + nData2 = OMX_BUFFERFLAG_EOS; + } + else + vc_assert(0); + break; + case OMX_EventPortSettingsChanged: + ilclient_debug_output("%s: port settings changed %d", st->name, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_PARAMETER_CHANGED, VCOS_OR); + break; + case OMX_EventMark: + ilclient_debug_output("%s: buffer mark %p", st->name, pEventData); + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_MARK, VCOS_OR); + break; + case OMX_EventParamOrConfigChanged: + ilclient_debug_output("%s: param/config 0x%X on port %d changed", st->name, nData2, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR); + break; + default: + vc_assert(0); + break; + } + + if (event) + { + // fill in details + event->eEvent = eEvent; + event->nData1 = nData1; + event->nData2 = nData2; + event->pEventData = pEventData; + + // remove from top of spare list + st->client->event_list = st->client->event_list->next; + + // put at head of component event queue + event->next = st->list; + st->list = event; + } + ilclient_unlock_events(st->client); + + // now call any callbacks without the event lock so the client can + // remove the event in context + switch(eEvent) { + case OMX_EventError: + if(event && st->client->error_callback) + st->client->error_callback(st->client->error_callback_data, st, error); + break; + case OMX_EventBufferFlag: + if ((nData2 & OMX_BUFFERFLAG_EOS) && st->client->eos_callback) + st->client->eos_callback(st->client->eos_callback_data, st, nData1); + break; + case OMX_EventPortSettingsChanged: + if (st->client->port_settings_callback) + st->client->port_settings_callback(st->client->port_settings_callback_data, st, nData1); + break; + case OMX_EventParamOrConfigChanged: + if (st->client->configchanged_callback) + st->client->configchanged_callback(st->client->configchanged_callback_data, st, nData2); + break; + default: + // ignore + break; + } + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: empty buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the same order + list = st->in_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->in_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR); + + if (st->client->empty_buffer_done_callback) + st->client->empty_buffer_done_callback(st->client->empty_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: fill buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the correct order + list = st->out_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->out_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR); + + if (st->client->fill_buffer_done_callback) + st->client->fill_buffer_done_callback(st->client->fill_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + + + +OMX_HANDLETYPE ilclient_get_handle(COMPONENT_T *comp) +{ + vcos_assert(comp); + return comp->comp; +} + + +static struct { + OMX_PORTDOMAINTYPE dom; + int param; +} port_types[] = { + { OMX_PortDomainVideo, OMX_IndexParamVideoInit }, + { OMX_PortDomainAudio, OMX_IndexParamAudioInit }, + { OMX_PortDomainImage, OMX_IndexParamImageInit }, + { OMX_PortDomainOther, OMX_IndexParamOtherInit }, +}; + +int ilclient_get_port_index(COMPONENT_T *comp, OMX_DIRTYPE dir, OMX_PORTDOMAINTYPE type, int index) +{ + uint32_t i; + // for each possible port type... + for (i=0; i<sizeof(port_types)/sizeof(port_types[0]); i++) + { + if ((port_types[i].dom == type) || (type == (OMX_PORTDOMAINTYPE) -1)) + { + OMX_PORT_PARAM_TYPE param; + OMX_ERRORTYPE error; + uint32_t j; + + param.nSize = sizeof(param); + param.nVersion.nVersion = OMX_VERSION; + error = OMX_GetParameter(ILC_GET_HANDLE(comp), port_types[i].param, ¶m); + assert(error == OMX_ErrorNone); + + // for each port of this type... + for (j=0; j<param.nPorts; j++) + { + int port = param.nStartPortNumber+j; + + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(portdef); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = port; + + error = OMX_GetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamPortDefinition, &portdef); + assert(error == OMX_ErrorNone); + + if (portdef.eDir == dir) + { + if (index-- == 0) + return port; + } + } + } + } + return -1; +} + +int ilclient_suggest_bufsize(COMPONENT_T *comp, OMX_U32 nBufSizeHint) +{ + OMX_PARAM_BRCMOUTPUTBUFFERSIZETYPE param; + OMX_ERRORTYPE error; + + param.nSize = sizeof(param); + param.nVersion.nVersion = OMX_VERSION; + param.nBufferSize = nBufSizeHint; + error = OMX_SetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamBrcmOutputBufferSize, + ¶m); + assert(error == OMX_ErrorNone); + + return (error == OMX_ErrorNone) ? 0 : -1; +} + +unsigned int ilclient_stack_size(void) +{ + return ILCLIENT_THREAD_DEFAULT_STACK_SIZE; +} + |