diff --git a/configure.ac b/configure.ac index e92283c..5bec9ac 100644 --- a/configure.ac +++ b/configure.ac @@ -23,13 +23,20 @@ if test x"$BOXTYPE" = x"tripledragon"; then TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) fi -if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then - PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) - PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) +if test x$BOXTYPE = xgeneric; then + if test x$BOXMODEL != xraspi; then + PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) + PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) + # don't know which version is exactly needed here... + PKG_CHECK_MODULES([SWSCALE], [libswscale]) + PKG_CHECK_MODULES([SWRESAMPLE], [libswresample]) + else + # raspbian has no current versions and only libav instead of ffmpeg... :-( + PKG_CHECK_MODULES([AVFORMAT], [libavformat]) + PKG_CHECK_MODULES([AVCODEC], [libavcodec]) + fi # don't know which version is exactly needed here... PKG_CHECK_MODULES([AVUTIL], [libavutil]) - PKG_CHECK_MODULES([SWSCALE], [libswscale]) - PKG_CHECK_MODULES([SWRESAMPLE], [libswresample]) fi AC_OUTPUT([ Makefile diff --git a/raspi/Makefile.am b/raspi/Makefile.am index 50faaf1..be80985 100644 --- a/raspi/Makefile.am +++ b/raspi/Makefile.am @@ -11,19 +11,26 @@ AM_CPPFLAGS += \ AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing AM_LDFLAGS = \ + @AVFORMAT_LIBS@ \ + @AVUTIL_LIBS@ \ + @AVCODEC_LIBS@ \ -L/opt/vc/lib/ -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt \ -lOpenThreads libraspi_la_SOURCES = \ hardware_caps.c \ + avdec.cpp \ dmx.cpp \ video.cpp \ audio.cpp \ glfb.cpp \ - ilclient.c \ - ilcore.c \ init.cpp \ playback.cpp \ pwrmngr.cpp \ record.cpp +# OMX stuff, from https://github.com/linuxstb/pidvbip +libraspi_la_SOURCES += \ + avcodec_omx.c \ + codec.c \ + omx_utils.c diff --git a/raspi/audio.cpp b/raspi/audio.cpp index 73e0ec2..ce31c33 100644 --- a/raspi/audio.cpp +++ b/raspi/audio.cpp @@ -22,6 +22,7 @@ #include "audio_lib.h" #include "dmx_lib.h" +#include "avdec.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args) @@ -29,6 +30,9 @@ cAudio * audioDecoder = NULL; +extern cDemux *audioDemux; +extern AVDec *avdec; + cAudio::cAudio(void *, void *, void *) { lt_debug("%s\n", __func__); @@ -52,24 +56,34 @@ void cAudio::closeDevice(void) int cAudio::do_mute(bool enable, bool remember) { lt_debug("%s(%d, %d)\n", __func__, enable, remember); + if (enable) + avdec->set_volume(0); + else + avdec->set_volume(volume); return 0; } int cAudio::setVolume(unsigned int left, unsigned int right) { lt_debug("%s(%d, %d)\n", __func__, left, right); + volume = (left + right)/2; + avdec->set_volume(volume); return 0; } int cAudio::Start(void) { - lt_debug("%s >\n", __func__); + lt_info("%s >\n", __func__); + avdec->start_audio(); + lt_info("%s <\n", __func__); return 0; } int cAudio::Stop(void) { - lt_debug("%s >\n", __func__); + lt_info("%s >\n", __func__); + avdec->stop_audio(); + lt_info("%s <\n", __func__); return 0; } @@ -151,4 +165,3 @@ void cAudio::setBypassMode(bool disable) { lt_debug("%s %d\n", __func__, disable); } - diff --git a/raspi/avcodec_omx.c b/raspi/avcodec_omx.c new file mode 100644 index 0000000..14aef6e --- /dev/null +++ b/raspi/avcodec_omx.c @@ -0,0 +1,442 @@ +/* + * this code is originally from + * pidvbip - tvheadend client for the Raspberry Pi + * (C) Dave Chapman 2012-2013 + * + * adaption for libstb-hal + * (C) Stefan Seyfried 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * avcodec_omx.c -- audio / video decoder for the Raspberry Pi + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include "codec.h" +#include "avcodec_omx.h" +#include "omx_utils.h" + +static void* acodec_omx_thread(struct codec_init_args_t* args) +{ + struct codec_t* codec = args->codec; + struct omx_pipeline_t* pi = args->pipe; + struct codec_queue_t* current = NULL; + int res = 0; + int is_paused = 0; + OMX_BUFFERHEADERTYPE *buf; + + free(args); + fprintf(stderr,"Starting acodec_omx_thread\n"); + +new_channel: + while(1) + { +next_packet: + /* NOTE: This lock is only used by the video thread when setting + up or tearing down the pipeline, so we are not blocking normal + video playback + */ + //fprintf(stderr,"[acodec] - waiting for omx_active_mutex\n"); + pthread_mutex_lock(&pi->omx_active_mutex); + //fprintf(stderr,"[acodec] - got omx_active_mutex\n"); + while (!pi->omx_active) { + pthread_cond_wait(&pi->omx_active_cv, &pi->omx_active_mutex); + //fprintf(stderr,"[acodec] - omx_active=%d\n",pi->omx_active); + } + + if (is_paused) { + // Wait for resume message + fprintf(stderr,"acodec: Waiting for resume\n"); + pthread_cond_wait(&codec->resume_cv,&codec->queue_mutex); + is_paused = 0; + pthread_mutex_unlock(&codec->queue_mutex); + } + current = codec_queue_get_next_item(codec); + + if (current->msgtype == MSG_STOP) { + printf("[acodec] Stopping\n"); + codec_queue_free_item(codec,current); + pthread_mutex_unlock(&pi->omx_active_mutex); + goto new_channel; + } else if (current->msgtype == MSG_NEW_CHANNEL) { + fprintf(stderr,"[acodec] NEW_CHANNEL received, going to new_channel\n"); + codec_queue_free_item(codec,current); + pthread_mutex_unlock(&pi->omx_active_mutex); + goto new_channel;; + } else if (current->msgtype == MSG_PAUSE) { + fprintf(stderr,"acodec: Paused\n"); + codec_queue_free_item(codec,current); + is_paused = 1; + pthread_mutex_unlock(&pi->omx_active_mutex); + goto next_packet; + } else if (current->msgtype == MSG_SET_VOLUME) { + fprintf(stderr, "[acodec] SET_VOLUME %lld\n", current->data->PTS); + omx_audio_volume(&pi->audio_render, current->data->PTS); + codec_queue_free_item(codec,current); + pthread_mutex_unlock(&pi->omx_active_mutex); + goto next_packet; + } + + buf = get_next_buffer(&pi->audio_render); + buf->nTimeStamp = pts_to_omx(current->data->PTS); + //fprintf(stderr,"Audio timestamp=%lld\n",current->data->PTS); + +#if 0 + res = -1; + if (codec->acodectype == CODEC_ID_MP2 || codec->acodectype == CODEC_ID_MP3) { + res = mpg123_decode(m,current->data->packet,current->data->packetlength,buf->pBuffer,buf->nAllocLen,&buf->nFilledLen); + res = (res == MPG123_ERR); + } +#endif + if (current->data->packetlength > (int)buf->nAllocLen) { + fprintf(stderr, "packetlength > alloclen: %d > %u\n", current->data->packetlength, buf->nAllocLen); + res = -1; + } else { + memcpy(buf->pBuffer, current->data->packet, current->data->packetlength); + buf->nFilledLen = current->data->packetlength; + } + + + if (res == 0) { + buf->nFlags = 0; + if(codec->first_packet) + { + //usleep(1000000); + fprintf(stderr,"First audio packet\n"); + buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + codec->first_packet = 0; + } + + buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + OERR(OMX_EmptyThisBuffer(pi->audio_render.h, buf)); + } + + pthread_mutex_unlock(&pi->omx_active_mutex); + + codec_set_pts(codec,current->data->PTS); + + codec_queue_free_item(codec,current); + } + + return 0; +} + +void acodec_omx_init(struct codec_t* codec, struct omx_pipeline_t* pi) +{ + fprintf(stderr, "%s\n", __func__); + codec->codecstate = NULL; + + codec_queue_init(codec); + + struct codec_init_args_t* args = malloc(sizeof(struct codec_init_args_t)); + args->codec = codec; + args->pipe = pi; + + pthread_create(&codec->thread,NULL,(void * (*)(void *))acodec_omx_thread,(void*)args); +} + +static void* vcodec_omx_thread(struct codec_init_args_t* args) +{ + struct codec_t* codec = args->codec; + struct omx_pipeline_t* pi = args->pipe; + char* audio_dest = args->audio_dest; + OMX_VIDEO_CODINGTYPE coding; + int width, height; + struct codec_queue_t* current = NULL; + int is_paused = 0; + int64_t prev_DTS; + OMX_BUFFERHEADERTYPE *buf; + int current_aspect; + int aspect; + int gopbytes,totalbytes; + uint64_t gopfirstdts; + uint64_t firstdts = -1; + double min_bitrate, max_bitrate; + + free(args); + + codec->first_packet = 1; + + pthread_mutex_lock(&pi->omx_active_mutex); + + fprintf(stderr,"Starting vcodec_omx_thread\n"); + +next_channel: + fprintf(stderr,"vcodec_omx_thread: next_channel\n"); + coding = OMX_VIDEO_CodingUnused; + codec->first_packet = 1; + prev_DTS = -1; + current_aspect = 0; + pi->video_render.aspect = 0; + aspect = 0; + firstdts = -1; + totalbytes = 0; + gopbytes = -1; + min_bitrate = 0; + max_bitrate = 0; + + while (1) + { +next_packet: + if (current == NULL) { + if (is_paused) { + // Wait for resume message + fprintf(stderr,"vcodec: Waiting for resume\n"); + pthread_cond_wait(&codec->resume_cv,&codec->queue_mutex); + pthread_mutex_unlock(&codec->queue_mutex); + omx_clock_set_speed(&pi->clock, 1<<16); + is_paused = 0; + } + //fprintf(stderr,"[vcodec] getting next item\n\n"); + current = codec_queue_get_next_item(codec); + //fprintf(stderr,"[vcodec] got next item\n\n"); + + if ((current->msgtype == MSG_NEW_CHANNEL) || (current->msgtype == MSG_STOP)) { + codec_queue_free_item(codec,current); + current = NULL; + if (pi->omx_active) { + fprintf(stderr,"[vcodec] NEW_CHANNEL received, restarting pipeline\n"); + goto stop; + } else { + fprintf(stderr,"[vcodec] NEW_CHANNEL received, pipeline not active\n"); + pthread_mutex_unlock(&pi->omx_active_mutex); + fprintf(stderr,"[vcodec] unlocked omx_active_mutex\n"); + goto next_channel; + } + } else if (current->msgtype == MSG_PAUSE) { + fprintf(stderr,"vcodec: Paused\n"); + codec_queue_free_item(codec,current); + current = NULL; + omx_clock_set_speed(&pi->clock, 0); + is_paused = 1; + goto next_packet; + } else if (current->msgtype == MSG_SET_ASPECT_4_3) { + omx_set_display_region(pi, 240, 0, 1440, 1080); + current = NULL; + goto next_packet; + } else if (current->msgtype == MSG_SET_ASPECT_16_9) { + omx_set_display_region(pi, 0, 0, 1920, 1080); + current = NULL; + goto next_packet; + } else if (current->msgtype == MSG_ZOOM) { + if ((int)current->data) { + fprintf(stderr,"4:3 on!\n"); + omx_set_display_region(pi, 240, 0, 1440, 1080); + } else { + fprintf(stderr,"4:3 off\n"); + omx_set_display_region(pi, 0, 0, 1920, 1080); + } + current = NULL; + goto next_packet; + } + if ((prev_DTS != -1) && ((prev_DTS + 40000) != current->data->DTS) && ((prev_DTS + 20000) != current->data->DTS)) { + fprintf(stderr,"DTS discontinuity - DTS=%lld, prev_DTS=%lld (diff = %lld)\n",current->data->DTS,prev_DTS,current->data->DTS-prev_DTS); + } + prev_DTS = current->data->DTS; + } + + if (current->data == NULL) { + fprintf(stderr,"ERROR: data is NULL (expect segfault!)"); + } + + if (current->data->frametype == 'I') { + if (firstdts == (uint64_t)-1) { firstdts = current->data->DTS; } + if (gopbytes != -1) { + double duration = current->data->DTS-gopfirstdts; + double total_duration = current->data->DTS-firstdts; + double bitrate = (1000000.0/duration) * gopbytes * 8.0; + double total_bitrate = (1000000.0/total_duration) * totalbytes * 8.0; + if ((min_bitrate == 0) || (bitrate < min_bitrate)) { min_bitrate = bitrate; } + if ((max_bitrate == 0) || (bitrate > max_bitrate)) { max_bitrate = bitrate; } + fprintf(stderr,"GOP: %d bytes (%dms) - %.3fMbps (avg: %.3fMbps, min: %.3fMbps, max: %.3fMbps \r",gopbytes,(int)(current->data->DTS-gopfirstdts),bitrate/1000000,total_bitrate/1000000,min_bitrate/1000000,max_bitrate/1000000); + } + gopbytes = current->data->packetlength; + gopfirstdts = current->data->DTS; + totalbytes += current->data->packetlength; + } else { + if (gopbytes >= 0) + gopbytes += current->data->packetlength; + totalbytes += current->data->packetlength; + } + if ((current->data->frametype == 'I') && (codec->vcodectype == OMX_VIDEO_CodingMPEG2)) { + unsigned char* p = current->data->packet; + /* Parse the MPEG stream to extract the aspect ratio. + TODO: Handle the Active Format Description (AFD) which is frame-accurate. This is just GOP-accurate . + + "AFD is optionally carried in the user data of video elementary bitstreams, after the sequence + extension, GOP header, and/or picture coding extension." + */ + if ((p[0]==0) && (p[1]==0) && (p[2]==1) && (p[3]==0xb3)) { // Sequence header + //int width = (p[4] << 4) | (p[5] & 0xf0) >> 4; + //int height = (p[5] & 0x0f) << 8 | p[6]; + aspect = (p[7] & 0xf0) >> 4; + + //fprintf(stderr,"MPEG-2 sequence header - width=%d, height=%d, aspect=%d\n",width,height,aspect); + } + } + + /* Check if aspect ratio in video_render component has changed */ + if ((codec->vcodectype == OMX_VIDEO_CodingMPEG2) && (pi->video_render.aspect != current_aspect)) { + if (pi->video_render.aspect == 2) { // 4:3 + fprintf(stderr,"Switching to 4:3\n"); + omx_set_display_region(pi, 240, 0, 1440, 1080); + } else { // 16:9 - DVB can only be 4:3 or 16:9 + fprintf(stderr,"Switching to 16:9\n"); + omx_set_display_region(pi, 0, 0, 1920, 1080); + } + current_aspect = pi->video_render.aspect; + } + + if (coding == OMX_VIDEO_CodingUnused) { + fprintf(stderr,"Setting up OMX pipeline... - vcodectype=%d\n",codec->vcodectype); + omx_setup_pipeline(pi, codec->vcodectype, audio_dest, ((codec->width*codec->height) > 720*576) ? 1 : 0); + + fprintf(stderr,"Done setting up OMX pipeline.\n"); + coding = codec->vcodectype; + width = codec->width; + height = codec->height; + fprintf(stderr,"Initialised video codec - %s width=%d, height=%d\n",((coding == OMX_VIDEO_CodingAVC) ? "H264" : "MPEG-2"), width, height); + codec->acodec->first_packet = 1; + + /* We are ready to go, allow the audio codec back in */ + pi->omx_active = 1; + pthread_cond_signal(&pi->omx_active_cv); + //fprintf(stderr,"[vcodec] unlocking omx_active_mutex\n"); + pthread_mutex_unlock(&pi->omx_active_mutex); + //fprintf(stderr,"[vcodec] unlocked omx_active_mutex\n"); + } else if ((coding != codec->vcodectype) || (width != codec->width) || (height != codec->height)) { + fprintf(stderr,"Change of codec detected, restarting video codec\n"); + goto stop; + } + + int bytes_left = current->data->packetlength; + unsigned char* p = current->data->packet; + //fprintf(stderr,"Processing video packet - %d bytes\n",bytes_left); + while (bytes_left > 0) { + // fprintf(stderr,"OMX buffers: v: %02d/20 a: %02d/32 free, vcodec queue: %4d, acodec queue: %4d\r",omx_get_free_buffer_count(&pi->video_decode),omx_get_free_buffer_count(&pi->audio_render),codec->queue_count, codec->acodec->queue_count); + buf = get_next_buffer(&pi->video_decode); /* This will block if there are no empty buffers */ + + int to_copy = OMX_MIN(bytes_left, (int)buf->nAllocLen); + //fprintf(stderr,"Copying %d bytes\n",to_copy); + + memcpy(buf->pBuffer, p, to_copy); + p += to_copy; + bytes_left -= to_copy; + buf->nTimeStamp = pts_to_omx(current->data->PTS); + buf->nFilledLen = to_copy; + + buf->hMarkTargetComponent = pi->video_render.h; + buf->pMarkData = (OMX_PTR)aspect; + + buf->nFlags = 0; + if(codec->first_packet) + { + fprintf(stderr,"First video packet\n"); + buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + codec->first_packet = 0; + } + + if (bytes_left == 0) + buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + if (pi->video_decode.port_settings_changed == 1) + { + pi->video_decode.port_settings_changed = 2; + fprintf(stderr,"video_decode port_settings_changed = 1\n"); + + if (pi->do_deinterlace) { + OERR(OMX_SetupTunnel(pi->video_decode.h, 131, pi->image_fx.h, 190)); + omx_send_command_and_wait(&pi->video_decode, OMX_CommandPortEnable, 131, NULL); + + omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortEnable, 190, NULL); + omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } else { + OERR(OMX_SetupTunnel(pi->video_decode.h, 131, pi->video_scheduler.h, 10)); + omx_send_command_and_wait(&pi->video_decode, OMX_CommandPortEnable, 131, NULL); + + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortEnable, 10, NULL); + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateExecuting, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + } + } + + if (pi->image_fx.port_settings_changed == 1) + { + pi->image_fx.port_settings_changed = 2; + fprintf(stderr,"image_fx port_settings_changed = 1\n"); + + OERR(OMX_SetupTunnel(pi->image_fx.h, 191, pi->video_scheduler.h, 10)); + omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortEnable, 191, NULL); + + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortEnable, 10, NULL); + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateExecuting, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + } + + if (pi->video_scheduler.port_settings_changed == 1) + { + pi->video_scheduler.port_settings_changed = 2; + fprintf(stderr,"video_scheduler port_settings_changed = 1\n"); + + OERR(OMX_SetupTunnel(pi->video_scheduler.h, 11, pi->video_render.h, 90)); + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortEnable, 11, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandPortEnable, 90, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } + + OERR(OMX_EmptyThisBuffer(pi->video_decode.h, buf)); + } + + codec_queue_free_item(codec,current); + current = NULL; + } + +stop: + /* We lock the mutex to stop the audio codec. It is unlocked after the pipline is setup again */ + + //fprintf(stderr,"[vcodec] - waiting for omx_active_mutex\n"); + pthread_mutex_lock(&pi->omx_active_mutex); + //fprintf(stderr,"[vcodec] - got omx_active_mutex, tearing down pipeline.\n"); + + omx_teardown_pipeline(pi); + //fprintf(stderr,"[vcodec] - End of omx thread, pipeline torn down.\n"); + pi->omx_active = 0; + + goto next_channel; + + return 0; +} + +void vcodec_omx_init(struct codec_t* codec, struct omx_pipeline_t* pi, char* audio_dest) +{ + fprintf(stderr, "%s\n", __func__); +//codec->vcodectype = OMX_VIDEO_CodingUnused; + codec_queue_init(codec); + + struct codec_init_args_t* args = malloc(sizeof(struct codec_init_args_t)); + args->codec = codec; + args->pipe = pi; + args->audio_dest = audio_dest; + + pthread_create(&codec->thread,NULL,(void * (*)(void *))vcodec_omx_thread,(void*)args); +} diff --git a/raspi/avcodec_omx.h b/raspi/avcodec_omx.h new file mode 100644 index 0000000..bf4d098 --- /dev/null +++ b/raspi/avcodec_omx.h @@ -0,0 +1,14 @@ +#ifndef _AVCODEC_OMX_H +#define _AVCODEC_OMX_H + +#include "codec.h" +#include "omx_utils.h" + +void acodec_omx_init(struct codec_t* codec, struct omx_pipeline_t* pipe); +void acodec_omx_add_to_queue(struct codec_t* codec, struct packet_t* packet); + +void vcodec_omx_init(struct codec_t* codec, struct omx_pipeline_t* pipe, char* audio_dest); +void vcodec_omx_add_to_queue(struct codec_t* codec, struct packet_t* packet); +int64_t vcodec_omx_current_get_pts(struct codec_t* codec); + +#endif diff --git a/raspi/avdec.cpp b/raspi/avdec.cpp new file mode 100644 index 0000000..5d3f98b --- /dev/null +++ b/raspi/avdec.cpp @@ -0,0 +1,524 @@ +/* + * (C) 2010-2013 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Audio / Video decoder for Raspberry pi + */ + +#include +#include +#include +#include + +#include +#include + +#include + +extern "C" { +#include +#include +#include +#include +#include +//#include +#include "codec.h" +#include "avcodec_omx.h" +} +/* ffmpeg buf 8k for audio */ +#define AINBUF_SIZE 0x2000 +/* ffmpeg buf 256k for video */ +#define VINBUF_SIZE 0x40000 + +#ifdef EXTRA_BUFFER +/* my own buf 16k */ +#define ADMX_BUF_SZ 0x4000 +/* my own buf 512k */ +#define VDMX_BUF_SZ 0x80000 +#endif + +#include "avdec.h" +#include "dmx_lib.h" +#include "lt_debug.h" + +#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args) +#define lt_info(args...) _lt_info(HAL_DEBUG_AUDIO, this, args) + +static AVRational omx_timebase = {1,1000000}; + +static struct codecs_t codecs; +static struct omx_pipeline_t omxpipe; + +extern cDemux *audioDemux; +extern cDemux *videoDemux; + +class Dec { + friend class aDec; + friend class vDec; + public: + Dec(bool audio); + ~Dec(); + Dec *base; + int dmx_read(uint8_t *buf, int buf_size); + private: + bool dec_running; + cDemux *dmx; +#ifdef EXTRA_BUFFER + int bufpos; + public: + int bufsize; + uint8_t *dmxbuf; +#endif +}; + +class aDec: public Dec, public OpenThreads::Thread +{ + public: + aDec(); + ~aDec(); + int set_volume(int vol); + private: + void run(); + int64_t curr_pts; +}; + +class vDec: public Dec, public OpenThreads::Thread +{ + public: + vDec(); + ~vDec(); + private: + void run(); +}; + +Dec::Dec(bool audio) +{ + lt_info("calling Dec::Dec(%d)\n", audio); + dec_running = false; + if (audio) + dmx = audioDemux; + else + dmx = videoDemux; +#ifdef EXTRA_BUFFER + if (audio) + bufsize = ADMX_BUF_SZ; + else + bufsize = VDMX_BUF_SZ; + dmxbuf = (uint8_t *)malloc(bufsize); + lt_info("dmxbuf: %p, bufsize: %d this %p\n", dmxbuf, bufsize, this); + bufpos = 0; +#endif +} + +Dec::~Dec() +{ +#ifdef EXTRA_BUFFER + free(dmxbuf); +#endif + lt_info("exiting Dec::~Dec()\n"); +} + +aDec::aDec() : Dec(true) +{ + lt_info("calling aDec::aDec()\n"); + start(); +} + +aDec::~aDec() +{ + lt_info("calling aDec::~aDec()\n"); + codec_stop(&codecs.acodec); + dec_running = false; + join(); + lt_info("exiting aDec::~aDec()\n"); +} + +vDec::vDec() : Dec(false) +{ + lt_info("calling vDec::vDec()\n"); + start(); +} + +vDec::~vDec() +{ + lt_info("calling vDec::~vDec()\n"); + codec_stop(&codecs.vcodec); + dec_running = false; + join(); + lt_info("exiting vDec::~vDec()\n"); +} + +AVDec::AVDec() +{ + bcm_host_init(); + av_register_all(); + if (OMX_Init() != OMX_ErrorNone) /* TODO: what now? */ + lt_info("AVDec::AVDec: OMX_Init() failed!\n"); + + memset(&omxpipe, 0, sizeof(omxpipe)); + memset(&codecs, 0, sizeof(codecs)); + + pthread_mutex_init(&codecs.playback_mutex, NULL); + pthread_mutex_init(&omxpipe.omx_active_mutex, NULL); + pthread_cond_init(&omxpipe.omx_active_cv, NULL); + + codecs.is_paused = 0; + codecs.vcodec.acodec = &codecs.acodec; + /* TODO: this is hard coded for h264 for now */ + codecs.vcodec.vcodectype = OMX_VIDEO_CodingAVC; + /* TODO: hard coded audio output HDMI */ + vcodec_omx_init(&codecs.vcodec, &omxpipe, (char *)"hdmi"); + acodec_omx_init(&codecs.acodec, &omxpipe); + + lt_info("AVDec created\n"); +} + +AVDec::~AVDec() +{ + stop_video(); + + if (OMX_Deinit() != OMX_ErrorNone) /* TODO: what now? */ + lt_info("AVDec::~AVDec: OMX_Deinit() failed!\n"); + + lt_info("AVDec destroyed\n"); +} + +static vDec *vdec = NULL; +static aDec *adec = NULL; + +#if 0 + int play_pcm(uint8_t *buffer, int size, int ch = -1, int srate = -1, int bits = -1, int le = -1); + int show_picture(const char *filename); + int pig(int x, int y, int w, int h); +#endif + +int AVDec::start_video() +{ + if (vdec) + lt_info("AVDec::start_video: vdec not NULL!\n"); + else + vdec = new vDec(); + return (vdec != NULL); +} + +int AVDec::stop_video() +{ + if (! vdec) + lt_info("AVDec::stop_video: vdec is NULL!\n"); + else + delete vdec; + vdec = NULL; + return 1; +} + +int AVDec::start_audio() +{ + if (adec) + lt_info("AVdec::start_audio: adec not NULL!\n"); + else + adec = new aDec(); + return (adec != NULL); +} + +int AVDec::stop_audio() +{ + if (! adec) + lt_info("AVDec::stop_audio: adec is NULL!\n"); + else + delete adec; + adec = NULL; + return 1; +} + +int AVDec::set_volume(int vol) +{ + if (adec) + return adec->set_volume(vol); + return -1; +} + +typedef struct { + Dec *d; + bool audio; +} helper_struct; + +static int _read(void *thiz, uint8_t *buf, int buf_size) +{ + helper_struct *h = (helper_struct *)thiz; + Dec *me; + if (h->audio) + me = (aDec *)h->d; + else + me = (vDec *)h->d; + //fprintf(stderr, "_read, me %p dmxbuf %p bufsize %d buf %p n %d audio %d\n", me, me->dmxbuf, me->bufsize, buf, buf_size, h->audio); + return me->dmx_read(buf, buf_size); +} + +static inline OMX_TICKS ToOMXTime(int64_t pts) +{ + OMX_TICKS ticks; + ticks.nLowPart = pts; + ticks.nHighPart = pts >> 32; + return ticks; +} + +static int64_t vpts = 0; +static int64_t apts = 0; + +void aDec::run() +{ + hal_set_threadname("hal:adec"); + lt_info("====================== start audio decoder thread ================================\n"); + int ret = 0; + codec_new_channel(&codecs.acodec); + codecs.acodec.first_packet = 1; + codecs.acodec.is_running = 1; + struct packet_t *packet; + /* libavcodec & friends */ + AVCodec *codec; + AVFormatContext *avfc = NULL; + AVCodecContext *c = NULL; + AVInputFormat *inp; + AVFrame *frame; + uint8_t *inbuf = (uint8_t *)av_malloc(AINBUF_SIZE); + AVPacket avpkt; + char tmp[128] = "unknown"; + + curr_pts = 0; + av_init_packet(&avpkt); + inp = av_find_input_format("mpegts"); + helper_struct h; + h.d = this; + h.audio = true; + dec_running = true; + AVIOContext *pIOCtx = avio_alloc_context(inbuf, AINBUF_SIZE, // internal Buffer and its size + 0, // bWriteable (1=true,0=false) + &h, // user data; will be passed to our callback functions + _read, // read callback + NULL, // write callback + NULL); // seek callback + avfc = avformat_alloc_context(); + avfc->pb = pIOCtx; + avfc->iformat = inp; + avfc->probesize = 188*5; + + if (avformat_open_input(&avfc, NULL, inp, NULL) < 0) { + lt_info("aDec: avformat_open_input() failed.\n"); + goto out; + } + ret = avformat_find_stream_info(avfc, NULL); + lt_debug("aDec: avformat_find_stream_info: %d\n", ret); + if (avfc->nb_streams != 1) + { + lt_info("aDec: nb_streams: %d, should be 1!\n", avfc->nb_streams); + goto out; + } + if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO) + lt_info("aDec: stream 0 no audio codec? 0x%x\n", avfc->streams[0]->codec->codec_type); + + c = avfc->streams[0]->codec; + codec = avcodec_find_decoder(c->codec_id); + if (!codec) { + lt_info("aDec: Codec for codec_id 0x%08x not found\n", c->codec_id); + goto out; + } + if (avcodec_open2(c, codec, NULL) < 0) { + lt_info("aDec: avcodec_open2() failed\n"); + goto out; + } + frame = avcodec_alloc_frame(); + if (!frame) { + lt_info("aDec: avcodec_alloc_frame failed\n"); + goto out2; + } + /* output sample rate, channels, layout could be set here if necessary */ + avcodec_string(tmp, sizeof(tmp), c, 0); + lt_info("aDec: decoding %s\n", tmp); + while (dec_running) { + int gotframe = 0; + if (av_read_frame(avfc, &avpkt) < 0) { + lt_info("aDec: av_read_frame < 0\n"); + usleep(1000); + continue; + break; + } + int len = avcodec_decode_audio4(c, frame, &gotframe, &avpkt); + if (gotframe && dec_running) { + curr_pts = (avpkt.pts); + apts = curr_pts; + lt_debug("aDec: pts 0x%" PRIx64 " %3f\n", curr_pts, curr_pts/90000.0); + int data_size = av_samples_get_buffer_size(NULL, c->channels, + frame->nb_samples, c->sample_fmt, 1); + packet = (packet_t *)malloc(sizeof(*packet)); + packet->PTS = av_rescale_q(avpkt.pts, avfc->streams[0]->time_base, omx_timebase); + packet->DTS = -1; + packet->packetlength = data_size; + packet->packet = (uint8_t *)malloc(data_size); + memcpy(packet->packet, frame->data[0], data_size); + packet->buf = packet->packet; /* This is what is free()ed */ + codec_queue_add_item(&codecs.acodec, packet, MSG_PACKET); + } + if (len != avpkt.size) + lt_info("aDec: decoded len: %d pkt.size: %d\n", len, avpkt.size); + av_free_packet(&avpkt); + } + lt_info("aDec: decoder loop exited\n"); +#if LIBAVCODEC_VERSION_INT >= (54 << 16 | 28 << 8) + avcodec_free_frame(&frame); +#else + av_free(frame); +#endif + out2: + avcodec_close(c); + c = NULL; + out: + if (avfc) + avformat_close_input(&avfc); + av_free(pIOCtx->buffer); + av_free(pIOCtx); + lt_info("======================== end audio decoder thread ================================\n"); +} + +int aDec::set_volume(int vol) +{ + struct packet_t *packet; + long volume = (vol - 100) * 60; + packet = (packet_t *)malloc(sizeof(*packet)); + packet->PTS = volume; + packet->buf = NULL; + codec_queue_add_item(&codecs.acodec, packet, MSG_SET_VOLUME); + return 0; +} + +void vDec::run() +{ + hal_set_threadname("hal:vdec"); + lt_info("====================== start video decoder thread ================================\n"); + int ret = 0; + codec_new_channel(&codecs.vcodec); + codecs.vcodec.first_packet = 1; + codecs.vcodec.is_running = 1; + + AVFormatContext *avfc = NULL; + AVInputFormat *inp; + uint8_t *inbuf = (uint8_t *)av_malloc(VINBUF_SIZE); + AVPacket avpkt; + struct packet_t *packet; + + av_init_packet(&avpkt); + inp = av_find_input_format("mpegts"); + helper_struct h; + h.d = this; + h.audio = false; + dec_running = true; + AVIOContext *pIOCtx = avio_alloc_context(inbuf, VINBUF_SIZE, // internal Buffer and its size + 0, // bWriteable (1=true,0=false) + &h, // user data; will be passed to our callback functions + _read, // read callback + NULL, // write callback + NULL); // seek callback + avfc = avformat_alloc_context(); + avfc->pb = pIOCtx; + avfc->iformat = inp; + avfc->probesize = 188*1000; + + if ((ret = avformat_open_input(&avfc, NULL, inp, NULL)) < 0) { + lt_info("vDec: Could not open input: %d ctx:%p\n", ret, avfc); + goto out; + } + while (avfc->nb_streams < 1) + { + lt_info("vDec: nb_streams %d, should be 1 => retry\n", avfc->nb_streams); + if (av_read_frame(avfc, &avpkt) < 0) + lt_info("vDec: av_read_frame < 0\n"); + av_free_packet(&avpkt); + if (! dec_running) + goto out; + } + + /* TODO: how to recover here? */ + if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + lt_info("vDec: no video codec? 0x%x streams: %d\n", + avfc->streams[0]->codec->codec_type, avfc->nb_streams); + + lt_info("vDec: decoder loop starts\n"); + while (dec_running) { + if (av_read_frame(avfc, &avpkt) < 0) { + usleep(1000); + continue; + break; + } + if (dec_running) { + vpts = avpkt.pts; + lt_debug("vDec: pts 0x%" PRIx64 " %3f\n", vpts, vpts/90000.0); + packet = (packet_t *)malloc(sizeof(*packet)); + packet->PTS = av_rescale_q(avpkt.pts, avfc->streams[0]->time_base, omx_timebase); + packet->DTS = -1; + packet->packetlength = avpkt.size; + packet->packet = (uint8_t *)malloc(avpkt.size); + memcpy(packet->packet, avpkt.data, avpkt.size); + packet->buf = packet->packet; /* This is what is free()ed */ + codec_queue_add_item(&codecs.vcodec, packet, MSG_PACKET); + } + av_free_packet(&avpkt); + } + lt_info("vDec: decoder loop ends\n"); + out: + if (avfc) + avformat_close_input(&avfc); + av_free(pIOCtx->buffer); + av_free(pIOCtx); + + lt_info("======================== end video decoder thread ================================\n"); +} + +int Dec::dmx_read(uint8_t *buf, int buf_size) +{ +#ifndef EXTRA_BUFFER + int tmp, ret = 0, cnt = 0; + while (++cnt < 20) { + tmp = dmx->Read(buf + ret, buf_size - ret, 10); + if (tmp > 0) + ret += tmp; + if (ret > buf_size - 512) + break; + } + return ret; +#else + int tmp = 0, ret = 0; + if (bufpos < bufsize - 4096) { + while (bufpos < buf_size && ++tmp < 20) { /* retry max 20 times */ + ret = dmx->Read(dmxbuf + bufpos, bufsize - bufpos, 10); + if (ret > 0) + bufpos += ret; + if (! dec_running) + break; + } + } + if (bufpos == 0) + return 0; + //lt_info("%s buf_size %d bufpos %d th %d tmp %d\n", __func__, buf_size, bufpos, thread_started, tmp); + if (bufpos > buf_size) { + memcpy(buf, dmxbuf, buf_size); + memmove(dmxbuf, dmxbuf + buf_size, bufpos - buf_size); + bufpos -= buf_size; + return buf_size; + } + memcpy(buf, dmxbuf, bufpos); + tmp = bufpos; + bufpos = 0; + return tmp; +#endif +} diff --git a/raspi/avdec.h b/raspi/avdec.h new file mode 100644 index 0000000..3bf33d9 --- /dev/null +++ b/raspi/avdec.h @@ -0,0 +1,34 @@ +/* + * (C) 2010-2013 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Audio / Video decoder wrapper class for Raspberry pi + */ + +#include "codec.h" + +class AVDec { +public: + AVDec(); + ~AVDec(); + int start_audio(); + int start_video(); + int stop_audio(); + int stop_video(); + int set_volume(int vol); + int play_pcm(uint8_t *buffer, int size, int ch = -1, int srate = -1, int bits = -1, int le = -1); + int show_picture(const char *filename); + int pig(int x, int y, int w, int h); +}; diff --git a/raspi/codec.c b/raspi/codec.c new file mode 100644 index 0000000..38f1f8c --- /dev/null +++ b/raspi/codec.c @@ -0,0 +1,265 @@ +/* + * this code is originally from + * pidvbip - tvheadend client for the Raspberry Pi + * (C) Dave Chapman 2012-2013 + * + * adaption for libstb-hal + * (C) Stefan Seyfried 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * codec.c -- audio / video codec queue functions + */ + +#include "config.h" + +#include +#include +#include "codec.h" + +void codec_queue_init(struct codec_t* codec) +{ + codec->queue_head = NULL; + codec->queue_tail = NULL; + codec->queue_count = 0; + codec->is_running = 1; + codec->PTS = -1; + + pthread_mutex_init(&codec->queue_mutex,NULL); + pthread_cond_init(&codec->queue_count_cv,NULL); + pthread_cond_init(&codec->resume_cv,NULL); + pthread_mutex_init(&codec->PTS_mutex,NULL); + pthread_mutex_init(&codec->isrunning_mutex,NULL); +} + +int codec_is_running(struct codec_t* codec) +{ + int res; + pthread_mutex_lock(&codec->isrunning_mutex); + + res = codec->is_running; + + pthread_mutex_unlock(&codec->isrunning_mutex); + + return res; +} + +void codec_flush_queue(struct codec_t* codec) +{ + /* Empty the queue */ + + pthread_mutex_lock(&codec->queue_mutex); + + struct codec_queue_t* p = codec->queue_head; + while (p) { + struct codec_queue_t* tmp = p; + p = p->next; + codec_queue_free_item(codec,tmp); + } + + codec->queue_count = 0; + codec->queue_head = NULL; + codec->queue_tail = NULL; + + pthread_mutex_unlock(&codec->queue_mutex); +} + +static void codec_stop0(struct codec_t* codec, int msg) +{ + struct codec_queue_t* new = malloc(sizeof(struct codec_queue_t)); + + if (new == NULL) { + fprintf(stderr,"FATAL ERROR: out of memory adding to queue\n"); + exit(1); + } + + pthread_mutex_lock(&codec->queue_mutex); + + pthread_mutex_lock(&codec->isrunning_mutex); + codec->is_running = 0; + pthread_mutex_unlock(&codec->isrunning_mutex); + + /* Empty the queue */ + + struct codec_queue_t* p = codec->queue_head; + while (p) { + struct codec_queue_t* tmp = p; + p = p->next; + codec_queue_free_item(codec,tmp); + } + + new->msgtype = msg; + new->data = NULL; + new->next = NULL; + new->prev = NULL; + codec->queue_head = new; + codec->queue_tail = new; + + if (codec->queue_count == 0) { + pthread_cond_signal(&codec->queue_count_cv); + } + codec->queue_count=1; + + codec_set_pts(codec,-1); + pthread_mutex_unlock(&codec->queue_mutex); +} + +void codec_stop(struct codec_t* codec) +{ + codec_stop0(codec, MSG_STOP); +} + +void codec_new_channel(struct codec_t* codec) +{ + codec_stop0(codec, MSG_NEW_CHANNEL); +} + +void codec_send_message(struct codec_t* codec, int m, void* data) +{ + struct codec_queue_t* new = malloc(sizeof(struct codec_queue_t)); + + if (new == NULL) { + fprintf(stderr,"FATAL ERROR: out of memory adding to queue\n"); + exit(1); + } + new->msgtype = m; + new->data = data; + + pthread_mutex_lock(&codec->queue_mutex); + + /* End to end of queue */ + if (codec->queue_tail == NULL) { + new->next = NULL; + new->prev = NULL; + codec->queue_head = new; + codec->queue_tail = new; + + pthread_cond_signal(&codec->queue_count_cv); + } else { + new->next = NULL; + new->prev = codec->queue_tail; + new->prev->next = new; + codec->queue_tail = new; + } + + codec->queue_count++; + + pthread_mutex_unlock(&codec->queue_mutex); +} + +void codec_pause(struct codec_t* codec) +{ + codec_send_message(codec, MSG_PAUSE, NULL); +} + +void codec_resume(struct codec_t* codec) +{ + pthread_cond_signal(&codec->resume_cv); +} + +void codec_queue_add_item(struct codec_t* codec, struct packet_t* packet, int msgtype) +{ + if (packet == NULL) { + fprintf(stderr,"ERROR: Adding NULL packet to queue, skipping\n"); + return; + } + + struct codec_queue_t* new = malloc(sizeof(struct codec_queue_t)); + + if (new == NULL) { + fprintf(stderr,"FATAL ERROR: out of memory adding to queue\n"); + exit(1); + } + + new->msgtype = msgtype; + new->data = packet; + + pthread_mutex_lock(&codec->queue_mutex); + + if (codec->is_running) { + if (codec->queue_head == NULL) { + new->next = NULL; + new->prev = NULL; + codec->queue_head = new; + codec->queue_tail = new; + pthread_cond_signal(&codec->queue_count_cv); + } else { + new->next = codec->queue_head; + new->prev = NULL; + new->next->prev = new; + codec->queue_head = new; + } + + codec->queue_count++; + } else { + fprintf(stderr,"Dropping packet - codec is stopped.\n"); + free(packet); + } + + pthread_mutex_unlock(&codec->queue_mutex); +} + +void codec_queue_free_item(__attribute__((unused))struct codec_t* codec, struct codec_queue_t* item) +{ + if (item == NULL) + return; + + if (item->data) { + free(item->data->buf); + free(item->data); + } + free(item); +} + +struct codec_queue_t* codec_queue_get_next_item(struct codec_t* codec) +{ + struct codec_queue_t* item; + pthread_mutex_lock(&codec->queue_mutex); + + while (codec->queue_tail == NULL) + pthread_cond_wait(&codec->queue_count_cv,&codec->queue_mutex); + + item = codec->queue_tail; + + codec->queue_tail = codec->queue_tail->prev; + if (codec->queue_tail == NULL) { + codec->queue_head = NULL; + } else { + codec->queue_tail->next = NULL; + } + + codec->queue_count--; + + pthread_mutex_unlock(&codec->queue_mutex); + + return item; +} + +void codec_set_pts(struct codec_t* codec, int64_t PTS) +{ + pthread_mutex_lock(&codec->PTS_mutex); + codec->PTS = PTS; + pthread_mutex_unlock(&codec->PTS_mutex); +} + +int64_t codec_get_pts(struct codec_t* codec) +{ + int64_t PTS; + + pthread_mutex_lock(&codec->PTS_mutex); + PTS = codec->PTS; + pthread_mutex_unlock(&codec->PTS_mutex); + + return PTS; +} diff --git a/raspi/codec.h b/raspi/codec.h new file mode 100644 index 0000000..fd9eb8b --- /dev/null +++ b/raspi/codec.h @@ -0,0 +1,91 @@ +#ifndef _CODEC_H +#define _CODEC_H + +#include +#include +#include + +#include "omx_utils.h" + +struct codec_init_args_t +{ + struct codec_t* codec; + struct omx_pipeline_t* pipe; + char* audio_dest; +}; + +struct packet_t +{ + unsigned char* buf; /* The buffer to be freed after use */ + unsigned char* packet; /* Pointer to the actual video data (within buf) */ + int packetlength; /* Number of bytes in packet */ + int frametype; + int64_t PTS; + int64_t DTS; +}; + +#define MSG_PACKET 1 +#define MSG_PLAY 2 +#define MSG_STOP 3 +#define MSG_PAUSE 4 +#define MSG_NEW_CHANNEL 5 +#define MSG_ZOOM 6 +#define MSG_SET_ASPECT_4_3 7 +#define MSG_SET_ASPECT_16_9 8 +#define MSG_SET_VOLUME 9 + +struct codec_queue_t +{ + int msgtype; + struct packet_t* data; + struct codec_queue_t* prev; + struct codec_queue_t* next; +}; + +struct codec_t +{ + void* codecstate; + int is_running; + pthread_t thread; + pthread_mutex_t queue_mutex; + pthread_cond_t queue_count_cv; + pthread_cond_t resume_cv; + struct codec_queue_t* queue_head; + struct codec_queue_t* queue_tail; + int queue_count; + int64_t PTS; + pthread_mutex_t PTS_mutex; + pthread_mutex_t isrunning_mutex; + struct codec_t* acodec; + OMX_VIDEO_CODINGTYPE vcodectype; + int width; + int height; + enum CodecID acodectype; + int first_packet; +}; + +struct codecs_t { + pthread_mutex_t playback_mutex; /* Locked by the thread with access to playback - htsp/avplay/etc */ + + struct codec_t vcodec; + struct codec_t acodec; // Audio + struct codec_t scodec; // Subtitles +// struct htsp_subscription_t subscription; // Details of the currently tuned channel + int is_paused; +}; + +void codec_queue_init(struct codec_t* codec); +void codec_new_channel(struct codec_t* codec); +void codec_stop(struct codec_t* codec); +void codec_send_message(struct codec_t* codec, int m, void* data); +void codec_pause(struct codec_t* codec); +void codec_resume(struct codec_t* codec); +void codec_queue_add_item(struct codec_t* codec, struct packet_t* packet, int msgtype); +void codec_queue_free_item(struct codec_t* codec,struct codec_queue_t* item); +struct codec_queue_t* codec_queue_get_next_item(struct codec_t* codec); +void codec_set_pts(struct codec_t* codec, int64_t PTS); +int64_t codec_get_pts(struct codec_t* codec); +int codec_is_running(struct codec_t* codec); +void codec_flush_queue(struct codec_t* codec); + +#endif diff --git a/raspi/dmx.cpp b/raspi/dmx.cpp index 7fa2ad7..3e967a9 100644 --- a/raspi/dmx.cpp +++ b/raspi/dmx.cpp @@ -382,20 +382,19 @@ bool cDemux::pesFilter(const unsigned short pid) p_flt.output = DMX_OUT_DECODER; p_flt.input = DMX_IN_FRONTEND; - /* for now, output to TS_TAP for live mode, - * test playback with "omxplayer /dev/dvb/adapter0/dvr0" */ switch (dmx_type) { case DMX_PCR_ONLY_CHANNEL: p_flt.pes_type = DMX_PES_OTHER; p_flt.output = DMX_OUT_TAP; + return true; break; case DMX_AUDIO_CHANNEL: p_flt.pes_type = DMX_PES_OTHER; - p_flt.output = DMX_OUT_TAP; + p_flt.output = DMX_OUT_TSDEMUX_TAP; break; case DMX_VIDEO_CHANNEL: p_flt.pes_type = DMX_PES_OTHER; - p_flt.output = DMX_OUT_TAP; + p_flt.output = DMX_OUT_TSDEMUX_TAP; break; case DMX_PES_CHANNEL: p_flt.pes_type = DMX_PES_OTHER; diff --git a/raspi/ilclient.c b/raspi/ilclient.c deleted file mode 100644 index 2cd8e43..0000000 --- a/raspi/ilclient.c +++ /dev/null @@ -1,1844 +0,0 @@ -/* -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. -*/ - -/* - * 2013 Stefan Seyfried - * changes for libstb-hal: - * * fixed shadow warnings from compiler - * * added __attribute__((unused)) annotation to please the compiler - * * fixed "comparing signed / unsinged" warnings by adding (int) casts - */ - -/* - * \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, - int /*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 error2 = OMX_GetParameter((*comp)->comp, types[i], &ports); - if(error2 == 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; - unsigned 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((int)(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 || (int)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 && (int)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 && (int)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(__attribute__((unused)) 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(__attribute__((unused)) 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(__attribute__((unused)) OMX_IN OMX_HANDLETYPE hComponent, - __attribute__((unused)) OMX_IN OMX_PTR pAppData, - __attribute__((unused)) 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(__attribute__((unused)) 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(__attribute__((unused)) OMX_OUT OMX_HANDLETYPE hComponent, - __attribute__((unused)) OMX_OUT OMX_PTR pAppData, - __attribute__((unused)) 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, - int /*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/raspi/ilcore.c b/raspi/ilcore.c deleted file mode 100644 index 8e1349c..0000000 --- a/raspi/ilcore.c +++ /dev/null @@ -1,316 +0,0 @@ -/* -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. -*/ - -/* - * 2013 Stefan Seyfried - * changes for libstb-hal: - * * fixed shadow warnings from compiler - * * added __attribute__((unused)) annotation to please the compiler - * * fixed "comparing signed / unsinged" warnings by adding (int) casts - */ - -/* - * \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. - unsigned 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 ( - __attribute__((unused)) OMX_IN OMX_STRING role, - __attribute__((unused)) OMX_INOUT OMX_U32 *pNumComps, - __attribute__((unused)) OMX_INOUT OMX_U8 **compNames) -{ - OMX_ERRORTYPE eError = OMX_ErrorNone; - - *pNumComps = 0; - return eError; -} - -/* OMX_GetRolesOfComponent */ -OMX_ERRORTYPE OMX_GetRolesOfComponent ( - __attribute__((unused)) OMX_IN OMX_STRING compName, - OMX_INOUT OMX_U32 *pNumRoles, - __attribute__((unused)) 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/raspi/init.cpp b/raspi/init.cpp index 557d823..2d47e15 100644 --- a/raspi/init.cpp +++ b/raspi/init.cpp @@ -37,11 +37,14 @@ #include "init_lib.h" #include "lt_debug.h" #include "glfb.h" +#include "avdec.h" + #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) static bool initialized = false; GLFramebuffer *glfb = NULL; +AVDec *avdec = NULL; typedef std::map keymap_t; static keymap_t kmap; @@ -215,6 +218,8 @@ void init_td_api() } if (! thread) thread = new Input(); + if (! avdec) + avdec = new AVDec(); initialized = true; } @@ -225,5 +230,7 @@ void shutdown_td_api() delete glfb; if (thread) delete thread; + if (avdec) + delete avdec; initialized = false; } diff --git a/raspi/omx_utils.c b/raspi/omx_utils.c new file mode 100644 index 0000000..2e9f1c5 --- /dev/null +++ b/raspi/omx_utils.c @@ -0,0 +1,1150 @@ +/* + * this code is originally from + * pidvbip - tvheadend client for the Raspberry Pi + * (C) Dave Chapman 2012-2013 + * + * adaption for libstb-hal + * (C) Stefan Seyfried 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * omx_utils.c -- OMX helper functions for the Raspberry Pi + */ + +#include "config.h" + +#include +#include + +#include "omx_utils.h" + +#if 1 +#define DEBUGF(...) +#else +#define DEBUGF(...) fprintf(stderr,__VA_ARGS__) +#endif + +OMX_TICKS pts_to_omx(uint64_t pts) +{ + OMX_TICKS ticks; + ticks.nLowPart = pts; + ticks.nHighPart = pts >> 32; + return ticks; +}; + +static int is_port_enabled(OMX_HANDLETYPE handle, int port) +{ + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + OMX_INIT_STRUCTURE(portdef); + portdef.nPortIndex = port; + OERR(OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef)); + + return (portdef.bEnabled == 0 ? 0 : 1);; +} + +static void omx_show_state(struct omx_component_t* component, int port1, int port2, int port3) +{ + OMX_STATETYPE state; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + OERR(OMX_GetState(component->h, &state)); + + DEBUGF("%s is in state ",component->name); + + switch (state) { + case OMX_StateInvalid: DEBUGF("OMX_StateInvalid\n"); break; + case OMX_StateLoaded: DEBUGF("OMX_StateLoaded\n"); break; + case OMX_StateIdle: DEBUGF("OMX_StateIdle\n"); break; + case OMX_StateExecuting: DEBUGF("OMX_StateExecuting\n"); break; + case OMX_StatePause: DEBUGF("OMX_StatePause\n"); break; + case OMX_StateWaitForResources: DEBUGF("OMX_StateWaitForResources\n"); break; + + default: + DEBUGF("0x%08x\n",(unsigned int)state); + } + + OMX_INIT_STRUCTURE(portdef); + + if (port1) { + portdef.nPortIndex = port1; + OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef)); + DEBUGF("Port %d is %s\n",port1, (portdef.bEnabled == 0 ? "disabled" : "enabled")); + } + + if (port2) { + portdef.nPortIndex = port2; + OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef)); + DEBUGF("Port %d is %s\n",port2, (portdef.bEnabled == 0 ? "disabled" : "enabled")); + } + + if (port3) { + portdef.nPortIndex = port3; + OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef)); + DEBUGF("Port %d is %s\n",port3, (portdef.bEnabled == 0 ? "disabled" : "enabled")); + } +} + + +/* From omxtx */ +/* Print some useful information about the state of the port: */ +static void dumpport(OMX_HANDLETYPE handle, int port) +{ + OMX_VIDEO_PORTDEFINITIONTYPE *viddef; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + OMX_INIT_STRUCTURE(portdef); + portdef.nPortIndex = port; + OERR(OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef)); + + printf("Port %d is %s, %s\n", portdef.nPortIndex, + (portdef.eDir == 0 ? "input" : "output"), + (portdef.bEnabled == 0 ? "disabled" : "enabled")); + printf("Wants %d bufs, needs %d, size %d, enabled: %d, pop: %d, " + "aligned %d\n", portdef.nBufferCountActual, + portdef.nBufferCountMin, portdef.nBufferSize, + portdef.bEnabled, portdef.bPopulated, + portdef.nBufferAlignment); + viddef = &portdef.format.video; + + switch (portdef.eDomain) { + case OMX_PortDomainVideo: + printf("Video type is currently:\n" + "\tMIME:\t\t%s\n" + "\tNative:\t\t%p\n" + "\tWidth:\t\t%d\n" + "\tHeight:\t\t%d\n" + "\tStride:\t\t%d\n" + "\tSliceHeight:\t%d\n" + "\tBitrate:\t%d\n" + "\tFramerate:\t%d (%x); (%f)\n" + "\tError hiding:\t%d\n" + "\tCodec:\t\t%d\n" + "\tColour:\t\t%d\n", + viddef->cMIMEType, viddef->pNativeRender, + viddef->nFrameWidth, viddef->nFrameHeight, + viddef->nStride, viddef->nSliceHeight, + viddef->nBitrate, + viddef->xFramerate, viddef->xFramerate, + ((float)viddef->xFramerate/(float)65536), + viddef->bFlagErrorConcealment, + viddef->eCompressionFormat, viddef->eColorFormat); + break; + case OMX_PortDomainImage: + printf("Image type is currently:\n" + "\tMIME:\t\t%s\n" + "\tNative:\t\t%p\n" + "\tWidth:\t\t%d\n" + "\tHeight:\t\t%d\n" + "\tStride:\t\t%d\n" + "\tSliceHeight:\t%d\n" + "\tError hiding:\t%d\n" + "\tCodec:\t\t%d\n" + "\tColour:\t\t%d\n", + portdef.format.image.cMIMEType, + portdef.format.image.pNativeRender, + portdef.format.image.nFrameWidth, + portdef.format.image.nFrameHeight, + portdef.format.image.nStride, + portdef.format.image.nSliceHeight, + portdef.format.image.bFlagErrorConcealment, + portdef.format.image.eCompressionFormat, + portdef.format.image.eColorFormat); + break; +/* Feel free to add others. */ + default: + break; + } +} + +OMX_ERRORTYPE omx_send_command_and_wait0(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData) +{ + pthread_mutex_lock(&component->cmd_queue_mutex); + component->cmd.hComponent = component->h; + component->cmd.Cmd = Cmd; + component->cmd.nParam = nParam; + component->cmd.pCmdData = pCmdData; + pthread_mutex_unlock(&component->cmd_queue_mutex); + + OMX_SendCommand(component->h, Cmd, nParam, pCmdData); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_send_command_and_wait1(struct omx_component_t* component, __attribute__((unused)) OMX_COMMANDTYPE Cmd, __attribute__((unused)) OMX_U32 nParam, __attribute__((unused)) OMX_PTR pCmdData) +{ + pthread_mutex_lock(&component->cmd_queue_mutex); + while (component->cmd.hComponent) { + /* pthread_cond_wait releases the mutex (which must be locked) and blocks on the condition variable */ + pthread_cond_wait(&component->cmd_queue_count_cv,&component->cmd_queue_mutex); + } + pthread_mutex_unlock(&component->cmd_queue_mutex); + + //fprintf(stderr,"Command completed\n"); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_send_command_and_wait(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData) +{ + omx_send_command_and_wait0(component,Cmd,nParam,pCmdData); + omx_send_command_and_wait1(component,Cmd,nParam,pCmdData); + return OMX_ErrorNone; +} + +//void omx_wait_for_event(struct omx_pipeleine_t* pipe, OMX_HANDLETYPE *hComponent +// //&pipe,video_render, OMXEventBufferFlag, 90, OMX_BUFFERFLAG_EOS) +//{ +// +//} + +/* The event handler is called from the OMX component thread */ +static OMX_ERRORTYPE omx_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) +{ + struct omx_component_t* component = (struct omx_component_t*)pAppData; + +// fprintf(stderr,"[EVENT]: threadid=%u\n",(unsigned int)pthread_self()); + + switch (eEvent) { + case OMX_EventError: + fprintf(stderr,"[EVENT] %s %p has errored: %x\n", component->name, hComponent, (unsigned int)nData1); + //exit(1); +//fprintf(stderr,"[EVENT] Waiting for lock\n"); + pthread_mutex_lock(&component->cmd_queue_mutex); +//fprintf(stderr,"[EVENT] Got lock - cmd.hComponent=%x\n",(unsigned int)pi->cmd.hComponent); + memset(&component->cmd,0,sizeof(component->cmd)); +//fprintf(stderr,"[EVENT] Clearing cmd\n"); + pthread_cond_signal(&component->cmd_queue_count_cv); + pthread_mutex_unlock(&component->cmd_queue_mutex); +//fprintf(stderr,"[EVENT] Returning from event\n"); + return nData1; + break; + + case OMX_EventCmdComplete: + DEBUGF("[EVENT] %s %p has completed the last command (%x).\n", component->name, hComponent, nData1); + +//fprintf(stderr,"[EVENT] Waiting for lock\n"); + pthread_mutex_lock(&component->cmd_queue_mutex); +//fprintf(stderr,"[EVENT] Got lock\n"); + if ((nData1 == component->cmd.Cmd) && + (nData2 == component->cmd.nParam)) { + memset(&component->cmd,0,sizeof(component->cmd)); + pthread_cond_signal(&component->cmd_queue_count_cv); + } + pthread_mutex_unlock(&component->cmd_queue_mutex); + + break; + + case OMX_EventPortSettingsChanged: + DEBUGF("[EVENT] %s %p port %d settings changed.\n", component->name, hComponent, (unsigned int)nData1); + if (component->port_settings_changed == 0) { component->port_settings_changed = 1; } + break; + + case OMX_EventBufferFlag: + DEBUGF("[EVENT] Got an EOS event on %s %p (port %d, d2 %x)\n", component->name, hComponent, (unsigned int)nData1, (unsigned int)nData2); + + if (nData2 & OMX_BUFFERFLAG_EOS) { + pthread_mutex_lock(&component->eos_mutex); + component->eos = 1; + pthread_cond_signal(&component->eos_cv); + pthread_mutex_unlock(&component->eos_mutex); + DEBUGF("Sent cond signal for EOS event\n"); + } + + break; + + case OMX_EventMark: + /* TODO: this cast is clearly wrong */ + component->aspect = (int)((OMX_MARKTYPE*)pEventData); + //fprintf(stderr,"[EVENT] OMX_EventMark - component->aspect=%d\n",component->aspect); + break; + + case OMX_EventParamOrConfigChanged: + DEBUGF("[EVENT] OMX_EventParamOrConfigChanged on component \"%s\" - d1=%x, d2=%x\n",component->name,(unsigned int)nData1, (unsigned int)nData2); + component->config_changed = 1; + break; + + default: + DEBUGF("[EVENT] Got an event of type %x on %s %p (d1: %x, d2 %x)\n", eEvent, + component->name, hComponent, (unsigned int)nData1, (unsigned int)nData2); + } + + return OMX_ErrorNone; +} + +static OMX_ERRORTYPE omx_empty_buffer_done(__attribute__((unused)) OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + __attribute__((unused)) OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + struct omx_component_t* component = (struct omx_component_t*)pAppData; + + if (component->buf_notempty == 0) { + pthread_mutex_lock(&component->buf_mutex); + component->buf_notempty = 1; + pthread_cond_signal(&component->buf_notempty_cv); + pthread_mutex_unlock(&component->buf_mutex); + } + return OMX_ErrorNone; +} + +void omx_clock_set_speed(struct omx_component_t *clk, int v) +{ + OMX_TIME_CONFIG_SCALETYPE scale; + OMX_INIT_STRUCTURE(scale); + if (! clk->h) { + fprintf(stderr, "omx_clock_set_speed: clock->h is null!\n"); + return; + } + + scale.xScale = v; + OERR(OMX_SetConfig(clk->h, OMX_IndexConfigTimeScale, &scale)); +} + +static OMX_ERRORTYPE omx_fill_buffer_done(__attribute__((unused)) OMX_IN OMX_HANDLETYPE hComponent, + __attribute__((unused)) OMX_IN OMX_PTR pAppData, + __attribute__((unused)) OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + DEBUGF("[omx_fill_buffer_done]\n"); + return OMX_ErrorNone; +} + +/* Function based on ilclient_create_component() */ +static void omx_disable_all_ports(struct omx_component_t* component) +{ + 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(component->h, types[i], &ports); + if(error == OMX_ErrorNone) { + uint32_t j; + for(j=0; jh, OMX_IndexParamPortDefinition, &portdef)); + + if (component == &component->pipe->audio_render) { + DEBUGF("Allocating %d buffers of %d bytes\n",(int)portdef.nBufferCountActual,(int)portdef.nBufferSize); + DEBUGF("portdef.bEnabled=%d\n",portdef.bEnabled); + } + + for (i = 0; i < portdef.nBufferCountActual; i++) { + OMX_U8 *buf; + + buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, "buffer"); + + // printf("Allocated a buffer of %u bytes\n",(unsigned int)portdef.nBufferSize); + + OERR(OMX_UseBuffer(component->h, end, port, NULL, portdef.nBufferSize, buf)); + + end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate); + } + + component->buffers = list; +} + +void omx_free_buffers(struct omx_component_t *component, int port) +{ + OMX_BUFFERHEADERTYPE *buf, *prev; +// int i=0; + + buf = component->buffers; + while (buf) { + prev = buf->pAppPrivate; + OERR(OMX_FreeBuffer(component->h, port, buf)); /* This also calls free() */ + buf = prev; + } +} + +int omx_get_free_buffer_count(struct omx_component_t* component) +{ + int n = 0; + OMX_BUFFERHEADERTYPE *buf = component->buffers; + + pthread_mutex_lock(&component->buf_mutex); + while (buf) { + if (buf->nFilledLen == 0) n++; + buf = buf->pAppPrivate; + } + pthread_mutex_unlock(&component->buf_mutex); + + return n; +} + +#if 0 +static void dump_buffer_status(OMX_BUFFERHEADERTYPE *buffers) +{ + OMX_BUFFERHEADERTYPE *buf = buffers; + + while (buf) { + fprintf(stderr,"*****\n"); + fprintf(stderr,"buf->pAppPrivate=%u\n",(unsigned int)buf->pAppPrivate); + fprintf(stderr,"buf->nAllocLen=%u\n",(unsigned int)buf->nAllocLen); + fprintf(stderr,"buf->nFilledLen=%u\n",(unsigned int)buf->nFilledLen); + fprintf(stderr,"buf->pBuffer=%u\n",(unsigned int)buf->pBuffer); + fprintf(stderr,"buf->nOffset=%u\n",(unsigned int)buf->nOffset); + fprintf(stderr,"buf->nFlags=%u\n",(unsigned int)buf->nFlags); + fprintf(stderr,"buf->nInputPortIndex=%u\n",(unsigned int)buf->nInputPortIndex); + fprintf(stderr,"buf->nOutputPortIndex=%u\n",(unsigned int)buf->nOutputPortIndex); + buf = buf->pAppPrivate; + } +} +#endif + +void summarise_buffers(OMX_BUFFERHEADERTYPE *buffers) +{ + OMX_BUFFERHEADERTYPE *buf = buffers; + + fprintf(stderr,"*******\n"); + while (buf) { + fprintf(stderr,"buf->nFilledLen=%u\n",(unsigned int)buf->nFilledLen); + buf = buf->pAppPrivate; + } +} + +/* Return the next free buffer, or NULL if none are free */ +OMX_BUFFERHEADERTYPE *get_next_buffer(struct omx_component_t* component) +{ + OMX_BUFFERHEADERTYPE *ret; + +retry: + pthread_mutex_lock(&component->buf_mutex); + + ret = component->buffers; + while (ret && ret->nFilledLen > 0) + ret = ret->pAppPrivate; + + if (!ret) + component->buf_notempty = 0; + + if (ret) { + pthread_mutex_unlock(&component->buf_mutex); + return ret; + } + + //summarise_buffers(pi->video_buffers); + while (component->buf_notempty == 0) + pthread_cond_wait(&component->buf_notempty_cv,&component->buf_mutex); + + pthread_mutex_unlock(&component->buf_mutex); + + goto retry; + + /* We never get here, but keep GCC happy */ + return NULL; +} + +OMX_ERRORTYPE omx_flush_tunnel(struct omx_component_t* source, int source_port, struct omx_component_t* sink, int sink_port) +{ + omx_send_command_and_wait(source,OMX_CommandFlush,source_port,NULL); + omx_send_command_and_wait(sink,OMX_CommandFlush,sink_port,NULL); + return OMX_ErrorNone; +} + +static void omx_config_pcm(struct omx_component_t* audio_render, int samplerate, int channels, int bitdepth, char* dest) +{ + OMX_AUDIO_PARAM_PCMMODETYPE pcm; +// int32_t s; + + OMX_INIT_STRUCTURE(pcm); + pcm.nPortIndex = 100; + pcm.nChannels = channels; + pcm.eNumData = OMX_NumericalDataSigned; + pcm.eEndian = OMX_EndianLittle; + pcm.nSamplingRate = samplerate; + pcm.bInterleaved = OMX_TRUE; + pcm.nBitPerSample = bitdepth; + pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + + switch(channels) { + case 1: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 8: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcm.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcm.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + pcm.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + pcm.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + pcm.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + break; + case 4: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelLR; + pcm.eChannelMapping[3] = OMX_AUDIO_ChannelRR; + break; + case 2: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + break; + } + + OERR(OMX_SetParameter(audio_render->h, OMX_IndexParamAudioPcm, &pcm)); + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest; + OMX_INIT_STRUCTURE(ar_dest); + strcpy((char *)ar_dest.sName, dest); + OERR(OMX_SetConfig(audio_render->h, OMX_IndexConfigBrcmAudioDestination, &ar_dest)); +} + +void omx_audio_volume(struct omx_component_t* audio_render, long vol) +{ + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCTURE(volume); + volume.nPortIndex = 100; + volume.sVolume.nValue = vol; + + OMX_ERRORTYPE omx_err = OMX_SetParameter(audio_render->h, OMX_IndexConfigAudioVolume, &volume); + + if (omx_err != OMX_ErrorNone) + printf("[acodec] omx_audio_volume failed 0x%x\n", omx_err); +} + +void omx_set_display_region(struct omx_pipeline_t* pi, int x, int y, int width, int height) +{ + OMX_CONFIG_DISPLAYREGIONTYPE region; + + OMX_INIT_STRUCTURE(region); + region.nPortIndex = 90; /* Video render input port */ + + region.set = OMX_DISPLAY_SET_DEST_RECT | OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_NOASPECT; + + region.fullscreen = OMX_FALSE; + region.noaspect = OMX_TRUE; + + region.dest_rect.x_offset = x; + region.dest_rect.y_offset = y; + region.dest_rect.width = width; + region.dest_rect.height = height; + + DEBUGF("Setting display region\n"); + OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexConfigDisplayRegion, ®ion)); +} + +OMX_ERRORTYPE omx_init_component(struct omx_pipeline_t* pi, struct omx_component_t* component, char* compname) +{ + memset(component,0,sizeof(struct omx_component_t)); + + pthread_mutex_init(&component->cmd_queue_mutex, NULL); + pthread_cond_init(&component->cmd_queue_count_cv,NULL); + component->buf_notempty = 1; + pthread_cond_init(&component->buf_notempty_cv,NULL); + pthread_cond_init(&component->eos_cv,NULL); + pthread_mutex_init(&component->eos_mutex,NULL); + + component->callbacks.EventHandler = omx_event_handler; + component->callbacks.EmptyBufferDone = omx_empty_buffer_done; + component->callbacks.FillBufferDone = omx_fill_buffer_done; + + component->pipe = pi; + + component->name = compname; + + /* Create OMX component */ + OERR(OMX_GetHandle(&component->h, compname, component, &component->callbacks)); + + /* Disable all ports */ + omx_disable_all_ports(component); + + return OMX_ErrorNone; +} + + +/* From: http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/camera.html + + In order to optimise loading of relevant drivers, the recommended initialisation sequence is: + + Create component. + Use OMX_IndexConfigRequestCallback to request callbacks on OMX_IndexParamCameraDeviceNumber. + Set OMX_IndexParamISPTunerName. + Set OMX_IndexParamCameraFlashType. + Set OMX_IndexParamCameraDeviceNumber. + Wait for the callback that OMX_IndexParamCameraDeviceNumber has changed. At this point, all the drivers have been loaded. Other settings can be applied whilst waiting for this event. + Query for OMX_IndexConfigCameraSensorModes as required. + Change state to IDLE, and proceed as required. +*/ + +OMX_ERRORTYPE omx_setup_camera_pipeline(struct omx_pipeline_t* pi) +{ + + // Create component. + omx_init_component(pi, &pi->camera, "OMX.broadcom.camera"); + + // Use OMX_IndexConfigRequestCallback to request callbacks on OMX_IndexParamCameraDeviceNumber. + OMX_CONFIG_REQUESTCALLBACKTYPE cbtype; + OMX_INIT_STRUCTURE(cbtype); + cbtype.nPortIndex=OMX_ALL; + cbtype.nIndex=OMX_IndexParamCameraDeviceNumber; + cbtype.bEnable = OMX_TRUE; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigRequestCallback, &cbtype)); + + // Set OMX_IndexParamISPTunerName. + + // Set OMX_IndexParamCameraFlashType. + + // Set OMX_IndexParamCameraDeviceNumber. + OMX_PARAM_U32TYPE device; + OMX_INIT_STRUCTURE(device); + device.nPortIndex = OMX_ALL; + device.nU32 = 0; + OERR(OMX_SetParameter(pi->camera.h, OMX_IndexParamCameraDeviceNumber, &device)); + + dumpport(pi->camera.h, 71); + + /* Set the resolution */ + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_INIT_STRUCTURE(portdef); + portdef.nPortIndex = 71; + OERR(OMX_GetParameter(pi->camera.h, OMX_IndexParamPortDefinition, &portdef)); + portdef.format.image.nFrameWidth = 640; + portdef.format.image.nFrameHeight = 360; + portdef.format.image.nStride = 640; + OERR(OMX_SetParameter(pi->camera.h, OMX_IndexParamPortDefinition, &portdef)); + + /* Set the framerate */ + OMX_CONFIG_FRAMERATETYPE framerate; + OMX_INIT_STRUCTURE(framerate); + framerate.nPortIndex = 71; + framerate.xEncodeFramerate = 25 << 16; // Q16 format - 25fps + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigVideoFramerate, &framerate)); + + /* Set the sharpness */ + OMX_CONFIG_SHARPNESSTYPE sharpness; + OMX_INIT_STRUCTURE(sharpness); + sharpness.nPortIndex = OMX_ALL; + sharpness.nSharpness = -50; /* -100 to 100 */ + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonSharpness, &sharpness)); + + /* Set the contrast */ + OMX_CONFIG_CONTRASTTYPE contrast; + OMX_INIT_STRUCTURE(contrast); + contrast.nPortIndex = OMX_ALL; + contrast.nContrast = -10; /* -100 to 100 */ + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonContrast, &contrast)); + + /* Set the brightness */ + OMX_CONFIG_BRIGHTNESSTYPE brightness; + OMX_INIT_STRUCTURE(brightness); + brightness.nPortIndex = OMX_ALL; + brightness.nBrightness = 50; /* 0 to 100 */ + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonBrightness, &brightness)); + + /* Set the saturation */ + OMX_CONFIG_SATURATIONTYPE saturation; + OMX_INIT_STRUCTURE(saturation); + saturation.nPortIndex = OMX_ALL; + saturation.nSaturation = 0; /* -100 to 100 */ + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonSaturation, &saturation)); + + /* Video stabilisation */ + OMX_CONFIG_FRAMESTABTYPE framestab; + OMX_INIT_STRUCTURE(framestab); + framestab.nPortIndex = OMX_ALL; + framestab.bStab = OMX_FALSE; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonFrameStabilisation, &framestab)); + + /* Set EV compensation, ISO and metering mode */ + OMX_CONFIG_EXPOSUREVALUETYPE exposurevalue; + OMX_INIT_STRUCTURE(exposurevalue); + exposurevalue.nPortIndex = OMX_ALL; + OERR(OMX_GetConfig(pi->camera.h, OMX_IndexConfigCommonExposureValue, &exposurevalue)); + fprintf(stderr,"nSensitivity=%d\n",exposurevalue.nSensitivity); + exposurevalue.xEVCompensation = 0; /* Fixed point value stored as Q16 */ + exposurevalue.nSensitivity = 100; /**< e.g. nSensitivity = 100 implies "ISO 100" */ + exposurevalue.bAutoSensitivity = OMX_FALSE; + exposurevalue.eMetering = OMX_MeteringModeAverage; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonExposureValue, &exposurevalue)); + + /* Set exposure mode */ + OMX_CONFIG_EXPOSURECONTROLTYPE exposure; + OMX_INIT_STRUCTURE(exposure); + exposure.nPortIndex = OMX_ALL; + exposure.eExposureControl = OMX_ExposureControlAuto; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonExposure, &exposure)); + + /* Set AWB mode */ + OMX_CONFIG_WHITEBALCONTROLTYPE awb; + OMX_INIT_STRUCTURE(awb); + awb.nPortIndex = OMX_ALL; + awb.eWhiteBalControl = OMX_WhiteBalControlAuto; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonWhiteBalance, &awb)); + + /* Set image effect */ + OMX_CONFIG_IMAGEFILTERTYPE imagefilter; + OMX_INIT_STRUCTURE(imagefilter); + imagefilter.nPortIndex = OMX_ALL; + imagefilter.eImageFilter = OMX_ImageFilterNone; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonImageFilter, &imagefilter)); + + /* Set colour effect */ + OMX_CONFIG_COLORENHANCEMENTTYPE colour; + OMX_INIT_STRUCTURE(colour); + colour.nPortIndex = OMX_ALL; + colour.bColorEnhancement = OMX_FALSE; + colour.nCustomizedU = 128; + colour.nCustomizedV = 128; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonColorEnhancement, &colour)); + + /* Turn off the LED - doesn't work! */ + OMX_CONFIG_PRIVACYINDICATORTYPE privacy; + OMX_INIT_STRUCTURE(privacy); + privacy.ePrivacyIndicatorMode = OMX_PrivacyIndicatorOff; + OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigPrivacyIndicator, &privacy)); + + // Wait for the callback that OMX_IndexParamCameraDeviceNumber has + // changed. At this point, all the drivers have been loaded. Other + // settings can be applied whilst waiting for this event. + fprintf(stderr,"Waiting for camera config to change\n"); + while (!pi->camera.config_changed); /* TODO: Use a condition variable */ + fprintf(stderr,"Config changed\n"); + + // Query for OMX_IndexConfigCameraSensorModes as required. + + // Change state to IDLE, and proceed as required. + omx_send_command_and_wait(&pi->camera, OMX_CommandStateSet, OMX_StateIdle, NULL); + + OMX_CONFIG_PORTBOOLEANTYPE cameraport; + OMX_INIT_STRUCTURE(cameraport); + cameraport.nPortIndex = 71; + cameraport.bEnabled = OMX_TRUE; + OERR(OMX_SetParameter(pi->camera.h, OMX_IndexConfigPortCapturing, &cameraport)); + + omx_init_component(pi, &pi->video_render, "OMX.broadcom.video_render"); + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + + OERR(OMX_SetupTunnel(pi->camera.h, 71, pi->video_render.h, 90)); /* Camera capture port to video render */ + + omx_send_command_and_wait(&pi->camera, OMX_CommandPortEnable, 71, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandPortEnable, 90, NULL); + + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateExecuting, NULL); + omx_send_command_and_wait(&pi->camera, OMX_CommandStateSet, OMX_StateExecuting, NULL); + + omx_set_display_region(pi, 1200, 180, 640, 360); + + OMX_CONFIG_DISPLAYREGIONTYPE region; + OMX_INIT_STRUCTURE(region); + region.nPortIndex = 90; /* Video render input port */ + region.set = OMX_DISPLAY_SET_LAYER; + region.layer = 10; + OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexConfigDisplayRegion, ®ion)); + + fprintf(stderr,"Camera pipeline configured\n"); + + dumpport(pi->camera.h, 71); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_setup_pipeline(struct omx_pipeline_t* pi, OMX_VIDEO_CODINGTYPE video_codec, char* audio_dest, __attribute__((unused)) int is_hd) +{ + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + + OMX_CONFIG_BOOLEANTYPE configBoolTrue; + OMX_INIT_STRUCTURE(configBoolTrue); + configBoolTrue.bEnabled = OMX_TRUE; + + pi->do_deinterlace = 0; + +#if 0 + if (((is_hd == 0) && (global_settings.deinterlace_sd)) || ((is_hd == 1) && (global_settings.deinterlace_hd))) { + DEBUGF("Enabling de-interlace\n"); + pi->do_deinterlace = 1; + } +#endif + + omx_init_component(pi, &pi->video_decode, "OMX.broadcom.video_decode"); + omx_init_component(pi, &pi->video_render, "OMX.broadcom.video_render"); + + if (pi->do_deinterlace) { + DEBUGF("Enabling de-interlacer\n"); + /* De-interlacer. Input port 190, Output port 191. Insert between decoder and scheduler */ + omx_init_component(pi, &pi->image_fx, "OMX.broadcom.image_fx"); + + /* Configure image_fx */ + omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateIdle, NULL); + + OMX_CONFIG_IMAGEFILTERPARAMSTYPE imagefilter; + OMX_INIT_STRUCTURE(imagefilter); + imagefilter.nPortIndex=191; + imagefilter.nNumParams=1; + imagefilter.nParams[0]=3; //??? + imagefilter.eImageFilter=OMX_ImageFilterDeInterlaceAdvanced; + + OERR(OMX_SetConfig(pi->image_fx.h, OMX_IndexConfigCommonImageFilterParameters, &imagefilter)); + } else { + memset(&pi->image_fx,0,sizeof(struct omx_component_t)); + } + + + omx_init_component(pi, &pi->clock, "OMX.broadcom.clock"); + + OMX_INIT_STRUCTURE(cstate); + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = OMX_CLOCKPORT0|OMX_CLOCKPORT1; + OERR(OMX_SetParameter(pi->clock.h, OMX_IndexConfigTimeClockState, &cstate)); + + OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; + OMX_INIT_STRUCTURE(refClock); + refClock.eClock = OMX_TIME_RefClockAudio; + OERR(OMX_SetConfig(pi->clock.h, OMX_IndexConfigTimeActiveRefClock, &refClock)); + + omx_init_component(pi, &pi->video_scheduler, "OMX.broadcom.video_scheduler"); + + /* Initialise audio output - hardcoded to 48000/Stereo/16-bit */ + omx_init_component(pi, &pi->audio_render, "OMX.broadcom.audio_render"); + + OMX_PARAM_PORTDEFINITIONTYPE param; + OMX_INIT_STRUCTURE(param); + param.nPortIndex = 100; + + OERR(OMX_GetParameter(pi->audio_render.h, OMX_IndexParamPortDefinition, ¶m)); + param.nBufferSize = 8192; /* Needs to be big enough for one frame of data */ + param.nBufferCountActual = 32; /* Arbitrary */ + OERR(OMX_SetParameter(pi->audio_render.h, OMX_IndexParamPortDefinition, ¶m)); + + omx_config_pcm(&pi->audio_render, 48000, 2, 16, audio_dest); + + OERR(OMX_SetConfig(pi->audio_render.h, OMX_IndexConfigBrcmClockReferenceSource, &configBoolTrue)); + + omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + + omx_send_command_and_wait0(&pi->audio_render, OMX_CommandPortEnable, 100, NULL); + omx_alloc_buffers(&pi->audio_render, 100); + omx_send_command_and_wait1(&pi->audio_render, OMX_CommandPortEnable, 100, NULL); + + + /* Setup clock tunnels first */ + omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateIdle, NULL); + + OERR(OMX_SetupTunnel(pi->clock.h, 80, pi->audio_render.h, 101)); + OERR(OMX_SetupTunnel(pi->clock.h, 81, pi->video_scheduler.h, 12)); + + OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortEnable, 80, NULL)); + OERR(OMX_SendCommand(pi->video_scheduler.h, OMX_CommandPortEnable, 12, NULL)); + + OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortEnable, 81, NULL)); + OERR(OMX_SendCommand(pi->audio_render.h, OMX_CommandPortEnable, 101, NULL)); + + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL); + + omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateExecuting, NULL); + + /* Configure video_decoder */ + omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateIdle, NULL); + + /* Enable lazy image pool destroying */ + OERR(OMX_SetConfig(pi->video_decode.h, OMX_IndexParamBrcmLazyImagePoolDestroy, &configBoolTrue)); + + OMX_INIT_STRUCTURE(format); + format.nPortIndex = 130; + format.eCompressionFormat = video_codec; + + OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamVideoPortFormat, &format)); + + /* Enable error concealment for H264 only - without this, HD channels don't work reliably */ + if (video_codec == OMX_VIDEO_CodingAVC) { + OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ec; + OMX_INIT_STRUCTURE(ec); + ec.bStartWithValidFrame = OMX_FALSE; + OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ec)); + } + + /* Enable video decoder input port */ + omx_send_command_and_wait0(&pi->video_decode, OMX_CommandPortEnable, 130, NULL); + + /* Allocate input buffers */ + omx_alloc_buffers(&pi->video_decode, 130); + + /* Wait for input port to be enabled */ + omx_send_command_and_wait1(&pi->video_decode, OMX_CommandPortEnable, 130, NULL); + + /* Change video_decode to OMX_StateExecuting */ + omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateExecuting, NULL); + + /* Change audio_render to OMX_StateExecuting */ + omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateExecuting, NULL); + + /* Enable passing of buffer marks */ + OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamPassBufferMarks, &configBoolTrue)); + OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexParamPassBufferMarks, &configBoolTrue)); + + return OMX_ErrorNone; +} + +void omx_teardown_pipeline(struct omx_pipeline_t* pi) +{ +// OMX_BUFFERHEADERTYPE *buf; +// int i=1; + + DEBUGF("[vcodec] omx_teardown pipeline:\n"); + DEBUGF("pipe->video_decode.port_settings_changed = %d\n",pi->video_decode.port_settings_changed); + DEBUGF("pipe->image_fx.port_settings_changed = %d\n",pi->image_fx.port_settings_changed); + DEBUGF("pipe->video_scheduler.port_settings_changed = %d\n",pi->video_scheduler.port_settings_changed); + //dumpport(pi->video_decode.h,130); + +#if 0 + /* Indicate end of video stream */ + buf = get_next_buffer(&pi->video_decode); + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + OERR(OMX_EmptyThisBuffer(pi->video_decode.h, buf)); + + /* NOTE: Three events are sent after the previous command: + + [EVENT] Got an event of type 4 on video_decode 0x426a10 (d1: 83, d2 1) + [EVENT] Got an event of type 4 on video_scheduler 0x430d10 (d1: b, d2 1) + [EVENT] Got an event of type 4 on video_render 0x430b30 (d1: 5a, d2 1) 5a = port (90) 1 = OMX_BUFFERFLAG_EOS + */ + +#endif + +#if 0 + DEBUGF("[vcodec] omx_teardown pipeline 2\n"); + /* Wait for video_decode to shutdown */ + pthread_mutex_lock(&pi->video_decode.eos_mutex); + while (!pi->video_decode.eos) + pthread_cond_wait(&pi->video_decode.eos_cv,&pi->video_decode.eos_mutex); + pthread_mutex_unlock(&pi->video_decode.eos_mutex); +#endif + + DEBUGF("[vcodec] omx_teardown pipeline 1\n"); + + /* Transition all components to Idle, if they have been initialised */ + omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateIdle, NULL); + omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateIdle, NULL); + DEBUGF("pipe->do_deinterlace=%d, pipe->image_fx=%d\n",pi->do_deinterlace,(int)pi->image_fx.h); + if (pi->video_decode.port_settings_changed == 2) { + if (pi->do_deinterlace) { + omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateIdle, NULL); + } else { + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL); + } + } + if (pi->image_fx.port_settings_changed == 2) { + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL); + } + if (pi->video_scheduler.port_settings_changed == 2) { + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + } + omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateIdle, NULL); + +#if 0 + DEBUGF("[vcodec] omx_teardown pipeline 2\n"); + /* Wait for video_render to shutdown */ + pthread_mutex_lock(&pi->video_render.eos_mutex); + while (!pi->video_render.eos) + pthread_cond_wait(&pi->video_render.eos_cv,&pi->video_render.eos_mutex); + pthread_mutex_unlock(&pi->video_render.eos_mutex); +#endif + +/* + Pipeline is as follows: + +[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render + clock 81 -> 12 video_scheduler + clock 80 -> 101 audio_render + [audio data] -> 100 audio_render +*/ + + /* Flush entrances to pipeline */ + omx_send_command_and_wait(&pi->video_decode,OMX_CommandFlush,130,NULL); + omx_send_command_and_wait(&pi->audio_render,OMX_CommandFlush,100,NULL); + + /* Flush all tunnels */ + DEBUGF("[vcodec] omx_teardown pipeline 3\n"); + if (pi->do_deinterlace) { + omx_flush_tunnel(&pi->video_decode, 131, &pi->image_fx, 190); + omx_flush_tunnel(&pi->image_fx, 191, &pi->video_scheduler, 10); + } else { + omx_flush_tunnel(&pi->video_decode, 131, &pi->video_scheduler, 10); + } + DEBUGF("[vcodec] omx_teardown pipeline 4\n"); + omx_flush_tunnel(&pi->video_scheduler, 11, &pi->video_render, 90); + omx_flush_tunnel(&pi->clock, 81, &pi->video_scheduler, 12); + + DEBUGF("[vcodec] omx_teardown pipeline 2b\n"); + + omx_send_command_and_wait(&pi->video_scheduler,OMX_CommandFlush,10,NULL); + DEBUGF("[vcodec] omx_teardown pipeline 5\n"); + + omx_flush_tunnel(&pi->clock, 80, &pi->audio_render, 101); + + /* Disable audio_render input port and buffers */ + omx_send_command_and_wait0(&pi->audio_render, OMX_CommandPortDisable, 100, NULL); + omx_free_buffers(&pi->audio_render, 100); + omx_send_command_and_wait1(&pi->audio_render, OMX_CommandPortDisable, 100, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 9\n"); + + /* Scheduler -> render tunnel */ + if (pi->video_scheduler.port_settings_changed == 2) { + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 11, NULL); + omx_send_command_and_wait(&pi->video_render, OMX_CommandPortDisable, 90, NULL); + + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 10, NULL); + } + + if ((pi->image_fx.port_settings_changed == 2) && (pi->do_deinterlace)) { + omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortDisable, 190, NULL); + omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortDisable, 191, NULL); + } + + DEBUGF("[vcodec] omx_teardown pipeline 8a\n"); + + //dumpport(pi->video_scheduler.h,10); + + /* Teardown tunnels */ +/* + Pipeline is as follows: + +[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render + clock 81 -> 12 video_scheduler + clock 80 -> 101 audio_render + [audio data] -> 100 audio_render +*/ + //dumpport(pi->video_decode.h,131); + OERR(OMX_SetupTunnel(pi->video_scheduler.h, 10, NULL, 0)); + + DEBUGF("[vcodec] omx_teardown pipeline 10\n"); + + /* NOTE: The clock disable doesn't complete until after the video scheduler port is + disabled (but it completes before the video scheduler port disabling completes). */ + OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortDisable, 80, NULL)); + omx_send_command_and_wait(&pi->audio_render, OMX_CommandPortDisable, 101, NULL); + OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortDisable, 81, NULL)); + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 12, NULL); + + DEBUGF("[vcodec] omx_teardown pipeline 12b\n"); + + if (pi->do_deinterlace) { + OERR(OMX_SetupTunnel(pi->image_fx.h, 190, NULL, 0)); + OERR(OMX_SetupTunnel(pi->image_fx.h, 191, NULL, 0)); + } + + DEBUGF("[vcodec] omx_teardown pipeline 13\n"); + + OERR(OMX_SetupTunnel(pi->video_scheduler.h, 11, NULL, 0)); + OERR(OMX_SetupTunnel(pi->video_render.h, 90, NULL, 0)); + + OERR(OMX_SetupTunnel(pi->clock.h, 81, NULL, 0)); + OERR(OMX_SetupTunnel(pi->video_scheduler.h, 12, NULL, 0)); + + DEBUGF("[vcodec] omx_teardown pipeline 13b\n"); + + OERR(OMX_SetupTunnel(pi->clock.h, 80, NULL, 0)); + OERR(OMX_SetupTunnel(pi->audio_render.h, 101, NULL, 0)); + + DEBUGF("[vcodec] omx_teardown pipeline 8b\n"); + + +/* + Pipeline is as follows: + +[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render + clock 81 -> 12 video_scheduler + clock 80 -> 101 audio_render + [audio data] -> 100 audio_render +*/ + + omx_show_state(&pi->video_decode,130,131,0); + dumpport(pi->video_decode.h,131); + omx_show_state(&pi->video_scheduler,10,11,12); + if (pi->do_deinterlace) { omx_show_state(&pi->image_fx,190,191,0); } + omx_show_state(&pi->video_render,90,0,0); + omx_show_state(&pi->audio_render,100,101,0); + omx_show_state(&pi->clock,80,81,0); + + if (pi->video_decode.port_settings_changed == 2) { + //dumpport(pi->video_decode.h,131); + omx_send_command_and_wait(&pi->video_decode, OMX_CommandPortDisable, 131, NULL); + } + + DEBUGF("[vcodec] omx_teardown pipeline 11\n"); + + /* Disable video_decode input port and buffers */ + //dumpport(pi->video_decode.h,130); + omx_send_command_and_wait0(&pi->video_decode, OMX_CommandPortDisable, 130, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 6\n"); + omx_free_buffers(&pi->video_decode, 130); + DEBUGF("[vcodec] omx_teardown pipeline 7\n"); + //omx_send_command_and_wait1(&pi->video_decode, OMX_CommandPortDisable, 130, NULL); + + //dumpport(pi->video_decode.h,130); + if (is_port_enabled(pi->video_decode.h, 130)) { + fprintf(stderr,"Unexpected error video_decode port 130 is not disabled\n"); + exit(1); + } + + DEBUGF("[vcodec] omx_teardown pipeline 12\n"); + + OERR(OMX_SetupTunnel(pi->video_decode.h, 131, NULL, 0)); + + DEBUGF("[vcodec] omx_teardown pipeline 15\n"); + + omx_show_state(&pi->video_decode,130,131,0); + + /* Transition all components to Loaded */ + DEBUGF("[vcodec] omx_teardown pipeline 15a\n"); + omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateLoaded, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 15b\n"); + omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateLoaded, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 15c\n"); + if (((pi->video_decode.port_settings_changed == 2) && (pi->do_deinterlace)) || (pi->image_fx.port_settings_changed == 2)) { + omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateLoaded, NULL); + } + DEBUGF("[vcodec] omx_teardown pipeline 15d\n"); + omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateLoaded, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 15e\n"); + omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateLoaded, NULL); + DEBUGF("[vcodec] omx_teardown pipeline 15f\n"); + if (pi->do_deinterlace) { omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateLoaded, NULL); } + + DEBUGF("[vcodec] omx_teardown pipeline 16\n"); + /* Finally free the component handles */ + OERR(OMX_FreeHandle(pi->video_decode.h)); + OERR(OMX_FreeHandle(pi->video_scheduler.h)); + OERR(OMX_FreeHandle(pi->video_render.h)); + OERR(OMX_FreeHandle(pi->audio_render.h)); + OERR(OMX_FreeHandle(pi->clock.h)); + if (pi->do_deinterlace) { OERR(OMX_FreeHandle(pi->image_fx.h)); } + DEBUGF("[vcodec] omx_teardown pipeline 17\n"); +} diff --git a/raspi/omx_utils.h b/raspi/omx_utils.h new file mode 100644 index 0000000..00b1432 --- /dev/null +++ b/raspi/omx_utils.h @@ -0,0 +1,124 @@ +/* + * this code is originally from + * pidvbip - tvheadend client for the Raspberry Pi + * (C) Dave Chapman 2012-2013 + * + * adaption for libstb-hal + * (C) Stefan Seyfried 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * omx_utils.h -- OMX helper functions for the Raspberry Pi + */ + +#ifndef __OMX_UTILS_H +#define __OMX_UTILS_H + +#include +#include + +#define OMX_MIN(a,b) (((a) < (b)) ? (a) : (b)) + +/* Macro borrowed from omxtx */ +#define OERR(cmd) do { \ + OMX_ERRORTYPE oerr = cmd; \ + if (oerr != OMX_ErrorNone) { \ + fprintf(stderr, #cmd " failed on line %d: %x\n", __LINE__, oerr); \ + exit(1); \ + } \ + } while (0) + +/* Macro borrowed from omxplayer */ +#define OMX_INIT_STRUCTURE(a) \ + memset(&(a), 0, sizeof(a)); \ + (a).nSize = sizeof(a); \ + (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (a).nVersion.s.nStep = OMX_VERSION_STEP + + +struct omx_cmd_t +{ + OMX_HANDLETYPE *hComponent; + OMX_COMMANDTYPE Cmd; + OMX_U32 nParam; + OMX_PTR pCmdData; +}; + +struct omx_component_t +{ + OMX_HANDLETYPE h; + OMX_CALLBACKTYPE callbacks; + + char* name; + /* Variables for handling asynchronous commands */ + struct omx_cmd_t cmd; + pthread_mutex_t cmd_queue_mutex; + pthread_cond_t cmd_queue_count_cv; + + /* Pointer to parent pipeline */ + struct omx_pipeline_t* pipe; + + OMX_BUFFERHEADERTYPE *buffers; + int port_settings_changed; + int config_changed; + + int aspect; /* Last aspect ratio reported from video_render MarkEvent callback */ + + pthread_mutex_t buf_mutex; + int buf_notempty; + pthread_cond_t buf_notempty_cv; + + pthread_mutex_t eos_mutex; + int eos; + pthread_cond_t eos_cv; +}; + +struct omx_pipeline_t +{ + struct omx_component_t video_decode; + struct omx_component_t video_scheduler; + struct omx_component_t video_render; + struct omx_component_t audio_render; + struct omx_component_t clock; + struct omx_component_t image_fx; /* For deinterlacing */ + + struct omx_component_t camera; + + int do_deinterlace; + pthread_mutex_t omx_active_mutex; + int omx_active; + pthread_cond_t omx_active_cv; + double channel_switch_starttime; +}; + +OMX_ERRORTYPE omx_init_component(struct omx_pipeline_t* pipe, struct omx_component_t* component, char* compname); +OMX_ERRORTYPE omx_setup_pipeline(struct omx_pipeline_t* pipe, OMX_VIDEO_CODINGTYPE video_codec, char* audio_dest, int is_hd); +void omx_teardown_pipeline(struct omx_pipeline_t* pipe); +OMX_BUFFERHEADERTYPE *get_next_buffer(struct omx_component_t* component); +OMX_ERRORTYPE omx_flush_tunnel(struct omx_component_t* source, int source_port, struct omx_component_t* sink, int sink_port); +void omx_free_buffers(struct omx_component_t *component, int port); +OMX_ERRORTYPE omx_send_command_and_wait(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData); +OMX_ERRORTYPE omx_send_command_and_wait0(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData); +OMX_ERRORTYPE omx_send_command_and_wait1(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData); +void omx_clock_set_speed(struct omx_component_t *clock, int v); +void summarise_buffers(OMX_BUFFERHEADERTYPE *buffers); +int omx_get_free_buffer_count(struct omx_component_t* component); +void omx_alloc_buffers(struct omx_component_t *component, int port); +OMX_TICKS pts_to_omx(uint64_t pts); +void omx_set_display_region(struct omx_pipeline_t* pipe, int x, int y, int width, int height); +void omx_audio_volume(struct omx_component_t* audio_render, long volume); + +#endif diff --git a/raspi/video.cpp b/raspi/video.cpp index 48b58cd..ed97c38 100644 --- a/raspi/video.cpp +++ b/raspi/video.cpp @@ -23,15 +23,9 @@ #include #include -#include - -#include -extern "C" { -#include "ilclient.h" -} - #include "video_lib.h" #include "dmx_lib.h" +#include "avdec.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) @@ -41,31 +35,7 @@ cVideo *videoDecoder = NULL; int system_rev = 0; extern cDemux *videoDemux; -static int dec_running = false; - - -class Dec: public OpenThreads::Thread -{ - public: - Dec(); - ~Dec(); - private: - void run(); -}; - -Dec::Dec() -{ - start(); -} - -Dec::~Dec() -{ - dec_running = false; - join(); -} - - -static Dec *dec = NULL; +extern AVDec *avdec; cVideo::cVideo(int, void *, void *, unsigned int) { @@ -73,7 +43,6 @@ cVideo::cVideo(int, void *, void *, unsigned int) display_aspect = DISPLAY_AR_16_9; display_crop = DISPLAY_AR_MODE_LETTERBOX; v_format = VIDEO_FORMAT_MPEG2; - bcm_host_init(); } cVideo::~cVideo(void) @@ -100,19 +69,14 @@ int cVideo::setCroppingMode(int) int cVideo::Start(void *, unsigned short, unsigned short, void *) { lt_debug("%s running %d >\n", __func__, thread_running); - if (!dec) { - dec = new Dec(); - } + avdec->start_video(); return 0; } int cVideo::Stop(bool) { lt_debug("%s running %d >\n", __func__, thread_running); - if (dec) { - delete dec; - dec = NULL; - } + avdec->stop_video(); return 0; } @@ -231,218 +195,3 @@ void cVideo::SetDemux(cDemux *) lt_debug("%s: not implemented yet\n", __func__); } - -void Dec::run() -{ - hal_set_threadname("hal:vdec"); - int ret = 0; - /* write to file instead of decoding. For testing only. */ - if (getenv("DEC_OUT")) { - lt_info_c("Dec::run %d\n", __LINE__); - FILE *ff = fopen("/tmp/video.pes", "w"); - unsigned char buf[65536]; - dec_running = true; - while (dec_running) - { - ret = videoDemux->Read(buf, 65536, 10); - if (ret <= 0) { - if (!dec_running) - break; - continue; - } - fwrite(buf, 1, ret, ff); - } - lt_info_c("Dec::run %d\n", __LINE__); - return; - } - lt_info_c("Dec::run %d\n", __LINE__); - - /* this code is mostly copied from hello_pi/hello_video example */ - OMX_VIDEO_PARAM_PORTFORMATTYPE format; - OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; - OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE conc; - COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL, *clock = NULL; - COMPONENT_T *list[5]; - TUNNEL_T tunnel[4]; - ILCLIENT_T *client; - int status = 0; - unsigned int data_len = 0; - int packet_size = 80<<10; /* 80kB */ - - memset(list, 0, sizeof(list)); - memset(tunnel, 0, sizeof(tunnel)); - - if ((client = ilclient_init()) == NULL) { - lt_info_c("Dec::run %d\n", __LINE__); - return; - } - - if (OMX_Init() != OMX_ErrorNone) { - lt_info_c("Dec::run %d\n", __LINE__); - ilclient_destroy(client); - return; - } - // create video_decode - if (ilclient_create_component(client, &video_decode, (char *)"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, (char *)"video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) - status = -14; - list[1] = video_render; - - // create clock - if (status == 0 && ilclient_create_component(client, &clock, (char *)"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, (char *)"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(&conc, 0, sizeof(conc)); - conc.nSize = sizeof(conc); - conc.nVersion.nVersion = OMX_VERSION; - conc.bStartWithValidFrame = OMX_FALSE; - if (status == 0 && - OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmVideoDecodeErrorConcealment, &conc) != OMX_ErrorNone) - status = -16; - - 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; - // format.xFramerate = 50 * (1 << 16); - - lt_info_c("Dec::run %d status %d\n", __LINE__, status); - 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; - dec_running = true; - - ilclient_change_component_state(video_decode, OMX_StateExecuting); - - lt_info_c("Dec::run %d\n", __LINE__); - 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; - again: - //lt_info_c("Dec::run %d\n", __LINE__); - ret = videoDemux->Read(dest, packet_size-data_len, 10); - if (ret <= 0) { - if (!dec_running) - break; - goto again; - } - - data_len += ret; - //lt_info_c("Dec::run %d data_len %d\n", __LINE__, data_len); - - 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) - break; - if (! dec_running) - 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; - } - } - lt_info_c("Dec::run %d\n", __LINE__); - - 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); - } - - 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); - lt_info_c("Dec::run %d ends\n", __LINE__); - // return status; -}