raspi: use decoder implementation from pidvbip

* remove ilclient
* use a decoder implementation similar to pidvbip instead,
  see https://github.com/linuxstb/pidvbip
* video is decoded in hardware (videocore)
* audio is decoded by libavcodec
* add a AVDec wrapper class for both audio and video decoders
* very raw, needs more polishing. But decodes audio and video :-)
* only tested with h264
This commit is contained in:
Stefan Seyfried
2013-10-13 22:36:45 +02:00
parent 19b0ff20e1
commit 510b655710
17 changed files with 2695 additions and 3468 deletions

View File

@@ -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

View File

@@ -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

View File

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

442
raspi/avcodec_omx.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* avcodec_omx.c -- audio / video decoder for the Raspberry Pi
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <sys/time.h>
#include <assert.h>
#include <libavformat/avformat.h>
#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);
}

14
raspi/avcodec_omx.h Normal file
View File

@@ -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

524
raspi/avdec.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* Audio / Video decoder for Raspberry pi
*/
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <assert.h>
#include <cstdio>
#include <cstdlib>
#include <OpenThreads/Thread>
extern "C" {
#include <bcm_host.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavutil/mathematics.h>
//#include <ao/ao.h>
#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
}

34
raspi/avdec.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* 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);
};

265
raspi/codec.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* codec.c -- audio / video codec queue functions
*/
#include "config.h"
#include <stdio.h>
#include <pthread.h>
#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;
}

91
raspi/codec.h Normal file
View File

@@ -0,0 +1,91 @@
#ifndef _CODEC_H
#define _CODEC_H
#include <stdint.h>
#include <pthread.h>
#include <libavformat/avformat.h>
#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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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 <stdio.h>
#include <stdarg.h>
//includes
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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<sizeof(OMX_COMPONENTTYPE)>>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 */

View File

@@ -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<uint16_t, uint16_t> 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;
}

1150
raspi/omx_utils.c Normal file

File diff suppressed because it is too large Load Diff

124
raspi/omx_utils.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* omx_utils.h -- OMX helper functions for the Raspberry Pi
*/
#ifndef __OMX_UTILS_H
#define __OMX_UTILS_H
#include <IL/OMX_Broadcom.h>
#include <interface/vcos/vcos.h>
#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

View File

@@ -23,15 +23,9 @@
#include <cstdio>
#include <cstdlib>
#include <OpenThreads/Thread>
#include <bcm_host.h>
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;
}