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;
-}