diff --git a/.gitignore b/.gitignore index 39dca49..54f360c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +#hello_video +*.a +*.bin + omxplayer-dist.tgz* # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 6e118a0..e0f8a74 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ # pi_video_looper Application to turn your Raspberry Pi into a dedicated looping video playback device, good for art installations, information displays, or just playing cat videos all day. + +# Raspberry Pi hello video +Fork of hello_video example from Raspberry Pi sample code that provides seamless looping of a raw H264 video stream. Original code is available at: https://github.com/raspberrypi/userland/tree/master/host_applications/linux/apps/hello_pi + +Build by executing `hv_rebuild.sh`. + +Install by executing `sudo make install` inside hello_video directory. + +Run by executing `hello_video.bin`. With no parameters the usage will be printed. diff --git a/install.sh b/install.sh index 17aa9eb..de20b8c 100755 --- a/install.sh +++ b/install.sh @@ -12,13 +12,18 @@ fi echo "Installing dependencies..." echo "==========================" apt-get update -apt-get -y install build-essential python-dev python-pip python-pygame supervisor git omxplayer +apt-get -y install build-essential python-dev python-pip python-pygame supervisor git omxplayer samba samba-common-bin echo "Installing hello_video..." echo "=========================" -git clone https://github.com/adafruit/pi_hello_video.git cd pi_hello_video -./rebuild.sh + +make -C libs/ilclient clean +make -C hello_video clean + +make -C libs/ilclient +make -C hello_video + cd hello_video make install cd ../.. diff --git a/pi_hello_video/Makefile.include b/pi_hello_video/Makefile.include new file mode 100644 index 0000000..ec3202a --- /dev/null +++ b/pi_hello_video/Makefile.include @@ -0,0 +1,29 @@ + +CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi + +LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L../libs/ilclient -L../libs/vgfont + +INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./ -I../libs/ilclient -I../libs/vgfont + +all: $(BIN) $(LIB) + +%.o: %.c + @rm -f $@ + $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.o: %.cpp + @rm -f $@ + $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.bin: $(OBJS) + $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic + +%.a: $(OBJS) + $(AR) r $@ $^ + +clean: + for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done + @rm -f $(BIN) $(LIB) + +install: + cp $(BIN) /usr/local/bin/ diff --git a/pi_hello_video/hello_video/Makefile b/pi_hello_video/hello_video/Makefile new file mode 100644 index 0000000..be21fd5 --- /dev/null +++ b/pi_hello_video/hello_video/Makefile @@ -0,0 +1,6 @@ +OBJS=video.o +BIN=hello_video.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/pi_hello_video/hello_video/README b/pi_hello_video/hello_video/README new file mode 100644 index 0000000..c4922d9 --- /dev/null +++ b/pi_hello_video/hello_video/README @@ -0,0 +1 @@ +The video clip test.h264 is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org diff --git a/pi_hello_video/hello_video/video.c b/pi_hello_video/hello_video/video.c new file mode 100644 index 0000000..ae87319 --- /dev/null +++ b/pi_hello_video/hello_video/video.c @@ -0,0 +1,246 @@ +/* +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. +*/ + +// Video deocode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +static int video_decode_test(char *filename, int loop) +{ + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + FILE *in; + int status = 0; + unsigned int data_len = 0; + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if((in = fopen(filename, "rb")) == NULL) + return -2; + + if((client = ilclient_init()) == NULL) + { + fclose(in); + return -3; + } + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return -4; + } + + // create video_decode + if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create video_render + if(status == 0 && ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[1] = video_render; + + // create clock + if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, video_render, 90); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if(status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to video_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + ilclient_change_component_state(video_render, OMX_StateExecuting); + } + if(!data_len) { + // Finished reading the file, either loop or exit. + if (loop) { + fseek(in, 0, SEEK_SET); + } + else { + break; + } + } + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // wait for EOS from render + ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0, + ILCLIENT_BUFFER_FLAG_EOS, 10000); + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + fclose(in); + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return status; +} + +void error_usage(char* name) { + printf("Usage: %s [--loop] \n", name); + exit(1); +} + +int main (int argc, char **argv) +{ + int loop = 0; + + if (argc < 2 || argc > 3) { + error_usage(argv[0]); + } + // Check optional parameter. + if (argc == 3) { + // Check for loop parameter. + if (strcmp(argv[1], "--loop") == 0) { + loop = 1; + } + // Error unknown parameter. + else { + error_usage(argv[0]); + } + } + bcm_host_init(); + return video_decode_test(argv[argc-1], loop); +} + + diff --git a/pi_hello_video/libs/ilclient/Makefile b/pi_hello_video/libs/ilclient/Makefile new file mode 100644 index 0000000..82979e3 --- /dev/null +++ b/pi_hello_video/libs/ilclient/Makefile @@ -0,0 +1,5 @@ +OBJS=ilclient.o ilcore.o +LIB=libilclient.a + +include ../../Makefile.include + diff --git a/pi_hello_video/libs/ilclient/ilclient.c b/pi_hello_video/libs/ilclient/ilclient.c new file mode 100644 index 0000000..da08ad0 --- /dev/null +++ b/pi_hello_video/libs/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 +#include +#include +#include +#include +#include + +#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; ievent_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; jcomp, 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; ierror_mask |= ILCLIENT_ERROR_UNPOPULATED; + for (i=0; iprivate = ((rand() >> 13) & 0xff)+1; + + for (i=0; iprivate && (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; ierror_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; iILCLIENT_T structure encapsulates the state needed for the IL + * Client API. It contains a set of callback functions used to + * communicate with the user. It also includes a linked list of free + * event structures. + ***********************************************************/ +typedef struct _ILCLIENT_T ILCLIENT_T; + + +/** + * Each ILEVENT_T structure stores the result of an EventHandler + * callback from a component, storing the event message type and any + * parameters returned. + ***********************************************************/ +typedef struct _ILEVENT_T ILEVENT_T; + + + +struct _COMPONENT_T; + +/** + * The COMPONENT_T structure represents an IL component, + * together with the necessary extra information required by the IL + * Client API. This structure stores the handle to the OMX component, + * as well as the event list containing all events sent by this + * component. The component state structure also holds a pair of + * buffer queues, for input and output buffers returned to the client + * by the FillBufferDone and EmptyBufferDone + * callbacks. As some operations result in error callbacks that can + * be ignored, an error mask is maintained to allow some errors to be + * ignored. A pointer to the client state structure is also added. + ***********************************************************/ +typedef struct _COMPONENT_T COMPONENT_T; + + +/** + * The generic callback function is used for communicating events from + * a particular component to the user. + * + * @param userdata The data returned from when the callback was registered. + * + * @param comp The component structure representing the component that + * originated this event. + * + * @param data The relevant data field from the event. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_CALLBACK_T)(void *userdata, COMPONENT_T *comp, OMX_U32 data); + + +/** + * The buffer callback function is used for indicating that a + * component has returned a buffer on a port using client buffer + * communication. + * + * @param data The data returned from when the callback was registered. + * + * @param comp The component from which the buffer originated. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_BUFFER_CALLBACK_T)(void *data, COMPONENT_T *comp); + + +/** + * The malloc function is passed into + * ilclient_enable_port_buffers() and used for allocating the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() call. + * + * @param size Size in bytes of the requested memory region. + * + * @param align Alignment requirement in bytes for the base memory address. + * + * @param description Text description of the memory being allocated. + * + * @return The memory address on success, NULL on failure. + ***********************************************************/ +typedef void *(*ILCLIENT_MALLOC_T)(void *userdata, VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); + + +/** + * The free function is passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers() and used for freeing the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers(). + * + * @param pointer Memory address to free, that was previously returned + * from ILCLIENT_MALLOC_T function. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_FREE_T)(void *userdata, void *pointer); + + +/** + * The event mask enumeration describes the possible events that the + * user can ask to wait for when waiting for a particular event. + ***********************************************************/ +typedef enum { + ILCLIENT_EMPTY_BUFFER_DONE = 0x1, /**< Set when a buffer is + returned from an input + port */ + + ILCLIENT_FILL_BUFFER_DONE = 0x2, /**< Set when a buffer is + returned from an output + port */ + + ILCLIENT_PORT_DISABLED = 0x4, /**< Set when a port indicates + it has completed a disable + command. */ + + ILCLIENT_PORT_ENABLED = 0x8, /**< Set when a port indicates + is has completed an enable + command. */ + + ILCLIENT_STATE_CHANGED = 0x10, /**< Set when a component + indicates it has completed + a state change command. */ + + ILCLIENT_BUFFER_FLAG_EOS = 0x20, /**< Set when a port signals + an EOS event. */ + + ILCLIENT_PARAMETER_CHANGED = 0x40, /**< Set when a port signals a + port settings changed + event. */ + + ILCLIENT_EVENT_ERROR = 0x80, /**< Set when a component + indicates an error. */ + + ILCLIENT_PORT_FLUSH = 0x100, /**< Set when a port indicates + is has completed a flush + command. */ + + ILCLIENT_MARKED_BUFFER = 0x200, /**< Set when a port indicates + it has marked a buffer. */ + + ILCLIENT_BUFFER_MARK = 0x400, /**< Set when a port indicates + it has received a buffer + mark. */ + + ILCLIENT_CONFIG_CHANGED = 0x800 /**< Set when a config parameter + changed. */ +} ILEVENT_MASK_T; + + +/** + * On component creation the user can set flags to control the + * creation of that component. + ***********************************************************/ +typedef enum { + ILCLIENT_FLAGS_NONE = 0x0, /**< Used if no flags are + set. */ + + ILCLIENT_ENABLE_INPUT_BUFFERS = 0x1, /**< If set we allow the + client to communicate with + input ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_ENABLE_OUTPUT_BUFFERS = 0x2, /**< If set we allow the + client to communicate with + output ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_DISABLE_ALL_PORTS = 0x4, /**< If set we disable all + ports on creation. */ + + ILCLIENT_HOST_COMPONENT = 0x8, /**< Create a host component. + The default host ilcore + only can create host components + by being locally hosted + so should only be used for testing + purposes. */ + + ILCLIENT_OUTPUT_ZERO_BUFFERS = 0x10 /**< All output ports will have + nBufferCountActual set to zero, + if supported by the component. */ +} ILCLIENT_CREATE_FLAGS_T; + + +/** + * \brief This structure represents a tunnel in the OpenMAX IL API. + * + * Some operations in this API act on a tunnel, so the tunnel state + * structure (TUNNEL_T) is a convenient store of the source and sink + * of the tunnel. For each, a pointer to the relevant component state + * structure and the port index is stored. + ***********************************************************/ +typedef struct { + COMPONENT_T *source; /**< The source component */ + int source_port; /**< The output port index on the source component */ + COMPONENT_T *sink; /**< The sink component */ + int sink_port; /**< The input port index on the sink component */ +} TUNNEL_T; + + +/** + * The set_tunnel macro is a useful function that initialises a + * TUNNEL_T structure. + ***********************************************************/ +#define set_tunnel(t,a,b,c,d) do {TUNNEL_T *_ilct = (t); \ + _ilct->source = (a); _ilct->source_port = (b); \ + _ilct->sink = (c); _ilct->sink_port = (d);} while(0) + +/** + * For calling OpenMAX IL methods directory, we need to access the + * OMX_HANDLETYPE corresponding to the COMPONENT_T structure. This + * macro enables this while keeping the COMPONENT_T structure opaque. + * The parameter x should be of the type *COMPONENT_T. + ***********************************************************/ +#define ILC_GET_HANDLE(x) ilclient_get_handle(x) + +/** + * An IL Client structure is created by the ilclient_init() + * method. This structure is used when creating components, but + * otherwise is not needed in other API functions as a pointer to this + * structure is maintained in the COMPONENT_T structure. + * + * @return pointer to client structure + ***********************************************************/ +VCHPRE_ ILCLIENT_T VCHPOST_ *ilclient_init(void); + +/** + * When all components have been deleted, the IL Client structure can + * be destroyed by calling the ilclient_destroy() function. + * + * @param handle The client handle. After calling this function, this + * handle should not be used. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_destroy(ILCLIENT_T *handle); + +/** + * The ilclient_set_port_settings_callback() function registers a + * callback to be used when the OMX_EventPortSettingsChanged event is + * received. When the event is received, a pointer to the component + * structure and port index is returned by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_port_settings_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_eos_callback() function registers a callback to be + * used when the OMX_EventBufferFlag is received with the + * OMX_BUFFERFLAG_EOS flag set. When the event is received, a pointer + * to the component structure and port index is returned by the + * callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_eos_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_error_callback() function registers a callback to be + * used when the OMX_EventError is received from a component. When + * the event is received, a pointer to the component structure and the + * error code are reported by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_error_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_configchanged_callback() function + * registers a callback to be used when an + * OMX_EventParamOrConfigChanged event occurs. When the + * event is received, a pointer to the component structure and the + * index value that has changed are reported by the callback. The + * user may then use an OMX_GetConfig call with the index + * as specified to retrieve the updated information. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_configchanged_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + + +/** + * The ilclient_set_fill_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an output port using the + * OMX_FillBuffer call is returned with the OMX_FillBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_output_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_fill_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_empty_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an input port using the + * OMX_EmptyBuffer call is returned with the OMX_EmptyBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_input_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_empty_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + + +/** + * Components are created using the ilclient_create_component() + * function. + * + * @param handle The client handle + * + * @param comp On successful creation, the component structure pointer + * will be written back into comp. + * + * @param name The name of the component to be created. Component + * names as provided are automatically prefixed with + * "OMX.broadcom." before passing to the IL core. The name + * provided will also be used in debugging messages added about this + * component. + * + * @param flags The client can specify some creation behaviour by using + * the flags field. The meaning of each flag is defined + * by the ILCLIENT_CREATE_FLAGS_T type. + * + * @return 0 on success, -1 on failure + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_create_component(ILCLIENT_T *handle, + COMPONENT_T **comp, + char *name, + ILCLIENT_CREATE_FLAGS_T flags); + +/** + * The ilclient_cleanup_components() function deallocates all + * state associated with components and frees the OpenMAX component + * handles. All tunnels connecting components should have been torn + * down explicitly, and all components must be in loaded state. + * + * @param list A null-terminated list of component pointers to be + * deallocated. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_cleanup_components(COMPONENT_T *list[]); + + +/** + * The ilclient_change_component_state() function changes the + * state of an individual component. This will trigger the state + * change, and also wait for that state change to be completed. It + * should not be called if this state change has dependencies on other + * components also changing states. Trying to change a component to + * its current state is treated as success. + * + * @param comp The component to change. + * + * @param state The new state to transition to. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_change_component_state(COMPONENT_T *comp, + OMX_STATETYPE state); + + +/** + * The ilclient_state_transition() function transitions a set of + * components that need to perform a simultaneous state transition; + * for example, when two components are tunnelled and the buffer + * supplier port needs to allocate and pass buffers to a non-supplier + * port. All components are sent a command to change state, then the + * function will wait for all components to signal that they have + * changed state. + * + * @param list A null-terminated list of component pointers. + * + * @param state The new state to which to transition all components. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_state_transition(COMPONENT_T *list[], + OMX_STATETYPE state); + + +/** + * The ilclient_disable_port() function disables a port on a + * given component. This function sends the disable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * sucess. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_port(COMPONENT_T *comp, + int portIndex); + + +/** + * The ilclient_enable_port() function enables a port on a + * given component. This function sends the enable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * sucess. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_enable_port(COMPONENT_T *comp, + int portIndex); + + + +/** + * The ilclient_enable_port_buffers() function enables a port + * in base profile mode on a given component. The port is not + * tunneled, so requires buffers to be allocated. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param ilclient_malloc This function will be used to allocate + * buffer payloads. If NULL then + * vcos_malloc_aligned will be used. + * + * @param ilclient_free If an error occurs, this function is used to + * free previously allocated payloads. If NULL then + * vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_malloc and ilclient_free, if these + * arguments are not NULL. + * + * @return 0 indicates success. -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_port_buffers(COMPONENT_T *comp, + int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * The ilclient_disable_port_buffers() function disables a + * port in base profile mode on a given component. The port is not + * tunneled, and has been supplied with buffers by the client. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param bufferList A list of buffers, using pAppPrivate + * as the next pointer that were allocated on this port, and currently + * held by the application. If buffers on this port are held by IL + * client or the component then these are automatically freed. + * + * @param ilclient_free This function is used to free the buffer payloads. + * If NULL then vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_free. + * + * @return void + */ +VCHPRE_ void VCHPOST_ ilclient_disable_port_buffers(COMPONENT_T *comp, + int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * With a populated tunnel structure, the + * ilclient_setup_tunnel() function connects the tunnel. It + * first transitions the source component to idle if currently in + * loaded state, and then optionally checks the source event list for + * a port settings changed event from the source port. If this event + * is not in the event queue then this function optionally waits for + * it to arrive. To disable this check for the port settings changed + * event, set timeout to zero. + * + * Both ports are then disabled, and the source port is inspected for + * a port streams parameter. If this is supported, then the + * portStream argument is used to select which port stream + * to use. The two ports are then tunnelled using the + * OMX_SetupTunnel function. If this is successful, then + * both ports are enabled. Note that for disabling and enabling the + * tunnelled ports, the functions ilclient_disable_tunnel() + * and ilclient_enable_tunnel() are used, so the relevant + * documentation for those functions applies here. + * + * @param tunnel The tunnel structure representing the tunnel to + * set up. + * + * @param portStream If port streams are supported on the output port + * of the tunnel, then this parameter indicates the port stream to + * select on this port. + * + * @param timeout The time duration in milliseconds to wait for the + * output port to signal a port settings changed event before + * returning a timeout failure. If this is 0, then we do not check + * for a port settings changed before setting up the tunnel. + * + * @return 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 + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_setup_tunnel(TUNNEL_T *tunnel, + unsigned int portStream, + int timeout); + + +/** + * The ilclient_disable_tunnel() function disables both ports listed in + * the tunnel structure. It will send a port disable command to each + * port, then waits for both to indicate they have completed the + * transition. The errors OMX_ErrorPortUnpopulated and + * OMX_ErrorSameState are both ignored by this function; the former + * since the first port to disable may deallocate buffers before the + * second port has been disabled, leading to the second port reporting + * the unpopulated error. + * + * @param tunnel The tunnel to disable. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_enable_tunnel() function enables both ports listed in + * the tunnel structure. It will first send a port enable command to + * each port. It then checks whether the sink component is not in + * loaded state - if so, the function waits for both ports to complete + * the requested port enable. If the sink component was in loaded + * state, then this component is transitioned to idle to allow the + * ports to exchange buffers and enable the ports. This is since + * typically this function is used when creating a tunnel between two + * components, where the source component is processing data to enable + * it to report the port settings changed event, and the sink port has + * yet to be used. Before transitioning the sink component to idle, + * this function waits for the sink port to be enabled - since the + * component is in loaded state, this will happen quickly. If the + * transition to idle fails, the sink component is transitioned back + * to loaded and the source port disabled. If the transition + * succeeds, the function then waits for the source port to complete + * the requested port enable. + * + * @param tunnel The tunnel to enable. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_flush_tunnels() function will flush a number of tunnels + * from the list of tunnels presented. For each tunnel that is to be + * flushed, both source and sink ports are sent a flush command. The + * function then waits for both ports to report they have completed + * the flush operation. + * + * @param tunnel List of tunnels. The list must be terminated with a + * tunnel structure with NULL component entries. + * + * @param max The maximum number of tunnels to flush from the list. + * A value of 0 indicates that all tunnels in the list are flushed. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_flush_tunnels(TUNNEL_T *tunnel, + int max); + + +/** + * The ilclient_teardown_tunnels() function tears down all tunnels in + * the list of tunnels presented. For each tunnel in the list, the + * OMX_SetupTunnel is called on the source port and on the sink port, + * where for both calls the destination component is NULL and the + * destination port is zero. The VMCSX IL implementation requires + * that all tunnels are torn down in this manner before components are + * freed. + * + * @param tunnels List of tunnels to teardown. The list must be + * terminated with a tunnel structure with NULL component entries. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_teardown_tunnels(TUNNEL_T *tunnels); + + +/** + * The ilclient_get_output_buffer() function returns a buffer + * that was sent to an output port and that has been returned from a + * component using the OMX_FillBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component that the buffer + * was returned from. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return Pointer to buffer if available, otherwise NULL. + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_output_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_get_input_buffer() function returns a buffer + * that was sent to an input port and that has been returned from a + * component using the OMX_EmptyBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component from which the buffer + * was returned. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return pointer to buffer if available, otherwise NULL + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_input_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_remove_event() function queries the event list for the + * given component, matching against the given criteria. If a matching + * event is found, it is removed and added to the free event list. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @return 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_remove_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2); + + +/** + * The ilclient_return_events() function removes all events + * from a component event list and adds them to the IL client free + * event list. This function is automatically called when components + * are freed. + * + * @param comp The component from which all events should be moved to + * the free list. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_return_events(COMPONENT_T *comp); + + +/** + * The ilclient_wait_for_event() function is similar to + * ilclient_remove_event(), but allows the caller to block until that + * event arrives. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @param event_flag Specifies a bitfield of IL client events to wait + * for, given in ILEVENT_MASK_T. If any of these events + * are signalled by the component, the event list is then re-checked + * for a matching event. If the ILCLIENT_EVENT_ERROR bit + * is included, and an error is signalled by the component, then the + * function will return an error code. If the + * ILCLIENT_CONFIG_CHANGED bit is included, and this bit is + * signalled by the component, then the function will return an error + * code. + * + * @param timeout Specifies how long to block for in milliseconds + * before returning a failure. + * + * @return 0 indicates success, a matching event has been removed from + * the component's event queue. A negative return indicates failure, + * in this case no events have been removed from the component's event + * queue. + * - -1: a timeout was received. + * - -2: an error event was received. + * - -3: a config changed event was received. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2, + int event_flag, + int timeout); + + +/** + * The ilclient_wait_for_command_complete() function waits + * for a message from a component that indicates that the command + * has completed. This is either a command success message, or an + * error message that signals the completion of an event. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @return 0 indicates success, either the command successfully completed + * or the OMX_ErrorSameState was returned. -1 indicates + * that the command terminated with a different error message. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2); + + +/** + * The ilclient_wait_for_command_complete_dual() function + * is similar to ilclient_wait_for_command_complete(). The + * difference is that while waiting for the component to complete the + * event or raise an error, we can also report if another reports an + * error that terminates a command. This is useful if the two + * components are tunneled, and we need to wait for one component to + * enable a port, or change state to OMX_StateIdle. If the + * other component is the buffer supplier and reports an error, then + * it will not allocate buffers, so our first component may stall. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @param related Another component, where we will return if this + * component raises an error that terminates a command. + * + * @return 0 indicates success, either the command successfully + * completed or the OMX_ErrorSameState was returned. -1 + * indicates that the command terminated with a different error + * message. -2 indicates that the related component raised an error. + * In this case, the error is not cleared from the related + * component's event list. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2, + COMPONENT_T *related); + + +/** + * The ilclient_debug_output() function adds a message to a + * host-specific debug display. For a local VideoCore host the message is + * added to the internal message log. For a Win32 host the message is + * printed to the debug display. This function should be customised + * when IL client is ported to another platform. + * + * @param format A message to add, together with the variable + * argument list similar to printf and other standard C functions. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_debug_output(char *format, ...); + +/** + * The ilclient_get_handle() function returns the + * underlying OMX component held by an IL component handle. This is + * needed when calling methods such as OMX_SetParameter on + * a component created using the IL client library. + * + * @param comp IL component handle + * + * @return The OMX_HANDLETYPE value for the component. + ***********************************************************/ +VCHPRE_ OMX_HANDLETYPE VCHPOST_ ilclient_get_handle(COMPONENT_T *comp); + + +#ifndef OMX_SKIP64BIT + +/** + * Macro to return OMX_TICKS from a signed 64 bit value. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_from_s64(s) (s) + +/** + * Macro to return signed 64 bit value from OMX_TICKS. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_to_s64(t) (t) + +#else + +/** + * Inline function to return OMX_TICKS from a signed 64 bit + * value. This is the version where OMX_TICKS is a + * structure, an alternative definition is used when + * OMX_TICKS is a signed 64 bit value. + */ +static inline OMX_TICKS ilclient_ticks_from_s64(int64_t s) { + OMX_TICKS ret; + ret.nLowPart = s; + ret.nHighPart = s>>32; + return ret; +} + +/** + * Inline function to return signed 64 bit value from + * OMX_TICKS. This is the version where + * OMX_TICKS is a structure, an alternative definition is + * used when OMX_TICKS is a signed 64 bit value. + */ +static inline int64_t ilclient_ticks_to_s64(OMX_TICKS t) { + uint64_t u = t.nLowPart | ((uint64_t)t.nHighPart << 32); + return u; +} + + +#endif /* OMX_SKIP64BIT */ + +/** + * The ilclient_get_port_index() function returns the n'th + * port index of the specified type and direction for the given + * component. + * + * @param comp The component of interest + * @param dir The direction + * @param type The type, or -1 for any type. + * @param index Which port (counting from 0). + * + * @return The port index, or -1 if not found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_get_port_index(COMPONENT_T *comp, + OMX_DIRTYPE dir, + OMX_PORTDOMAINTYPE type, + int index); + + +/** + * The ilclient_suggest_bufsize() function gives a + * component a hint about the size of buffer it should use. This size + * is set on the component by setting the + * OMX_IndexParamBrcmOutputBufferSize index on the given + * component. + * + * @param comp IL component handle + * @param nBufSizeHint Size of buffer in bytes + * + * @return 0 indicates success, -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_suggest_bufsize(COMPONENT_T *comp, + OMX_U32 nBufSizeHint); + + +/** + * The ilclient_stack_size() function suggests a minimum + * stack size that tasks calling into with API will require. + * + * @return Suggested stack size in bytes. + ***********************************************************/ +VCHPRE_ unsigned int VCHPOST_ ilclient_stack_size(void); + +#endif /* ILCLIENT_H */ diff --git a/pi_hello_video/libs/ilclient/ilcore.c b/pi_hello_video/libs/ilclient/ilcore.c new file mode 100644 index 0000000..356733d --- /dev/null +++ b/pi_hello_video/libs/ilclient/ilcore.c @@ -0,0 +1,308 @@ +/* +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 Host core implementation. + */ + +#include +#include + +//includes +#include +#include +#include +#include + +#include "IL/OMX_Component.h" +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); + + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } + else + coreInit++; + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + + coreInit--; + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/setup.py b/setup.py index a2fbaf2..cd8afb8 100644 --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ from ez_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages -setup(name = 'Adafruit_Video_Looper', +setup(name = 'NURD_Video_Looper', version = '1.0.0', - author = 'Tony DiCola', - author_email = 'tdicola@adafruit.com', - description = 'Application to turn your Raspberry Pi into a dedicated looping video playback device, good for art installations, information displays, or just playing cat videos all day.', + author = 'Nurds', + author_email = 'ben@nurd.me', + description = 'Application to turn your Raspberry Pi Zero into a dedicated looping video playback device', license = 'GNU GPLv2', - url = 'https://github.com/adafruit/pi_video_looper', + url = 'https://github.com/FRCTeam3255/pi_video_looper', install_requires = ['pyudev'], packages = find_packages()) diff --git a/video_looper.ini b/video_looper.ini index 1ce2588..7c982c7 100644 --- a/video_looper.ini +++ b/video_looper.ini @@ -20,8 +20,8 @@ video_player = omxplayer # directory option will search only a specified directory on the SD card for # movie files. Note that you change the directory by modifying the setting in # the [directory] section below. The default is usb_drive. -file_reader = usb_drive -#file_reader = directory +#file_reader = usb_drive +file_reader = directory # The rest of the configuration for video looper below is optional and can be # ignored. @@ -54,7 +54,7 @@ console_output = true [directory] # The path to search for movies when using the directory file reader. -path = /home/pi +path = /home/pi/video # USB drive file reader configuration follows. [usb_drive]