mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
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:
17
configure.ac
17
configure.ac
@@ -23,13 +23,20 @@ if test x"$BOXTYPE" = x"tripledragon"; then
|
|||||||
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
|
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then
|
if test x$BOXTYPE = xgeneric; then
|
||||||
PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1])
|
if test x$BOXMODEL != xraspi; then
|
||||||
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
|
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...
|
# don't know which version is exactly needed here...
|
||||||
PKG_CHECK_MODULES([AVUTIL], [libavutil])
|
PKG_CHECK_MODULES([AVUTIL], [libavutil])
|
||||||
PKG_CHECK_MODULES([SWSCALE], [libswscale])
|
|
||||||
PKG_CHECK_MODULES([SWRESAMPLE], [libswresample])
|
|
||||||
fi
|
fi
|
||||||
AC_OUTPUT([
|
AC_OUTPUT([
|
||||||
Makefile
|
Makefile
|
||||||
|
@@ -11,19 +11,26 @@ AM_CPPFLAGS += \
|
|||||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
AM_LDFLAGS = \
|
||||||
|
@AVFORMAT_LIBS@ \
|
||||||
|
@AVUTIL_LIBS@ \
|
||||||
|
@AVCODEC_LIBS@ \
|
||||||
-L/opt/vc/lib/ -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt \
|
-L/opt/vc/lib/ -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt \
|
||||||
-lOpenThreads
|
-lOpenThreads
|
||||||
|
|
||||||
libraspi_la_SOURCES = \
|
libraspi_la_SOURCES = \
|
||||||
hardware_caps.c \
|
hardware_caps.c \
|
||||||
|
avdec.cpp \
|
||||||
dmx.cpp \
|
dmx.cpp \
|
||||||
video.cpp \
|
video.cpp \
|
||||||
audio.cpp \
|
audio.cpp \
|
||||||
glfb.cpp \
|
glfb.cpp \
|
||||||
ilclient.c \
|
|
||||||
ilcore.c \
|
|
||||||
init.cpp \
|
init.cpp \
|
||||||
playback.cpp \
|
playback.cpp \
|
||||||
pwrmngr.cpp \
|
pwrmngr.cpp \
|
||||||
record.cpp
|
record.cpp
|
||||||
|
|
||||||
|
# OMX stuff, from https://github.com/linuxstb/pidvbip
|
||||||
|
libraspi_la_SOURCES += \
|
||||||
|
avcodec_omx.c \
|
||||||
|
codec.c \
|
||||||
|
omx_utils.c
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "audio_lib.h"
|
#include "audio_lib.h"
|
||||||
#include "dmx_lib.h"
|
#include "dmx_lib.h"
|
||||||
|
#include "avdec.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
|
|
||||||
#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args)
|
#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args)
|
||||||
@@ -29,6 +30,9 @@
|
|||||||
|
|
||||||
cAudio * audioDecoder = NULL;
|
cAudio * audioDecoder = NULL;
|
||||||
|
|
||||||
|
extern cDemux *audioDemux;
|
||||||
|
extern AVDec *avdec;
|
||||||
|
|
||||||
cAudio::cAudio(void *, void *, void *)
|
cAudio::cAudio(void *, void *, void *)
|
||||||
{
|
{
|
||||||
lt_debug("%s\n", __func__);
|
lt_debug("%s\n", __func__);
|
||||||
@@ -52,24 +56,34 @@ void cAudio::closeDevice(void)
|
|||||||
int cAudio::do_mute(bool enable, bool remember)
|
int cAudio::do_mute(bool enable, bool remember)
|
||||||
{
|
{
|
||||||
lt_debug("%s(%d, %d)\n", __func__, enable, remember);
|
lt_debug("%s(%d, %d)\n", __func__, enable, remember);
|
||||||
|
if (enable)
|
||||||
|
avdec->set_volume(0);
|
||||||
|
else
|
||||||
|
avdec->set_volume(volume);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cAudio::setVolume(unsigned int left, unsigned int right)
|
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||||
{
|
{
|
||||||
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
||||||
|
volume = (left + right)/2;
|
||||||
|
avdec->set_volume(volume);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cAudio::Start(void)
|
int cAudio::Start(void)
|
||||||
{
|
{
|
||||||
lt_debug("%s >\n", __func__);
|
lt_info("%s >\n", __func__);
|
||||||
|
avdec->start_audio();
|
||||||
|
lt_info("%s <\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cAudio::Stop(void)
|
int cAudio::Stop(void)
|
||||||
{
|
{
|
||||||
lt_debug("%s >\n", __func__);
|
lt_info("%s >\n", __func__);
|
||||||
|
avdec->stop_audio();
|
||||||
|
lt_info("%s <\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,4 +165,3 @@ void cAudio::setBypassMode(bool disable)
|
|||||||
{
|
{
|
||||||
lt_debug("%s %d\n", __func__, disable);
|
lt_debug("%s %d\n", __func__, disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
442
raspi/avcodec_omx.c
Normal file
442
raspi/avcodec_omx.c
Normal 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
14
raspi/avcodec_omx.h
Normal 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
524
raspi/avdec.cpp
Normal 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
34
raspi/avdec.h
Normal 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
265
raspi/codec.c
Normal 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
91
raspi/codec.h
Normal 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
|
@@ -382,20 +382,19 @@ bool cDemux::pesFilter(const unsigned short pid)
|
|||||||
p_flt.output = DMX_OUT_DECODER;
|
p_flt.output = DMX_OUT_DECODER;
|
||||||
p_flt.input = DMX_IN_FRONTEND;
|
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) {
|
switch (dmx_type) {
|
||||||
case DMX_PCR_ONLY_CHANNEL:
|
case DMX_PCR_ONLY_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_OTHER;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
p_flt.output = DMX_OUT_TAP;
|
p_flt.output = DMX_OUT_TAP;
|
||||||
|
return true;
|
||||||
break;
|
break;
|
||||||
case DMX_AUDIO_CHANNEL:
|
case DMX_AUDIO_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_OTHER;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
p_flt.output = DMX_OUT_TAP;
|
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||||
break;
|
break;
|
||||||
case DMX_VIDEO_CHANNEL:
|
case DMX_VIDEO_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_OTHER;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
p_flt.output = DMX_OUT_TAP;
|
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||||
break;
|
break;
|
||||||
case DMX_PES_CHANNEL:
|
case DMX_PES_CHANNEL:
|
||||||
p_flt.pes_type = DMX_PES_OTHER;
|
p_flt.pes_type = DMX_PES_OTHER;
|
||||||
|
1844
raspi/ilclient.c
1844
raspi/ilclient.c
File diff suppressed because it is too large
Load Diff
1039
raspi/ilclient.h
1039
raspi/ilclient.h
File diff suppressed because it is too large
Load Diff
316
raspi/ilcore.c
316
raspi/ilcore.c
@@ -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 */
|
|
||||||
|
|
@@ -37,11 +37,14 @@
|
|||||||
#include "init_lib.h"
|
#include "init_lib.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
#include "glfb.h"
|
#include "glfb.h"
|
||||||
|
#include "avdec.h"
|
||||||
|
|
||||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
||||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
||||||
|
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
GLFramebuffer *glfb = NULL;
|
GLFramebuffer *glfb = NULL;
|
||||||
|
AVDec *avdec = NULL;
|
||||||
|
|
||||||
typedef std::map<uint16_t, uint16_t> keymap_t;
|
typedef std::map<uint16_t, uint16_t> keymap_t;
|
||||||
static keymap_t kmap;
|
static keymap_t kmap;
|
||||||
@@ -215,6 +218,8 @@ void init_td_api()
|
|||||||
}
|
}
|
||||||
if (! thread)
|
if (! thread)
|
||||||
thread = new Input();
|
thread = new Input();
|
||||||
|
if (! avdec)
|
||||||
|
avdec = new AVDec();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,5 +230,7 @@ void shutdown_td_api()
|
|||||||
delete glfb;
|
delete glfb;
|
||||||
if (thread)
|
if (thread)
|
||||||
delete thread;
|
delete thread;
|
||||||
|
if (avdec)
|
||||||
|
delete avdec;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
}
|
}
|
||||||
|
1150
raspi/omx_utils.c
Normal file
1150
raspi/omx_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
124
raspi/omx_utils.h
Normal file
124
raspi/omx_utils.h
Normal 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
|
259
raspi/video.cpp
259
raspi/video.cpp
@@ -23,15 +23,9 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <OpenThreads/Thread>
|
|
||||||
|
|
||||||
#include <bcm_host.h>
|
|
||||||
extern "C" {
|
|
||||||
#include "ilclient.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "video_lib.h"
|
#include "video_lib.h"
|
||||||
#include "dmx_lib.h"
|
#include "dmx_lib.h"
|
||||||
|
#include "avdec.h"
|
||||||
#include "lt_debug.h"
|
#include "lt_debug.h"
|
||||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
||||||
#define lt_info(args...) _lt_info(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;
|
int system_rev = 0;
|
||||||
|
|
||||||
extern cDemux *videoDemux;
|
extern cDemux *videoDemux;
|
||||||
static int dec_running = false;
|
extern AVDec *avdec;
|
||||||
|
|
||||||
|
|
||||||
class Dec: public OpenThreads::Thread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Dec();
|
|
||||||
~Dec();
|
|
||||||
private:
|
|
||||||
void run();
|
|
||||||
};
|
|
||||||
|
|
||||||
Dec::Dec()
|
|
||||||
{
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dec::~Dec()
|
|
||||||
{
|
|
||||||
dec_running = false;
|
|
||||||
join();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Dec *dec = NULL;
|
|
||||||
|
|
||||||
cVideo::cVideo(int, void *, void *, unsigned int)
|
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_aspect = DISPLAY_AR_16_9;
|
||||||
display_crop = DISPLAY_AR_MODE_LETTERBOX;
|
display_crop = DISPLAY_AR_MODE_LETTERBOX;
|
||||||
v_format = VIDEO_FORMAT_MPEG2;
|
v_format = VIDEO_FORMAT_MPEG2;
|
||||||
bcm_host_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cVideo::~cVideo(void)
|
cVideo::~cVideo(void)
|
||||||
@@ -100,19 +69,14 @@ int cVideo::setCroppingMode(int)
|
|||||||
int cVideo::Start(void *, unsigned short, unsigned short, void *)
|
int cVideo::Start(void *, unsigned short, unsigned short, void *)
|
||||||
{
|
{
|
||||||
lt_debug("%s running %d >\n", __func__, thread_running);
|
lt_debug("%s running %d >\n", __func__, thread_running);
|
||||||
if (!dec) {
|
avdec->start_video();
|
||||||
dec = new Dec();
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cVideo::Stop(bool)
|
int cVideo::Stop(bool)
|
||||||
{
|
{
|
||||||
lt_debug("%s running %d >\n", __func__, thread_running);
|
lt_debug("%s running %d >\n", __func__, thread_running);
|
||||||
if (dec) {
|
avdec->stop_video();
|
||||||
delete dec;
|
|
||||||
dec = NULL;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,218 +195,3 @@ void cVideo::SetDemux(cDemux *)
|
|||||||
lt_debug("%s: not implemented yet\n", __func__);
|
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;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user