summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2014-01-07 16:32:58 +0100
committerThomas Reufer <thomas@reufer.ch>2014-01-07 16:32:58 +0100
commitdd9fbf38610934623869728d1b00904d15783480 (patch)
tree789059da0b20f3c7d9711b3dd638a770d2a6cfb2
parent9512123c95324f1679d748993662bd9f08f6f763 (diff)
downloadvdr-plugin-rpihddevice-0.0.5.tar.gz
vdr-plugin-rpihddevice-0.0.5.tar.bz2
2013-11-17: Version 0.0.50.0.5
------------------------- - new: - improved audio format detection (taken from softhddevice) - separate thread for audio decoding - fixed: - jump forward/backward in recordings - several minor bugfixes - missing: - still picture - trick modes - deinterlacer - video format/output options
-rw-r--r--HISTORY18
-rw-r--r--Makefile6
-rw-r--r--README2
-rw-r--r--audio.c779
-rw-r--r--audio.h82
-rw-r--r--omx.c876
-rw-r--r--omx.h122
-rw-r--r--omxdevice.c1149
-rw-r--r--omxdevice.h41
-rw-r--r--ovgosd.c1
-rw-r--r--rpihddevice.c8
-rw-r--r--setup.c27
-rw-r--r--setup.h26
-rw-r--r--types.h67
14 files changed, 2001 insertions, 1203 deletions
diff --git a/HISTORY b/HISTORY
index bad3594..5faf55a 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,20 @@
VDR Plugin 'rpihddevice' Revision History
-----------------------------------------
+2013-11-17: Version 0.0.5
+-------------------------
+- new:
+ - improved audio format detection (taken from softhddevice)
+ - separate thread for audio decoding
+- fixed:
+ - jump forward/backward in recordings
+ - several minor bugfixes
+- missing:
+ - still picture
+ - trick modes
+ - deinterlacer
+ - video format/output options
+
2013-10-14: Version 0.0.4
-------------------------
- new:
@@ -11,7 +25,7 @@ VDR Plugin 'rpihddevice' Revision History
- removed drawing of black box in front of console which lead to malfunction
due to memory bandwidth problem. console blank out will be handled with
video format/output options in future versions.
-- missing
+- missing:
- trick modes
- deinterlacer
- video format/output options
@@ -26,7 +40,7 @@ VDR Plugin 'rpihddevice' Revision History
- replay start/stop/pause
- improved H264 detection
- blank out console
-- missing
+- missing:
- trick modes
- other audio formats
- much more...
diff --git a/Makefile b/Makefile
index 4b02ae3..58af8bd 100644
--- a/Makefile
+++ b/Makefile
@@ -54,8 +54,8 @@ DEFINES += -Wno-write-strings -fpermissive
CXXFLAGS += -D__STDC_CONSTANT_MACROS
ILCDIR =ilclient
-VCINCDIR =/opt/vc/include
-VCLIBDIR =/opt/vc/lib
+VCINCDIR =$(SDKSTAGE)/opt/vc/include
+VCLIBDIR =$(SDKSTAGE)/opt/vc/lib
INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads -I$(VCINCDIR)/interface/vmcs_host/linux
@@ -65,7 +65,7 @@ LDFLAGS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive
### The object files (add further files here):
ILCLIENT = $(ILCDIR)/libilclient.a
-OBJS = $(PLUGIN).o setup.o audio.o omxdevice.o ovgosd.o
+OBJS = $(PLUGIN).o setup.o omx.o audio.o omxdevice.o ovgosd.o
### The main target:
diff --git a/README b/README
index f90aa02..271d093 100644
--- a/README
+++ b/README
@@ -15,4 +15,4 @@ Description:
Requirements:
- valid MPEG2 licence
- - libmpg123
+ - ffmpeg (tested with 1.0.7)
diff --git a/audio.c b/audio.c
index f85a4ff..25ee90d 100644
--- a/audio.c
+++ b/audio.c
@@ -6,55 +6,507 @@
#include "audio.h"
#include "setup.h"
+#include "omx.h"
#include <vdr/tools.h>
#include <vdr/remux.h>
#include <string.h>
-cAudioDecoder::cAudioDecoder() :
- m_codec(ePCM),
- m_outputFormat(ePCM),
- m_outputPort(eLocal),
+class cAudioParser
+{
+
+public:
+
+ cAudioParser() { }
+ ~cAudioParser() { }
+
+ inline AVPacket* Packet(void) { return &m_packet; }
+ inline unsigned int Size(void) { return m_packet.stream_index; }
+ inline unsigned char *Data(void) { return m_packet.data; }
+
+ int Init(void)
+ {
+ return av_new_packet(&m_packet, 64 * 1024 /* 1 PES packet */);
+ }
+
+ int DeInit(void)
+ {
+ av_free_packet(&m_packet);
+ return 0;
+ }
+
+ void Reset(void)
+ {
+ m_packet.stream_index = 0;
+ memset(m_packet.data, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+ }
+
+ bool Append(const unsigned char *data, unsigned int length)
+ {
+ if (m_packet.stream_index + length + FF_INPUT_BUFFER_PADDING_SIZE > m_packet.size)
+ return false;
+
+ memcpy(m_packet.data + m_packet.stream_index, data, length);
+ m_packet.stream_index += length;
+ memset(m_packet.data + m_packet.stream_index, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+ return true;
+ }
+
+ void Shrink(unsigned int length)
+ {
+ if (length < m_packet.stream_index)
+ {
+ memmove(m_packet.data, m_packet.data + length, m_packet.stream_index - length);
+ m_packet.stream_index -= length;
+ memset(m_packet.data + m_packet.stream_index, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+ }
+ else
+ Reset();
+ }
+
+ cAudioCodec::eCodec Parse(unsigned int &offset)
+ {
+ cAudioCodec::eCodec codec = cAudioCodec::eInvalid;
+
+ while (Size() - offset >= 5)
+ {
+ const uint8_t *p = Data() + offset;
+ int n = Size() - offset;
+ int r = 0;
+
+ // 4 bytes 0xFFExxxxx MPEG audio
+ // 3 bytes 0x56Exxx AAC LATM audio
+ // 5 bytes 0x0B77xxxxxx AC-3 audio
+ // 6 bytes 0x0B77xxxxxxxx E-AC-3 audio
+ // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio
+ // PCM audio can't be found
+
+ if (FastMpegCheck(p))
+ {
+ r = MpegCheck(p, n);
+ codec = cAudioCodec::eMPG;
+ }
+ else if (FastAc3Check(p))
+ {
+ r = Ac3Check(p, n);
+ codec = cAudioCodec::eAC3;
+
+ if (r > 0 && p[5] > (10 << 3))
+ codec = cAudioCodec::eEAC3;
+ }
+ else if (FastLatmCheck(p))
+ {
+ r = LatmCheck(p, n);
+ codec = cAudioCodec::eAAC;
+ }
+ else if (FastAdtsCheck(p))
+ {
+ r = AdtsCheck(p, n);
+ codec = cAudioCodec::eDTS;
+ }
+
+ if (r < 0) // need more bytes
+ break;
+
+ if (r > 0)
+ return codec;
+
+ ++offset;
+ }
+ return cAudioCodec::eInvalid;
+ }
+
+private:
+
+ AVPacket m_packet;
+
+ /* ------------------------------------------------------------------------- */
+ /* audio codec parser helper functions, taken from vdr-softhddevice */
+ /* ------------------------------------------------------------------------- */
+
+ static const uint16_t BitRateTable[2][3][16];
+ static const uint16_t SampleRateTable[4];
+ static const uint16_t Ac3FrameSizeTable[38][3];
+
+ ///
+ /// Fast check for MPEG audio.
+ ///
+ /// 4 bytes 0xFFExxxxx MPEG audio
+ ///
+ static bool FastMpegCheck(const uint8_t *p)
+ {
+ if (p[0] != 0xFF) // 11bit frame sync
+ return false;
+ if ((p[1] & 0xE0) != 0xE0)
+ return false;
+ if ((p[1] & 0x18) == 0x08) // version ID - 01 reserved
+ return false;
+ if (!(p[1] & 0x06)) // layer description - 00 reserved
+ return false;
+ if ((p[2] & 0xF0) == 0xF0) // bit rate index - 1111 reserved
+ return false;
+ if ((p[2] & 0x0C) == 0x0C) // sampling rate index - 11 reserved
+ return false;
+ return true;
+ }
+
+ /// Check for MPEG audio.
+ ///
+ /// 0xFFEx already checked.
+ ///
+ /// @param data incomplete PES packet
+ /// @param size number of bytes
+ ///
+ /// @retval <0 possible MPEG audio, but need more data
+ /// @retval 0 no valid MPEG audio
+ /// @retval >0 valid MPEG audio
+ ///
+ /// From: http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
+ ///
+ /// AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
+ ///
+ /// o a 11x Frame sync
+ /// o b 2x MPEG audio version (2.5, reserved, 2, 1)
+ /// o c 2x Layer (reserved, III, II, I)
+ /// o e 2x BitRate index
+ /// o f 2x SampleRate index (4100, 48000, 32000, 0)
+ /// o g 1x Padding bit
+ /// o .. Doesn't care
+ ///
+ /// frame length:
+ /// Layer I:
+ /// FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
+ /// Layer II & III:
+ /// FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
+ ///
+ static int MpegCheck(const uint8_t *data, int size)
+ {
+ int frame_size;
+ int mpeg2 = !(data[1] & 0x08) && (data[1] & 0x10);
+ int mpeg25 = !(data[1] & 0x08) && !(data[1] & 0x10);
+ int layer = 4 - ((data[1] >> 1) & 0x03);
+ int padding = (data[2] >> 1) & 0x01;
+
+ int sample_rate = SampleRateTable[(data[2] >> 2) & 0x03];
+ if (!sample_rate)
+ return 0;
+
+ sample_rate >>= mpeg2; // MPEG 2 half rate
+ sample_rate >>= mpeg25; // MPEG 2.5 quarter rate
+
+ int bit_rate = BitRateTable[mpeg2 | mpeg25][layer - 1][(data[2] >> 4) & 0x0F];
+ if (!bit_rate)
+ return 0;
+
+ switch (layer)
+ {
+ case 1:
+ frame_size = (12000 * bit_rate) / sample_rate;
+ frame_size = (frame_size + padding) * 4;
+ break;
+ case 2:
+ case 3:
+ default:
+ frame_size = (144000 * bit_rate) / sample_rate;
+ frame_size = frame_size + padding;
+ break;
+ }
+ if (frame_size + 4 > size)
+ return -frame_size - 4;
+
+ // check if after this frame a new MPEG frame starts
+ if (cAudioParser::FastMpegCheck(data + frame_size))
+ return frame_size;
+
+ return 0;
+ }
+
+ ///
+ /// Fast check for (E-)AC-3 audio.
+ ///
+ /// 5 bytes 0x0B77xxxxxx AC-3 audio
+ ///
+ static bool FastAc3Check(const uint8_t *p)
+ {
+ if (p[0] != 0x0B) // 16bit sync
+ return false;
+ if (p[1] != 0x77)
+ return false;
+ return true;
+ }
+
+ ///
+ /// Check for (E-)AC-3 audio.
+ ///
+ /// 0x0B77xxxxxx already checked.
+ ///
+ /// @param data incomplete PES packet
+ /// @param size number of bytes
+ ///
+ /// @retval <0 possible AC-3 audio, but need more data
+ /// @retval 0 no valid AC-3 audio
+ /// @retval >0 valid AC-3 audio
+ ///
+ /// o AC-3 Header
+ /// AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB CCDDDDDD EEEEEFFF
+ ///
+ /// o a 16x Frame sync, always 0x0B77
+ /// o b 16x CRC 16
+ /// o c 2x Sample rate
+ /// o d 6x Frame size code
+ /// o e 5x Bit stream ID
+ /// o f 3x Bit stream mode
+ ///
+ /// o E-AC-3 Header
+ /// AAAAAAAA AAAAAAAA BBCCCDDD DDDDDDDD EEFFGGGH IIIII...
+ ///
+ /// o a 16x Frame sync, always 0x0B77
+ /// o b 2x Frame type
+ /// o c 3x Sub stream ID
+ /// o d 10x Frame size - 1 in words
+ /// o e 2x Frame size code
+ /// o f 2x Frame size code 2
+ ///
+ static int Ac3Check(const uint8_t *p, int size)
+ {
+ int frame_size;
+
+ if (size < 5) // need 5 bytes to see if AC-3/E-AC-3
+ return -5;
+
+ if (p[5] > (10 << 3)) // E-AC-3
+ {
+ if ((p[4] & 0xF0) == 0xF0) // invalid fscod fscod2
+ return 0;
+
+ frame_size = ((p[2] & 0x03) << 8) + p[3] + 1;
+ frame_size *= 2;
+ }
+ else // AC-3
+ {
+ int fscod = p[4] >> 6;
+ if (fscod == 0x03) // invalid sample rate
+ return 0;
+
+ int frmsizcod = p[4] & 0x3F;
+ if (frmsizcod > 37) // invalid frame size
+ return 0;
+
+ // invalid is checked above
+ frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2;
+ }
+ if (frame_size + 5 > size)
+ return -frame_size - 5;
+
+ // check if after this frame a new AC-3 frame starts
+ if (FastAc3Check(p + frame_size))
+ return frame_size;
+
+ return 0;
+ }
+
+ ///
+ /// Fast check for AAC LATM audio.
+ ///
+ /// 3 bytes 0x56Exxx AAC LATM audio
+ ///
+ static bool FastLatmCheck(const uint8_t *p)
+ {
+ if (p[0] != 0x56) // 11bit sync
+ return false;
+ if ((p[1] & 0xE0) != 0xE0)
+ return false;
+ return true;
+ }
+
+ ///
+ /// Check for AAC LATM audio.
+ ///
+ /// 0x56Exxx already checked.
+ ///
+ /// @param data incomplete PES packet
+ /// @param size number of bytes
+ ///
+ /// @retval <0 possible AAC LATM audio, but need more data
+ /// @retval 0 no valid AAC LATM audio
+ /// @retval >0 valid AAC LATM audio
+ ///
+ static int LatmCheck(const uint8_t *p, int size)
+ {
+ // 13 bit frame size without header
+ int frame_size = ((p[1] & 0x1F) << 8) + p[2];
+ frame_size += 3;
+
+ if (frame_size + 2 > size)
+ return -frame_size - 2;
+
+ // check if after this frame a new AAC LATM frame starts
+ if (FastLatmCheck(p + frame_size))
+ return frame_size;
+
+ return 0;
+ }
+
+ ///
+ /// Fast check for ADTS Audio Data Transport Stream.
+ ///
+ /// 7/9 bytes 0xFFFxxxxxxxxxxx(xxxx) ADTS audio
+ ///
+ static bool FastAdtsCheck(const uint8_t *p)
+ {
+ if (p[0] != 0xFF) // 12bit sync
+ return false;
+ if ((p[1] & 0xF6) != 0xF0) // sync + layer must be 0
+ return false;
+ if ((p[2] & 0x3C) == 0x3C) // sampling frequency index != 15
+ return false;
+ return true;
+ }
+
+ ///
+ /// Check for ADTS Audio Data Transport Stream.
+ ///
+ /// 0xFFF already checked.
+ ///
+ /// @param data incomplete PES packet
+ /// @param size number of bytes
+ ///
+ /// @retval <0 possible ADTS audio, but need more data
+ /// @retval 0 no valid ADTS audio
+ /// @retval >0 valid AC-3 audio
+ ///
+ /// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP
+ /// (QQQQQQQQ QQQQQQQ)
+ ///
+ /// o A*12 sync word 0xFFF
+ /// o B*1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2
+ /// o C*2 layer: always 0
+ /// o ..
+ /// o F*4 sampling frequency index (15 is invalid)
+ /// o ..
+ /// o M*13 frame length
+ ///
+ static int AdtsCheck(const uint8_t *p, int size)
+ {
+ if (size < 6)
+ return -6;
+
+ int frame_size = (p[3] & 0x03) << 11;
+ frame_size |= (p[4] & 0xFF) << 3;
+ frame_size |= (p[5] & 0xE0) >> 5;
+
+ if (frame_size + 3 > size)
+ return -frame_size - 3;
+
+ // check if after this frame a new ADTS frame starts
+ if (FastAdtsCheck(p + frame_size))
+ return frame_size;
+
+ return 0;
+ }
+};
+
+///
+/// MPEG bit rate table.
+///
+/// BitRateTable[Version][Layer][Index]
+///
+const uint16_t cAudioParser::BitRateTable[2][3][16] =
+{
+ { // MPEG Version 1
+ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
+ },
+ { // MPEG Version 2 & 2.5
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
+ }
+};
+
+///
+/// MPEG sample rate table.
+///
+const uint16_t cAudioParser::SampleRateTable[4] = { 44100, 48000, 32000, 0 };
+
+///
+/// Possible AC-3 frame sizes.
+///
+/// from ATSC A/52 table 5.18 frame size code table.
+///
+const uint16_t cAudioParser::Ac3FrameSizeTable[38][3] =
+{
+ { 64, 69, 96}, { 64, 70, 96}, { 80, 87, 120}, { 80, 88, 120},
+ { 96, 104, 144}, { 96, 105, 144}, { 112, 121, 168}, {112, 122, 168},
+ { 128, 139, 192}, { 128, 140, 192}, { 160, 174, 240}, {160, 175, 240},
+ { 192, 208, 288}, { 192, 209, 288}, { 224, 243, 336}, {224, 244, 336},
+ { 256, 278, 384}, { 256, 279, 384}, { 320, 348, 480}, {320, 349, 480},
+ { 384, 417, 576}, { 384, 418, 576}, { 448, 487, 672}, {448, 488, 672},
+ { 512, 557, 768}, { 512, 558, 768}, { 640, 696, 960}, {640, 697, 960},
+ { 768, 835, 1152}, { 768, 836, 1152}, { 896, 975, 1344}, {896, 976, 1344},
+ {1024, 1114, 1536}, {1024, 1115, 1536}, {1152, 1253, 1728},
+ {1152, 1254, 1728}, {1280, 1393, 1920}, {1280, 1394, 1920},
+};
+
+/* ------------------------------------------------------------------------- */
+
+cAudioDecoder::cAudioDecoder(cOmx *omx) :
+ cThread(),
+ m_codec(cAudioCodec::eInvalid),
+ m_outputFormat(cAudioCodec::ePCM),
+ m_outputPort(cAudioPort::eLocal),
m_channels(0),
m_samplingRate(0),
m_passthrough(false),
+ m_outputFormatChanged(true),
+ m_pts(0),
m_frame(0),
- m_mutex(new cMutex()) { }
+ m_mutex(new cMutex()),
+ m_newData(new cCondWait()),
+ m_parser(new cAudioParser()),
+ m_omx(omx)
+{
+}
cAudioDecoder::~cAudioDecoder()
{
+ delete m_parser;
+ delete m_newData;
delete m_mutex;
}
int cAudioDecoder::Init(void)
{
- int ret = 0;
+ int ret = m_parser->Init();
+ if (ret)
+ return ret;
avcodec_register_all();
- m_codecs[ePCM ].codec = NULL;
- m_codecs[eMPG ].codec = avcodec_find_decoder(CODEC_ID_MP3);
- m_codecs[eAC3 ].codec = avcodec_find_decoder(CODEC_ID_AC3);
- m_codecs[eEAC3].codec = avcodec_find_decoder(CODEC_ID_EAC3);
- m_codecs[eAAC ].codec = avcodec_find_decoder(CODEC_ID_AAC_LATM);
- m_codecs[eDTS ].codec = avcodec_find_decoder(CODEC_ID_DTS);
+ m_codecs[cAudioCodec::ePCM ].codec = NULL;
+ m_codecs[cAudioCodec::eMPG ].codec = avcodec_find_decoder(CODEC_ID_MP3);
+ m_codecs[cAudioCodec::eAC3 ].codec = avcodec_find_decoder(CODEC_ID_AC3);
+ m_codecs[cAudioCodec::eEAC3].codec = avcodec_find_decoder(CODEC_ID_EAC3);
+ m_codecs[cAudioCodec::eAAC ].codec = avcodec_find_decoder(CODEC_ID_AAC_LATM);
+ m_codecs[cAudioCodec::eDTS ].codec = avcodec_find_decoder(CODEC_ID_DTS);
- for (int i = 0; i < eNumCodecs; i++)
+ for (int i = 0; i < cAudioCodec::eNumCodecs; i++)
{
- eCodec codec = static_cast<eCodec>(i);
+ cAudioCodec::eCodec codec = static_cast<cAudioCodec::eCodec>(i);
if (m_codecs[codec].codec)
{
m_codecs[codec].context = avcodec_alloc_context3(m_codecs[codec].codec);
if (!m_codecs[codec].context)
{
- esyslog("rpihddevice: failed to allocate %s context!", CodecStr(codec));
+ esyslog("rpihddevice: failed to allocate %s context!", cAudioCodec::Str(codec));
ret = -1;
break;
}
if (avcodec_open2(m_codecs[codec].context, m_codecs[codec].codec, NULL) < 0)
{
- esyslog("rpihddevice: failed to open %s decoder!", CodecStr(codec));
+ esyslog("rpihddevice: failed to open %s decoder!", cAudioCodec::Str(codec));
ret = -1;
break;
}
@@ -67,138 +519,168 @@ int cAudioDecoder::Init(void)
esyslog("rpihddevice: failed to allocate audio frame!");
ret = -1;
}
+ avcodec_get_frame_defaults(m_frame);
if (ret < 0)
DeInit();
+ Start();
+
return ret;
}
int cAudioDecoder::DeInit(void)
{
- for (int i = 0; i < eNumCodecs; i++)
+ Cancel();
+
+ for (int i = 0; i < cAudioCodec::eNumCodecs; i++)
{
- eCodec codec = static_cast<eCodec>(i);
+ cAudioCodec::eCodec codec = static_cast<cAudioCodec::eCodec>(i);
avcodec_close(m_codecs[codec].context);
av_free(m_codecs[codec].context);
}
av_free(m_frame);
+ m_parser->DeInit();
return 0;
}
-bool cAudioDecoder::SetupAudioCodec(const unsigned char *data, int length)
+bool cAudioDecoder::WriteData(const unsigned char *buf, unsigned int length, uint64_t pts)
{
m_mutex->Lock();
- bool ret = false;
+ bool ret = m_parser->Append(buf, length);
+ if (ret)
+ {
+ // set current pts as reference
+ m_pts = pts;
- // try to decode audio sample
- AVPacket avpkt;
- av_init_packet(&avpkt);
- avpkt.data = (unsigned char *)(data + PesPayloadOffset(data));
- avpkt.size = PesLength(data) - PesPayloadOffset(data);
+ ProbeCodec();
+ m_newData->Signal();
+ }
- for (int i = 0; i < eNumCodecs; i++)
- {
- eCodec codec = static_cast<eCodec>(i);
- if (m_codecs[codec].codec)
- {
- int frame = 0;
- avcodec_get_frame_defaults(m_frame);
+ m_mutex->Unlock();
+ return ret;
+}
- m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
- m_codecs[codec].context->request_channels = 0;
+void cAudioDecoder::Reset(void)
+{
+ m_mutex->Lock();
+ m_parser->Reset();
+ m_codec = cAudioCodec::eInvalid;
+ avcodec_get_frame_defaults(m_frame);
+ m_mutex->Unlock();
+}
- int len = avcodec_decode_audio4(m_codecs[codec].context, m_frame, &frame, &avpkt);
+bool cAudioDecoder::Poll(void)
+{
+ return !m_parser->Size() && m_omx->PollAudioBuffers();
+}
- if (len > 0 && frame)
+void cAudioDecoder::Action(void)
+{
+ dsyslog("rpihddevice: cAudioDecoder() thread started");
+
+ while (Running())
+ {
+ if (m_parser->Size())
+ {
+ m_mutex->Lock();
+ if (m_outputFormatChanged)
{
- m_codec = codec;
- m_channels = m_codecs[m_codec].context->channels;
- m_samplingRate = m_codecs[m_codec].context->sample_rate;
- dsyslog("rpihddevice: set audio codec to %s with %d channels, %dHz",
- CodecStr(m_codec), m_channels, m_samplingRate);
-
- m_passthrough = false;
- m_outputFormat = ePCM;
- m_outputPort = eLocal;
-
- if (cRpiSetup::GetAudioPort() == eHDMI &&
- cRpiSetup::IsAudioFormatSupported(ePCM, 2, 48000))
- {
- m_outputPort = eHDMI;
+ m_outputFormatChanged = false;
+ m_omx->SetupAudioRender(
+ m_outputFormat, m_channels,
+ m_samplingRate, m_outputPort);
+ }
- if (cRpiSetup::IsAudioPassthrough() &&
- cRpiSetup::IsAudioFormatSupported(m_codec, m_channels, m_samplingRate))
- {
- m_passthrough = true;
- m_outputFormat = m_codec;
- }
- dsyslog("rpihddevice: set HDMI audio output format to %s%s",
- CodecStr(m_outputFormat), m_passthrough ? " (pass-through)" : "");
- }
- else
- {
- m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
- m_codecs[m_codec].context->request_channels = 2;
- m_channels = 2;
- dsyslog("rpihddevice: set analog audio output format to PCM stereo");
- }
+ OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(m_pts);
+ if (buf)
+ {
+ while (DecodeFrame())
+ buf->nFilledLen += ReadFrame(
+ buf->pBuffer + buf->nFilledLen,
+ buf->nAllocLen - buf->nFilledLen);
- ret = true;
- break;
+ if (!m_omx->EmptyAudioBuffer(buf))
+ esyslog("rpihddevice: failed to empty audio buffer!");
+ }
+ else
+ {
+ esyslog("rpihddevice: failed to get audio buffer!");
+ cCondWait::SleepMs(5);
}
+ m_mutex->Unlock();
}
+ else
+ m_newData->Wait(50);
}
- m_mutex->Unlock();
- return ret;
+ dsyslog("rpihddevice: cAudioDecoder() thread ended");
}
-unsigned int cAudioDecoder::DecodeAudio(const unsigned char *data, int length, unsigned char *outbuf, int bufsize)
+unsigned int cAudioDecoder::DecodeFrame()
{
- m_mutex->Lock();
+ unsigned int ret = 0;
if (m_passthrough)
+ ret = m_parser->Size();
+
+ else if (m_parser->Size())
{
- if (length > bufsize)
- esyslog("rpihddevice: pass-through audio frame is bigger than output buffer!");
- else
- memcpy(outbuf, data, length);
+ int frame = 0;
+ int len = avcodec_decode_audio4(m_codecs[m_codec].context,
+ m_frame, &frame, m_parser->Packet());
- return length;
+ // decoding error or number of channels changed ?
+ if (len < 0 || m_channels != m_codecs[m_codec].context->channels)
+ {
+ m_parser->Reset();
+ m_codec = cAudioCodec::eInvalid;
+ }
+ else
+ {
+ m_parser->Shrink(len);
+ ret = frame ? len : 0;
+ }
}
+ return ret;
+}
- AVPacket avpkt;
- av_init_packet(&avpkt);
-
- avpkt.data = (unsigned char *)data;
- avpkt.size = length;
-
- unsigned int outsize = 0;
+unsigned int cAudioDecoder::ReadFrame(unsigned char *buf, unsigned int bufsize)
+{
+ unsigned int ret = 0;
- while (avpkt.size > 0)
+ if (m_passthrough)
{
- int frame = 0;
- avcodec_get_frame_defaults(m_frame);
-
- int len = avcodec_decode_audio4(m_codecs[m_codec].context, m_frame, &frame, &avpkt);
- if (len < 0)
- break;
+ // for pass-through directly read from AV packet
+ if (m_parser->Size() > bufsize)
+ ret = bufsize;
+ else
+ ret = m_parser->Size();
- if (frame)
+ memcpy(buf, m_parser->Data(), ret);
+ m_parser->Shrink(ret);
+ }
+ else
+ {
+ if (m_frame->nb_samples > 0)
{
- unsigned int framesize = av_samples_get_buffer_size(NULL,
+ ret = av_samples_get_buffer_size(NULL,
m_channels == 6 ? 8 : m_channels, m_frame->nb_samples,
m_codecs[m_codec].context->sample_fmt, 1);
- if (outsize + framesize <= bufsize)
+ if (ret > bufsize)
+ {
+ esyslog("rpihddevice: decoded audio frame too big!");
+ ret = 0;
+ }
+ else
{
if (m_channels == 6)
{
// interleaved copy to fit 5.1 data into 8 channels
int32_t* src = (int32_t*)m_frame->data[0];
- int32_t* dst = (int32_t*)outbuf;
+ int32_t* dst = (int32_t*)buf;
for (int i = 0; i < m_frame->nb_samples; i++)
{
@@ -209,21 +691,106 @@ unsigned int cAudioDecoder::DecodeAudio(const unsigned char *data, int length, u
}
}
else
- memcpy(outbuf, m_frame->data[0], framesize);
-
- outsize += framesize;
- outbuf += framesize;
+ memcpy(buf, m_frame->data[0], ret);
}
- else
+ }
+ }
+ return ret;
+}
+
+bool cAudioDecoder::ProbeCodec(void)
+{
+ bool ret = false;
+
+ unsigned int offset = 0;
+ cAudioCodec::eCodec codec = m_parser->Parse(offset);
+
+ if (codec != cAudioCodec::eInvalid)
+ {
+ if (offset)
+ m_parser->Shrink(offset);
+
+ // if new codec has been found, decode one packet to determine number of
+ // channels, since they are needed to properly set audio output format
+ if (codec != m_codec || cRpiSetup::HasAudioSetupChanged())
+ {
+ m_codecs[codec].context->flags |= CODEC_FLAG_TRUNCATED;
+ m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
+ m_codecs[codec].context->request_channels = 0;
+
+ int frame = 0;
+ avcodec_get_frame_defaults(m_frame);
+ int len = avcodec_decode_audio4(m_codecs[codec].context, m_frame,
+ &frame, m_parser->Packet());
+
+ if (len > 0 && frame)
{
- esyslog("rpihddevice: decoded audio frame is bigger than output buffer!");
- break;
+ SetCodec(codec);
+ ret = true;
}
}
- avpkt.size -= len;
- avpkt.data += len;
}
+ return ret;
+}
- m_mutex->Unlock();
- return outsize;
+void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec)
+{
+ if (codec != cAudioCodec::eInvalid)
+ {
+ if (m_codec == cAudioCodec::eInvalid)
+ m_outputFormatChanged = true;
+
+ m_codec = codec;
+ m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
+ m_codecs[m_codec].context->request_channels = 0;
+
+ m_passthrough = false;
+ cAudioCodec::eCodec outputFormat = cAudioCodec::ePCM;
+ cAudioPort::ePort outputPort = cAudioPort::eLocal;
+
+ int channels = m_codecs[m_codec].context->channels;
+ int samplingRate = m_codecs[m_codec].context->sample_rate;
+
+ dsyslog("rpihddevice: set audio codec to %s with %d channels, %dHz",
+ cAudioCodec::Str(m_codec), channels, samplingRate);
+
+ if (cRpiSetup::GetAudioPort() == cAudioPort::eHDMI &&
+ cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM,
+ m_codecs[m_codec].context->channels,
+ m_codecs[m_codec].context->sample_rate))
+ {
+ outputPort = cAudioPort::eHDMI;
+
+ if (cRpiSetup::IsAudioPassthrough() &&
+ cRpiSetup::IsAudioFormatSupported(m_codec,
+ m_codecs[m_codec].context->channels,
+ m_codecs[m_codec].context->sample_rate))
+ {
+ m_passthrough = true;
+ outputFormat = m_codec;
+ }
+ }
+ else
+ {
+ m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
+ m_codecs[m_codec].context->request_channels = 2;
+ channels = 2;
+ }
+
+ if ((outputFormat != m_outputFormat) ||
+ (outputPort != m_outputPort ) ||
+ (channels != m_channels ) ||
+ (samplingRate != m_samplingRate))
+ {
+ m_outputFormat = outputFormat;
+ m_outputPort = outputPort;
+ m_channels = channels;
+ m_samplingRate = samplingRate;
+ m_outputFormatChanged = true;
+
+ dsyslog("rpihddevice: set %s audio output format to %s%s",
+ cAudioPort::Str(m_outputPort), cAudioCodec::Str(m_outputFormat),
+ m_passthrough ? " (pass-through)" : "");
+ }
+ }
}
diff --git a/audio.h b/audio.h
index a4ade81..327d312 100644
--- a/audio.h
+++ b/audio.h
@@ -12,56 +12,38 @@ extern "C"
#include <libavcodec/avcodec.h>
}
-class cMutex;
+#include <vdr/thread.h>
-class cAudioDecoder
-{
+#include "types.h"
+#include "omx.h"
-public:
+class cAudioParser;
- enum eCodec {
- ePCM,
- eMPG,
- eAC3,
- eEAC3,
- eAAC,
- eDTS,
- eNumCodecs
- };
+class cAudioDecoder : public cThread
+{
- enum ePort {
- eLocal,
- eHDMI
- };
+public:
- static const char* CodecStr(eCodec codec)
- {
- return (codec == ePCM) ? "PCM" :
- (codec == eMPG) ? "MPG" :
- (codec == eAC3) ? "AC3" :
- (codec == eEAC3) ? "E-AC3" :
- (codec == eAAC) ? "AAC" :
- (codec == eDTS) ? "DTS" : "unknown";
- }
-
- cAudioDecoder();
+ cAudioDecoder(cOmx *omx);
virtual ~cAudioDecoder();
virtual int Init(void);
virtual int DeInit(void);
- virtual bool SetupAudioCodec(const unsigned char *data, int length);
+ virtual bool WriteData(const unsigned char *buf, unsigned int length, uint64_t pts = 0);
- virtual eCodec GetCodec(void) { return m_codec; }
- virtual eCodec GetOutputFormat(void) { return m_outputFormat; }
- virtual ePort GetOutputPort(void) { return m_outputPort; }
- virtual int GetChannels(void) { return m_channels; }
- virtual int GetSamplingrate(void) { return m_samplingRate; }
+ virtual bool Poll(void);
+ virtual void Reset(void);
- virtual unsigned int DecodeAudio(const unsigned char *data, int length,
- unsigned char *outbuf, int bufsize);
+protected:
-private:
+ virtual void Action(void);
+
+ virtual unsigned int DecodeFrame();
+ virtual unsigned int ReadFrame(unsigned char *buf, unsigned int bufsize);
+
+ virtual bool ProbeCodec(void);
+ void SetCodec(cAudioCodec::eCodec codec);
struct Codec
{
@@ -69,17 +51,23 @@ private:
AVCodecContext *context;
};
- Codec m_codecs[eNumCodecs];
- eCodec m_codec;
- eCodec m_outputFormat;
- ePort m_outputPort;
- int m_channels;
- int m_samplingRate;
-
- bool m_passthrough;
+private:
- AVFrame *m_frame;
- cMutex *m_mutex;
+ Codec m_codecs[cAudioCodec::eNumCodecs];
+ cAudioCodec::eCodec m_codec;
+ cAudioCodec::eCodec m_outputFormat;
+ cAudioPort::ePort m_outputPort;
+ int m_channels;
+ int m_samplingRate;
+ bool m_passthrough;
+ bool m_outputFormatChanged;
+ uint64_t m_pts;
+
+ AVFrame *m_frame;
+ cMutex *m_mutex;
+ cCondWait *m_newData;
+ cAudioParser *m_parser;
+ cOmx *m_omx;
};
#endif
diff --git a/omx.c b/omx.c
new file mode 100644
index 0000000..7c0ddcf
--- /dev/null
+++ b/omx.c
@@ -0,0 +1,876 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "omx.h"
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+extern "C"
+{
+#include "ilclient.h"
+}
+
+#include "bcm_host.h"
+
+#define OMX_INIT_STRUCT(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
+
+#define OMX_AUDIO_CHANNEL_MAPPING(s, c) \
+switch (c) { \
+case 4: \
+ (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \
+ (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \
+ (s).eChannelMapping[2] = OMX_AUDIO_ChannelLR; \
+ (s).eChannelMapping[3] = OMX_AUDIO_ChannelRR; \
+ break; \
+case 1: \
+ (s).eChannelMapping[0] = OMX_AUDIO_ChannelCF; \
+ break; \
+case 8: \
+ (s).eChannelMapping[6] = OMX_AUDIO_ChannelLS; \
+ (s).eChannelMapping[7] = OMX_AUDIO_ChannelRS; \
+case 6: \
+ (s).eChannelMapping[2] = OMX_AUDIO_ChannelCF; \
+ (s).eChannelMapping[3] = OMX_AUDIO_ChannelLFE; \
+ (s).eChannelMapping[4] = OMX_AUDIO_ChannelLR; \
+ (s).eChannelMapping[5] = OMX_AUDIO_ChannelRR; \
+case 2: \
+default: \
+ (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \
+ (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \
+ break; }
+
+
+const char* cOmx::errStr(int err)
+{
+ return err == OMX_ErrorNone ? "None" :
+ err == OMX_ErrorInsufficientResources ? "InsufficientResources" :
+ err == OMX_ErrorUndefined ? "Undefined" :
+ err == OMX_ErrorInvalidComponentName ? "InvalidComponentName" :
+ err == OMX_ErrorComponentNotFound ? "ComponentNotFound" :
+ err == OMX_ErrorInvalidComponent ? "InvalidComponent" :
+ err == OMX_ErrorBadParameter ? "BadParameter" :
+ err == OMX_ErrorNotImplemented ? "NotImplemented" :
+ err == OMX_ErrorUnderflow ? "Underflow" :
+ err == OMX_ErrorOverflow ? "Overflow" :
+ err == OMX_ErrorHardware ? "Hardware" :
+ err == OMX_ErrorInvalidState ? "InvalidState" :
+ err == OMX_ErrorStreamCorrupt ? "StreamCorrupt" :
+ err == OMX_ErrorPortsNotCompatible ? "PortsNotCompatible" :
+ err == OMX_ErrorResourcesLost ? "ResourcesLost" :
+ err == OMX_ErrorNoMore ? "NoMore" :
+ err == OMX_ErrorVersionMismatch ? "VersionMismatch" :
+ err == OMX_ErrorNotReady ? "NotReady" :
+ err == OMX_ErrorTimeout ? "Timeout" :
+ err == OMX_ErrorSameState ? "SameState" :
+ err == OMX_ErrorResourcesPreempted ? "ResourcesPreempted" :
+ err == OMX_ErrorPortUnresponsiveDuringAllocation ? "PortUnresponsiveDuringAllocation" :
+ err == OMX_ErrorPortUnresponsiveDuringDeallocation ? "PortUnresponsiveDuringDeallocation" :
+ err == OMX_ErrorPortUnresponsiveDuringStop ? "PortUnresponsiveDuringStop" :
+ err == OMX_ErrorIncorrectStateTransition ? "IncorrectStateTransition" :
+ err == OMX_ErrorIncorrectStateOperation ? "IncorrectStateOperation" :
+ err == OMX_ErrorUnsupportedSetting ? "UnsupportedSetting" :
+ err == OMX_ErrorUnsupportedIndex ? "UnsupportedIndex" :
+ err == OMX_ErrorBadPortIndex ? "BadPortIndex" :
+ err == OMX_ErrorPortUnpopulated ? "PortUnpopulated" :
+ err == OMX_ErrorComponentSuspended ? "ComponentSuspended" :
+ err == OMX_ErrorDynamicResourcesUnavailable ? "DynamicResourcesUnavailable" :
+ err == OMX_ErrorMbErrorsInFrame ? "MbErrorsInFrame" :
+ err == OMX_ErrorFormatNotDetected ? "FormatNotDetected" :
+ err == OMX_ErrorContentPipeOpenFailed ? "ContentPipeOpenFailed" :
+ err == OMX_ErrorContentPipeCreationFailed ? "ContentPipeCreationFailed" :
+ err == OMX_ErrorSeperateTablesUsed ? "SeperateTablesUsed" :
+ err == OMX_ErrorTunnelingUnsupported ? "TunnelingUnsupported" :
+ err == OMX_ErrorKhronosExtensions ? "KhronosExtensions" :
+ err == OMX_ErrorVendorStartUnused ? "VendorStartUnused" :
+ err == OMX_ErrorDiskFull ? "DiskFull" :
+ err == OMX_ErrorMaxFileSize ? "MaxFileSize" :
+ err == OMX_ErrorDrmUnauthorised ? "DrmUnauthorised" :
+ err == OMX_ErrorDrmExpired ? "DrmExpired" :
+ err == OMX_ErrorDrmGeneral ? "DrmGeneral" :
+ "unknown";
+}
+
+void cOmx::HandleEndOfStream(unsigned int portId)
+ {
+ dsyslog("rpihddevice: HandleEndOfStream(%d)", portId);
+
+ switch (portId)
+ {
+ case 131:
+ break;
+
+ case 11:
+ break;
+
+ case 90:
+ break;
+ }
+}
+
+void cOmx::HandlePortSettingsChanged(unsigned int portId)
+{
+ //dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId);
+
+ switch (portId)
+ {
+ case 131:
+
+ OMX_PARAM_PORTDEFINITIONTYPE portdef;
+ OMX_INIT_STRUCT(portdef);
+ portdef.nPortIndex = 131;
+ if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamPortDefinition,
+ &portdef) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get video decoder port format!");
+
+ OMX_CONFIG_INTERLACETYPE interlace;
+ OMX_INIT_STRUCT(interlace);
+ interlace.nPortIndex = 131;
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexConfigCommonInterlace,
+ &interlace) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get video decoder interlace config!");
+
+ dsyslog("rpihddevice: decoding video %dx%d%s",
+ portdef.format.video.nFrameWidth,
+ portdef.format.video.nFrameHeight,
+ interlace.eMode == OMX_InterlaceProgressive ? "p" : "i");
+
+ if (ilclient_setup_tunnel(&m_tun[eVideoDecoderToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from video decoder to scheduler!");
+ if (ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to enable video scheduler!");
+ break;
+
+ case 11:
+ if (ilclient_setup_tunnel(&m_tun[eVideoSchedulerToVideoRender], 0, 1000) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from scheduler to render!");
+ if (ilclient_change_component_state(m_comp[eVideoRender], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to enable video render!");
+ break;
+ }
+}
+
+void cOmx::HandleBufferEmpty(COMPONENT_T *comp)
+{
+ if (comp == m_comp[eVideoDecoder])
+ {
+ m_mutex->Lock();
+ m_freeVideoBuffers++;
+ m_mutex->Unlock();
+ }
+ else if (comp == m_comp[eAudioRender])
+ {
+ m_mutex->Lock();
+ m_freeAudioBuffers++;
+ m_mutex->Unlock();
+ }
+}
+
+void cOmx::OnBufferEmpty(void *instance, COMPONENT_T *comp)
+{
+ cOmx* omx = static_cast <cOmx*> (instance);
+ omx->HandleBufferEmpty(comp);
+}
+
+void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, unsigned int data)
+{
+ cOmx* omx = static_cast <cOmx*> (instance);
+ omx->HandlePortSettingsChanged(data);
+}
+
+void cOmx::OnEndOfStream(void *instance, COMPONENT_T *comp, unsigned int data)
+{
+ cOmx* omx = static_cast <cOmx*> (instance);
+ omx->HandleEndOfStream(data);
+}
+
+void cOmx::OnError(void *instance, COMPONENT_T *comp, unsigned int data)
+{
+ if (data != OMX_ErrorSameState)
+ esyslog("rpihddevice: OmxError(%s)", errStr((int)data));
+}
+
+cOmx::cOmx() :
+ m_mutex(new cMutex()),
+ m_setVideoStartTime(true),
+ m_setAudioStartTime(true),
+ m_setVideoDiscontinuity(false),
+ m_freeAudioBuffers(0),
+ m_freeVideoBuffers(0),
+ m_clockReference(eClockRefAudio)
+{
+}
+
+cOmx::~cOmx()
+{
+ delete m_mutex;
+}
+
+int cOmx::Init(void)
+{
+ m_client = ilclient_init();
+ if (m_client == NULL)
+ esyslog("rpihddevice: ilclient_init() failed!");
+
+ if (OMX_Init() != OMX_ErrorNone)
+ esyslog("rpihddevice: OMX_Init() failed!");
+
+ ilclient_set_error_callback(m_client, OnError, this);
+ ilclient_set_empty_buffer_done_callback(m_client, OnBufferEmpty, this);
+ ilclient_set_port_settings_callback(m_client, OnPortSettingsChanged, this);
+ ilclient_set_eos_callback(m_client, OnEndOfStream, this);
+
+ // create video_decode
+ if (ilclient_create_component(m_client, &m_comp[eVideoDecoder],
+ "video_decode", (ILCLIENT_CREATE_FLAGS_T)
+ (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
+ esyslog("rpihddevice: failed creating video decoder!");
+
+ // create video_render
+ if (ilclient_create_component(m_client, &m_comp[eVideoRender],
+ "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating video render!");
+
+ //create clock
+ if (ilclient_create_component(m_client, &m_comp[eClock],
+ "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating clock!");
+
+ // create audio_render
+ if (ilclient_create_component(m_client, &m_comp[eAudioRender],
+ "audio_render", (ILCLIENT_CREATE_FLAGS_T)
+ (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
+ esyslog("rpihddevice: failed creating audio render!");
+
+ //create video_scheduler
+ if (ilclient_create_component(m_client, &m_comp[eVideoScheduler],
+ "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating video scheduler!");
+
+ // setup tunnels
+ set_tunnel(&m_tun[eVideoDecoderToVideoScheduler],
+ m_comp[eVideoDecoder], 131, m_comp[eVideoScheduler], 10);
+
+ set_tunnel(&m_tun[eVideoSchedulerToVideoRender],
+ m_comp[eVideoScheduler], 11, m_comp[eVideoRender], 90);
+
+ set_tunnel(&m_tun[eClockToVideoScheduler],
+ m_comp[eClock], 80, m_comp[eVideoScheduler], 12);
+
+ set_tunnel(&m_tun[eClockToAudioRender],
+ m_comp[eClock], 81, m_comp[eAudioRender], 101);
+
+ // setup clock tunnels first
+ if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to audio render!");
+
+ ilclient_change_component_state(m_comp[eClock], OMX_StateExecuting);
+ ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle);
+
+ // set up the number and size of buffers for audio render
+ m_freeAudioBuffers = 2; //64;
+ OMX_PARAM_PORTDEFINITIONTYPE param;
+ OMX_INIT_STRUCT(param);
+ param.nPortIndex = 100;
+ if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get audio render port parameters!");
+ param.nBufferSize = 160 * 1024;
+ param.nBufferCountActual = m_freeAudioBuffers;
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render port parameters!");
+
+ OMX_INIT_STRUCT(param);
+ param.nPortIndex = 130;
+ if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
+ OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get video decoder port parameters!");
+
+ m_freeVideoBuffers = param.nBufferCountActual;
+ dsyslog("rpihddevice: started with %d video and %d audio buffers",
+ m_freeVideoBuffers, m_freeAudioBuffers);
+
+/* // configure video decoder stall callback
+ OMX_CONFIG_BUFFERSTALLTYPE stallConf;
+ OMX_INIT_STRUCT(stallConf);
+ stallConf.nPortIndex = 131;
+ stallConf.nDelay = 1500 * 1000;
+ if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigBufferStall,
+ &stallConf) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder stall config!");
+
+ OMX_CONFIG_REQUESTCALLBACKTYPE reqCallback;
+ OMX_INIT_STRUCT(reqCallback);
+ reqCallback.nPortIndex = 131;
+ reqCallback.nIndex = OMX_IndexConfigBufferStall;
+ reqCallback.bEnable = OMX_TRUE;
+ if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigRequestCallback,
+ &reqCallback) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder stall callback!");
+*/
+// if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0)
+// esyslog("rpihddevice: failed to enable port buffer on audio render!");
+
+ OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ectype;
+ OMX_INIT_STRUCT(ectype);
+ ectype.bStartWithValidFrame = OMX_FALSE;
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
+ OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ectype) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decode error concealment failed\n");
+
+ return 0;
+}
+
+int cOmx::DeInit(void)
+{
+ ilclient_teardown_tunnels(m_tun);
+ ilclient_state_transition(m_comp, OMX_StateIdle);
+ ilclient_state_transition(m_comp, OMX_StateLoaded);
+ ilclient_cleanup_components(m_comp);
+
+ OMX_Deinit();
+ ilclient_destroy(m_client);
+
+ return 0;
+}
+
+OMX_TICKS cOmx::ToOmxTicks(int64_t val)
+{
+ OMX_TICKS ticks;
+ ticks.nLowPart = val;
+ ticks.nHighPart = val >> 32;
+ return ticks;
+}
+
+int64_t cOmx::FromOmxTicks(OMX_TICKS &ticks)
+{
+ int64_t ret = ticks.nLowPart | ((int64_t)(ticks.nHighPart) << 32);
+ return ret;
+}
+
+void cOmx::PtsToTicks(uint64_t pts, OMX_TICKS &ticks)
+{
+ // ticks = pts * OMX_TICKS_PER_SECOND / PTSTICKS
+ pts = pts * 100 / 9;
+ ticks.nLowPart = pts;
+ ticks.nHighPart = pts >> 32;
+}
+
+uint64_t cOmx::TicksToPts(OMX_TICKS &ticks)
+{
+ // pts = ticks * PTSTICKS / OMX_TICKS_PER_SECOND
+ uint64_t pts = ticks.nHighPart;
+ pts = (pts << 32) + ticks.nLowPart;
+ pts = pts * 9 / 100;
+ return pts;
+}
+
+int64_t cOmx::GetSTC(void)
+{
+ int64_t stc = -1;
+// return stc;
+
+ OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp;
+ OMX_INIT_STRUCT(timestamp);
+ timestamp.nPortIndex = OMX_ALL;
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeCurrentMediaTime, &timestamp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed get current clock reference!");
+ else
+ stc = TicksToPts(timestamp.nTimestamp);
+
+// dsyslog("S %u", timestamp.nTimestamp.nLowPart);
+
+ return stc;
+}
+
+bool cOmx::IsClockRunning(void)
+{
+ OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
+ OMX_INIT_STRUCT(cstate);
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed get clock state!");
+
+ if (cstate.eState == OMX_TIME_ClockStateRunning)
+ return true;
+ else
+ return false;
+}
+
+void cOmx::SetClockState(eClockState clockState)
+{
+ m_mutex->Lock();
+
+ dsyslog("rpihddevice: SetClockState(%s)",
+ clockState == eClockStateRun ? "eClockStateRun" :
+ clockState == eClockStateStop ? "eClockStateStop" :
+ clockState == eClockStateWaitForVideo ? "eClockStateWaitForVideo" :
+ clockState == eClockStateWaitForAudio ? "eClockStateWaitForAudio" :
+ clockState == eClockStateWaitForAudioVideo ? "eClockStateWaitForAudioVideo" :
+ "unknown");
+
+ OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
+ OMX_INIT_STRUCT(cstate);
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get clock state!");
+
+/* dsyslog("rpihddevice: current clock state: %s",
+ cstate.eState == OMX_TIME_ClockStateRunning ? "Running" :
+ cstate.eState == OMX_TIME_ClockStateStopped ? "Stopped" :
+ cstate.eState == OMX_TIME_ClockStateWaitingForStartTime ? "WaitingForStartTime" :
+ "unknown");
+*/
+ if ((cstate.eState == OMX_TIME_ClockStateRunning) &&
+ (clockState == eClockStateWaitForVideo ||
+ clockState == eClockStateWaitForAudio ||
+ clockState == eClockStateWaitForAudioVideo))
+ {
+ // clock already running, need to stop it first
+ cstate.eState = OMX_TIME_ClockStateStopped;
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to stop clock!");
+ }
+
+ switch (clockState)
+ {
+ case eClockStateRun:
+ cstate.eState = OMX_TIME_ClockStateRunning;
+ break;
+
+ case eClockStateStop:
+ cstate.eState = OMX_TIME_ClockStateStopped;
+ break;
+
+ case eClockStateWaitForVideo:
+ cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
+ cstate.nWaitMask = OMX_CLOCKPORT0;
+ m_setVideoStartTime = true;
+ SetClockReference(eClockRefVideo);
+ break;
+
+ case eClockStateWaitForAudio:
+ cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
+ cstate.nWaitMask = OMX_CLOCKPORT1;
+ m_setAudioStartTime = true;
+ SetClockReference(eClockRefAudio);
+ break;
+
+ case eClockStateWaitForAudioVideo:
+ cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
+ cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1;
+ m_setVideoStartTime = true;
+ m_setAudioStartTime = true;
+ SetClockReference(eClockRefAudio);
+ break;
+ }
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set clock state!");
+
+ m_mutex->Unlock();
+}
+
+void cOmx::SetClockScale(float scale)
+{
+ OMX_TIME_CONFIG_SCALETYPE scaleType;
+ OMX_INIT_STRUCT(scaleType);
+ scaleType.xScale = floor(scale * pow(2, 16));
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeScale, &scaleType) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set clock scale (%d)!", scaleType.xScale);
+ else
+ dsyslog("rpihddevice: set clock scale to %.2f (%d)", scale, scaleType.xScale);
+}
+
+void cOmx::SetMediaTime(uint64_t pts)
+{
+ OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
+ OMX_INIT_STRUCT(timeStamp);
+ timeStamp.nPortIndex = m_clockReference == eClockRefAudio ? 81 : 80;
+ cOmx::PtsToTicks(pts, timeStamp.nTimestamp);
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ /*m_clockReference == eClockRefAudio ?
+ OMX_IndexConfigTimeCurrentAudioReference :
+ OMX_IndexConfigTimeCurrentVideoReference*/
+ OMX_IndexConfigTimeClientStartTime, &timeStamp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set %s media clock reference!",
+ m_clockReference == eClockRefAudio ? "audio" : "video");
+}
+
+unsigned int cOmx::GetMediaTime(void)
+{
+ unsigned int ret = 0;
+
+ OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp;
+ OMX_INIT_STRUCT(timestamp);
+ timestamp.nPortIndex = OMX_ALL;
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeCurrentMediaTime, &timestamp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed get current clock reference!");
+ else
+ ret = timestamp.nTimestamp.nLowPart;
+
+ return ret;
+}
+
+void cOmx::SetClockReference(eClockReference clockReference)
+{
+ m_clockReference = clockReference;
+
+ OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock;
+ OMX_INIT_STRUCT(refClock);
+ refClock.eClock = (m_clockReference == eClockRefAudio) ?
+ OMX_TIME_RefClockAudio : OMX_TIME_RefClockVideo;
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
+ OMX_IndexConfigTimeActiveRefClock, &refClock) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed set active clock reference!");
+}
+
+void cOmx::SetVolume(int vol)
+{
+ dsyslog("rpihddevice: SetVolumeDevice(%d)", vol);
+
+ OMX_AUDIO_CONFIG_VOLUMETYPE volume;
+ OMX_INIT_STRUCT(volume);
+ volume.nPortIndex = 100;
+ volume.bLinear = OMX_TRUE;
+ volume.sVolume.nValue = vol * 100 / 255;
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexConfigAudioVolume, &volume) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set volume!");
+}
+
+void cOmx::SendEos(void)
+{
+#if 0
+ OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 1);
+ if (buf == NULL)
+ return;
+
+ buf->nFilledLen = 0;
+ buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
+
+ if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to send empty packet to video decoder!");
+
+ if (!m_eosEvent->Wait(10000))
+ esyslog("rpihddevice: time out waiting for EOS event!");
+#endif
+}
+
+void cOmx::Stop(void)
+{
+ // put video decoder into idle
+ ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle);
+
+ // put video scheduler into idle
+ ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoScheduler], 1);
+ ilclient_disable_tunnel(&m_tun[eVideoDecoderToVideoScheduler]);
+ ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1);
+ ilclient_disable_tunnel(&m_tun[eClockToVideoScheduler]);
+ ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateIdle);
+
+ // put video render into idle
+ ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1);
+ ilclient_disable_tunnel(&m_tun[eVideoSchedulerToVideoRender]);
+ ilclient_change_component_state(m_comp[eVideoRender], OMX_StateIdle);
+
+ // put audio render onto idle
+ ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
+ ilclient_disable_tunnel(&m_tun[eClockToAudioRender]);
+ ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle);
+
+ // disable port buffers and allow video decoder and audio render to reconfig
+ ilclient_disable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL);
+ ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL);
+
+ SetClockState(eClockStateStop);
+}
+
+void cOmx::FlushAudio(void)
+{
+ ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
+
+ if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_CommandFlush, 100, NULL) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to flush audio render!");
+
+ m_setAudioStartTime = true;
+}
+
+void cOmx::FlushVideo(bool flushRender)
+{
+ ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1);
+
+ if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_CommandFlush, 130, NULL) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to flush video decoder!");
+
+ ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoScheduler], 1);
+
+ if (flushRender)
+ ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1);
+
+ m_setVideoStartTime = true;
+ m_setVideoDiscontinuity = true;
+}
+
+int cOmx::SetVideoCodec(cVideoCodec::eCodec codec)
+{
+ // configure video decoder
+ OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat;
+ OMX_INIT_STRUCT(videoFormat);
+ videoFormat.nPortIndex = 130;
+ videoFormat.eCompressionFormat =
+ codec == cVideoCodec::eMPEG2 ? OMX_VIDEO_CodingMPEG2 :
+ codec == cVideoCodec::eH264 ? OMX_VIDEO_CodingAVC :
+ OMX_VIDEO_CodingAutoDetect;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
+ OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder parameters!");
+
+ if (ilclient_enable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL) != 0)
+ esyslog("rpihddevice: failed to enable port buffer on video decoder!");
+
+ if (ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to set video decoder to executing state!");
+
+ // setup clock tunnels first
+ if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ return 0;
+}
+
+int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, int samplingRate,
+ cAudioPort::ePort audioPort)
+{
+ // put audio render onto idle
+ ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
+ ilclient_disable_tunnel(&m_tun[eClockToAudioRender]);
+ ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle);
+ ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL);
+
+ if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_CommandFlush, 100, NULL) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to flush audio render!");
+
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ OMX_INIT_STRUCT(format);
+ format.nPortIndex = 100;
+ if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get audio port format parameters!");
+
+ format.eEncoding =
+ outputFormat == cAudioCodec::ePCM ? OMX_AUDIO_CodingPCM :
+ outputFormat == cAudioCodec::eMPG ? OMX_AUDIO_CodingMP3 :
+ outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP :
+ outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP :
+ outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC :
+ outputFormat == cAudioCodec::eDTS ? OMX_AUDIO_CodingDTS :
+ OMX_AUDIO_CodingAutoDetect;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio port format parameters!");
+
+ switch (outputFormat)
+ {
+ case cAudioCodec::eMPG:
+ OMX_AUDIO_PARAM_MP3TYPE mp3;
+ OMX_INIT_STRUCT(mp3);
+ mp3.nPortIndex = 100;
+ mp3.nChannels = channels;
+ mp3.nSampleRate = samplingRate;
+ mp3.eChannelMode = OMX_AUDIO_ChannelModeStereo; // ?
+ mp3.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; // should be MPEG-1 layer 2
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioMp3, &mp3) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render mp3 parameters!");
+ break;
+
+ case cAudioCodec::eAC3:
+ case cAudioCodec::eEAC3:
+ OMX_AUDIO_PARAM_DDPTYPE ddp;
+ OMX_INIT_STRUCT(ddp);
+ ddp.nPortIndex = 100;
+ ddp.nChannels = channels;
+ ddp.nSampleRate = samplingRate;
+ OMX_AUDIO_CHANNEL_MAPPING(ddp, channels);
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioDdp, &ddp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render ddp parameters!");
+ break;
+
+ case cAudioCodec::eAAC:
+ OMX_AUDIO_PARAM_AACPROFILETYPE aac;
+ OMX_INIT_STRUCT(aac);
+ aac.nPortIndex = 100;
+ aac.nChannels = channels;
+ aac.nSampleRate = samplingRate;
+ aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4LATM;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioAac, &aac) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render aac parameters!");
+ break;
+
+ case cAudioCodec::eDTS:
+ OMX_AUDIO_PARAM_DTSTYPE dts;
+ OMX_INIT_STRUCT(aac);
+ dts.nPortIndex = 100;
+ dts.nChannels = channels;
+ dts.nSampleRate = samplingRate;
+ dts.nDtsType = 1; // ??
+ dts.nFormat = 0; // ??
+ dts.nDtsFrameSizeBytes = 0; // ?
+ OMX_AUDIO_CHANNEL_MAPPING(dts, channels);
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioDts, &dts) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render dts parameters!");
+ break;
+
+ case cAudioCodec::ePCM:
+ OMX_AUDIO_PARAM_PCMMODETYPE pcm;
+ OMX_INIT_STRUCT(pcm);
+ pcm.nPortIndex = 100;
+ pcm.nChannels = channels == 6 ? 8 : channels;
+ pcm.eNumData = OMX_NumericalDataSigned;
+ pcm.eEndian = OMX_EndianLittle;
+ pcm.bInterleaved = OMX_TRUE;
+ pcm.nBitPerSample = 16;
+ pcm.nSamplingRate = samplingRate;
+ pcm.ePCMMode = OMX_AUDIO_PCMModeLinear;
+ OMX_AUDIO_CHANNEL_MAPPING(pcm, channels);
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexParamAudioPcm, &pcm) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render pcm parameters!");
+ break;
+
+ default:
+ esyslog("rpihddevice: output codec not supported: %s!",
+ cAudioCodec::Str(outputFormat));
+ break;
+ }
+
+ OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
+ OMX_INIT_STRUCT(audioDest);
+ strcpy((char *)audioDest.sName,
+ audioPort == cAudioPort::eLocal ? "local" : "hdmi");
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]),
+ OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio destination!");
+
+ if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0)
+ esyslog("rpihddevice: failed to enable port buffer on audio render!");
+
+ ilclient_change_component_state(m_comp[eAudioRender], OMX_StateExecuting);
+
+ if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ return 0;
+}
+
+OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts)
+{
+ m_mutex->Lock();
+ OMX_BUFFERHEADERTYPE* buf = NULL;
+
+ if (m_freeAudioBuffers > 0)
+ {
+ buf = ilclient_get_input_buffer(m_comp[eAudioRender], 100, 0);
+ if (buf)
+ {
+ cOmx::PtsToTicks(pts, buf->nTimeStamp);
+ buf->nFlags = m_setAudioStartTime ? OMX_BUFFERFLAG_STARTTIME : 0;
+ buf->nFlags |= pts ? 0 : OMX_BUFFERFLAG_TIME_UNKNOWN;
+
+ m_setAudioStartTime = false;
+ m_freeAudioBuffers--;
+ }
+ }
+ m_mutex->Unlock();
+ return buf;
+}
+
+OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts)
+{
+ m_mutex->Lock();
+ OMX_BUFFERHEADERTYPE* buf = NULL;
+
+ if (m_freeVideoBuffers > 0)
+ {
+ buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 0);
+ if (buf)
+ {
+ cOmx::PtsToTicks(pts, buf->nTimeStamp);
+ buf->nFlags = m_setVideoStartTime ?
+ OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN;
+ buf->nFlags |= m_setVideoDiscontinuity ? OMX_BUFFERFLAG_DISCONTINUITY : 0;
+
+ m_setVideoStartTime = false;
+ m_setVideoDiscontinuity = false;
+ m_freeVideoBuffers--;
+ }
+ }
+ m_mutex->Unlock();
+ return buf;
+}
+
+bool cOmx::PollVideoBuffers(int minBuffers)
+{
+ return (m_freeVideoBuffers > minBuffers);
+}
+
+bool cOmx::PollAudioBuffers(int minBuffers)
+{
+ return (m_freeAudioBuffers > minBuffers);
+}
+
+bool cOmx::EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf)
+{
+ if (!buf)
+ return false;
+
+ return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf)
+ == OMX_ErrorNone);
+}
+
+bool cOmx::EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf)
+{
+ if (!buf)
+ return false;
+
+ return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf)
+ == OMX_ErrorNone);
+}
diff --git a/omx.h b/omx.h
new file mode 100644
index 0000000..08c56b6
--- /dev/null
+++ b/omx.h
@@ -0,0 +1,122 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef OMX_H
+#define OMX_H
+
+#include "types.h"
+
+extern "C"
+{
+#include "ilclient.h"
+}
+
+class cMutex;
+
+class cOmx
+{
+
+public:
+
+ cOmx();
+ virtual ~cOmx();
+ int Init(void);
+ int DeInit(void);
+
+ static OMX_TICKS ToOmxTicks(int64_t val);
+ static int64_t FromOmxTicks(OMX_TICKS &ticks);
+ static void PtsToTicks(uint64_t pts, OMX_TICKS &ticks);
+ static uint64_t TicksToPts(OMX_TICKS &ticks);
+
+ int64_t GetSTC(void);
+ bool IsClockRunning(void);
+
+ enum eClockState {
+ eClockStateRun,
+ eClockStateStop,
+ eClockStateWaitForVideo,
+ eClockStateWaitForAudio,
+ eClockStateWaitForAudioVideo
+ };
+
+ void SetClockState(eClockState clockState);
+ void SetClockScale(float scale);
+ void SetMediaTime(uint64_t pts);
+ unsigned int GetMediaTime(void);
+
+ enum eClockReference {
+ eClockRefAudio,
+ eClockRefVideo
+ };
+
+ void SetClockReference(eClockReference clockReference);
+ void SetVolume(int vol);
+ void SendEos(void);
+ void Stop(void);
+
+ void FlushAudio(void);
+ void FlushVideo(bool flushRender = false);
+
+ int SetVideoCodec(cVideoCodec::eCodec codec);
+ int SetupAudioRender(cAudioCodec::eCodec outputFormat,
+ int channels, int samplingRate, cAudioPort::ePort audioPort);
+
+ OMX_BUFFERHEADERTYPE* GetAudioBuffer(uint64_t pts = 0);
+ OMX_BUFFERHEADERTYPE* GetVideoBuffer(uint64_t pts = 0);
+ bool PollVideoBuffers(int minBuffers = 0);
+ bool PollAudioBuffers(int minBuffers = 0);
+
+ bool EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf);
+ bool EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf);
+
+private:
+
+ static const char* errStr(int err);
+
+ enum eOmxComponent {
+ eClock = 0,
+ eVideoDecoder,
+ eVideoScheduler,
+ eVideoRender,
+ eAudioRender,
+ eNumComponents
+ };
+
+ enum eOmxTunnel {
+ eVideoDecoderToVideoScheduler = 0,
+ eVideoSchedulerToVideoRender,
+ eClockToVideoScheduler,
+ eClockToAudioRender,
+ eNumTunnels
+ };
+
+ ILCLIENT_T *m_client;
+ COMPONENT_T *m_comp[cOmx::eNumComponents + 1];
+ TUNNEL_T m_tun[cOmx::eNumTunnels + 1];
+
+ cMutex *m_mutex;
+
+ bool m_setVideoStartTime;
+ bool m_setAudioStartTime;
+ bool m_setVideoDiscontinuity;
+
+ int m_freeAudioBuffers;
+ int m_freeVideoBuffers;
+
+ eClockReference m_clockReference;
+
+ void HandleEndOfStream(unsigned int portId);
+ void HandlePortSettingsChanged(unsigned int portId);
+ void HandleBufferEmpty(COMPONENT_T *comp);
+
+ static void OnBufferEmpty(void *instance, COMPONENT_T *comp);
+ static void OnPortSettingsChanged(void *instance, COMPONENT_T *comp, unsigned int data);
+ static void OnEndOfStream(void *instance, COMPONENT_T *comp, unsigned int data);
+ static void OnError(void *instance, COMPONENT_T *comp, unsigned int data);
+
+};
+
+#endif
diff --git a/omxdevice.c b/omxdevice.c
index aa5610e..d1893e5 100644
--- a/omxdevice.c
+++ b/omxdevice.c
@@ -5,6 +5,7 @@
*/
#include "omxdevice.h"
+#include "omx.h"
#include "audio.h"
#include "setup.h"
@@ -13,860 +14,13 @@
#include <string.h>
-extern "C"
-{
-#include "ilclient.h"
-}
-
-#include "bcm_host.h"
-
-#define OMX_INIT_STRUCT(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
-
-#define OMX_AUDIO_CHANNEL_MAPPING(s, c) \
-switch (c) { \
-case 4: \
- (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \
- (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \
- (s).eChannelMapping[2] = OMX_AUDIO_ChannelLR; \
- (s).eChannelMapping[3] = OMX_AUDIO_ChannelRR; \
- break; \
-case 1: \
- (s).eChannelMapping[0] = OMX_AUDIO_ChannelCF; \
- break; \
-case 8: \
- (s).eChannelMapping[6] = OMX_AUDIO_ChannelLS; \
- (s).eChannelMapping[7] = OMX_AUDIO_ChannelRS; \
-case 6: \
- (s).eChannelMapping[2] = OMX_AUDIO_ChannelCF; \
- (s).eChannelMapping[3] = OMX_AUDIO_ChannelLFE; \
- (s).eChannelMapping[4] = OMX_AUDIO_ChannelLR; \
- (s).eChannelMapping[5] = OMX_AUDIO_ChannelRR; \
-case 2: \
-default: \
- (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \
- (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \
- break; }
-
-class cOmx
-{
-
-private:
-
- static const char* errStr(int err)
- {
- return err == OMX_ErrorNone ? "None" :
- err == OMX_ErrorInsufficientResources ? "InsufficientResources" :
- err == OMX_ErrorUndefined ? "Undefined" :
- err == OMX_ErrorInvalidComponentName ? "InvalidComponentName" :
- err == OMX_ErrorComponentNotFound ? "ComponentNotFound" :
- err == OMX_ErrorInvalidComponent ? "InvalidComponent" :
- err == OMX_ErrorBadParameter ? "BadParameter" :
- err == OMX_ErrorNotImplemented ? "NotImplemented" :
- err == OMX_ErrorUnderflow ? "Underflow" :
- err == OMX_ErrorOverflow ? "Overflow" :
- err == OMX_ErrorHardware ? "Hardware" :
- err == OMX_ErrorInvalidState ? "InvalidState" :
- err == OMX_ErrorStreamCorrupt ? "StreamCorrupt" :
- err == OMX_ErrorPortsNotCompatible ? "PortsNotCompatible" :
- err == OMX_ErrorResourcesLost ? "ResourcesLost" :
- err == OMX_ErrorNoMore ? "NoMore" :
- err == OMX_ErrorVersionMismatch ? "VersionMismatch" :
- err == OMX_ErrorNotReady ? "NotReady" :
- err == OMX_ErrorTimeout ? "Timeout" :
- err == OMX_ErrorSameState ? "SameState" :
- err == OMX_ErrorResourcesPreempted ? "ResourcesPreempted" :
- err == OMX_ErrorPortUnresponsiveDuringAllocation ? "PortUnresponsiveDuringAllocation" :
- err == OMX_ErrorPortUnresponsiveDuringDeallocation ? "PortUnresponsiveDuringDeallocation" :
- err == OMX_ErrorPortUnresponsiveDuringStop ? "PortUnresponsiveDuringStop" :
- err == OMX_ErrorIncorrectStateTransition ? "IncorrectStateTransition" :
- err == OMX_ErrorIncorrectStateOperation ? "IncorrectStateOperation" :
- err == OMX_ErrorUnsupportedSetting ? "UnsupportedSetting" :
- err == OMX_ErrorUnsupportedIndex ? "UnsupportedIndex" :
- err == OMX_ErrorBadPortIndex ? "BadPortIndex" :
- err == OMX_ErrorPortUnpopulated ? "PortUnpopulated" :
- err == OMX_ErrorComponentSuspended ? "ComponentSuspended" :
- err == OMX_ErrorDynamicResourcesUnavailable ? "DynamicResourcesUnavailable" :
- err == OMX_ErrorMbErrorsInFrame ? "MbErrorsInFrame" :
- err == OMX_ErrorFormatNotDetected ? "FormatNotDetected" :
- err == OMX_ErrorContentPipeOpenFailed ? "ContentPipeOpenFailed" :
- err == OMX_ErrorContentPipeCreationFailed ? "ContentPipeCreationFailed" :
- err == OMX_ErrorSeperateTablesUsed ? "SeperateTablesUsed" :
- err == OMX_ErrorTunnelingUnsupported ? "TunnelingUnsupported" :
- err == OMX_ErrorKhronosExtensions ? "KhronosExtensions" :
- err == OMX_ErrorVendorStartUnused ? "VendorStartUnused" :
- err == OMX_ErrorDiskFull ? "DiskFull" :
- err == OMX_ErrorMaxFileSize ? "MaxFileSize" :
- err == OMX_ErrorDrmUnauthorised ? "DrmUnauthorised" :
- err == OMX_ErrorDrmExpired ? "DrmExpired" :
- err == OMX_ErrorDrmGeneral ? "DrmGeneral" :
- "unknown";
- };
-
- enum eOmxComponent {
- eClock = 0,
- eVideoDecoder,
- eVideoScheduler,
- eVideoRender,
- eAudioRender,
- eNumComponents
- };
-
- enum eOmxTunnel {
- eVideoDecoderToVideoScheduler = 0,
- eVideoSchedulerToVideoRender,
- eClockToVideoScheduler,
- eClockToAudioRender,
- eNumTunnels
- };
-
- // to do: make this private!
-
- ILCLIENT_T *m_client;
- COMPONENT_T *m_comp[cOmx::eNumComponents + 1];
- TUNNEL_T m_tun[cOmx::eNumTunnels + 1];
-
- cMutex *m_mutex;
-
- bool m_firstVideoBuffer;
- bool m_firstAudioBuffer;
-
- int m_freeAudioBuffers;
- int m_freeVideoBuffers;
-
- void HandleEndOfStream(unsigned int portId)
- {
- dsyslog("rpihddevice: HandleEndOfStream(%d)", portId);
-
- switch (portId)
- {
- case 131:
- break;
-
- case 11:
- break;
-
- case 90:
- break;
- }
- }
-
- void HandlePortSettingsChanged(unsigned int portId)
- {
- //dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId);
-
- switch (portId)
- {
- case 131:
-
- OMX_PARAM_PORTDEFINITIONTYPE portdef;
- OMX_INIT_STRUCT(portdef);
- portdef.nPortIndex = 131;
- if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamPortDefinition,
- &portdef) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to get video decoder port format!");
-
- OMX_CONFIG_INTERLACETYPE interlace;
- OMX_INIT_STRUCT(interlace);
- interlace.nPortIndex = 131;
- if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexConfigCommonInterlace,
- &interlace) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to get video decoder interlace config!");
-
- dsyslog("rpihddevice: decoding video %dx%d%s",
- portdef.format.video.nFrameWidth,
- portdef.format.video.nFrameHeight,
- interlace.eMode == OMX_InterlaceProgressive ? "p" : "i");
-
- if (ilclient_setup_tunnel(&m_tun[eVideoDecoderToVideoScheduler], 0, 0) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from video decoder to scheduler!");
- if (ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateExecuting) != 0)
- esyslog("rpihddevice: failed to enable video scheduler!");
- break;
-
- case 11:
- if (ilclient_setup_tunnel(&m_tun[eVideoSchedulerToVideoRender], 0, 1000) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from scheduler to render!");
- if (ilclient_change_component_state(m_comp[eVideoRender], OMX_StateExecuting) != 0)
- esyslog("rpihddevice: failed to enable video render!");
- break;
- }
- }
-
- void HandleBufferEmpty(COMPONENT_T *comp)
- {
- if (comp == m_comp[eVideoDecoder])
- {
- m_mutex->Lock();
- m_freeVideoBuffers++;
- m_mutex->Unlock();
- }
- else if (comp == m_comp[eAudioRender])
- {
- m_mutex->Lock();
- m_freeAudioBuffers++;
- m_mutex->Unlock();
- }
- }
-
- static void OnBufferEmpty(void *instance, COMPONENT_T *comp)
- {
- cOmx* omx = static_cast <cOmx*> (instance);
- omx->HandleBufferEmpty(comp);
- }
-
- static void OnPortSettingsChanged(void *instance, COMPONENT_T *comp, unsigned int data)
- {
- cOmx* omx = static_cast <cOmx*> (instance);
- omx->HandlePortSettingsChanged(data);
- }
-
- static void OnEndOfStream(void *instance, COMPONENT_T *comp, unsigned int data)
- {
- cOmx* omx = static_cast <cOmx*> (instance);
- omx->HandleEndOfStream(data);
- }
-
- static void OnError(void *instance, COMPONENT_T *comp, unsigned int data)
- {
- if (data != OMX_ErrorSameState)
- esyslog("rpihddevice: OmxError(%s)", errStr((int)data));
- }
-
-public:
-
- cOmx() :
- m_mutex(new cMutex()),
- m_firstVideoBuffer(true),
- m_firstAudioBuffer(true),
- m_freeAudioBuffers(0),
- m_freeVideoBuffers(0)
- { }
-
- virtual ~cOmx()
- {
- delete m_mutex;
- }
-
- int Init(void)
- {
- m_client = ilclient_init();
- if (m_client == NULL)
- esyslog("rpihddevice: ilclient_init() failed!");
-
- if (OMX_Init() != OMX_ErrorNone)
- esyslog("rpihddevice: OMX_Init() failed!");
-
- ilclient_set_error_callback(m_client, OnError, this);
- ilclient_set_empty_buffer_done_callback(m_client, OnBufferEmpty, this);
- ilclient_set_port_settings_callback(m_client, OnPortSettingsChanged, this);
- ilclient_set_eos_callback(m_client, OnEndOfStream, this);
-
- // create video_decode
- if (ilclient_create_component(m_client, &m_comp[eVideoDecoder],
- "video_decode", (ILCLIENT_CREATE_FLAGS_T)
- (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
- esyslog("rpihddevice: failed creating video decoder!");
-
- // create video_render
- if (ilclient_create_component(m_client, &m_comp[eVideoRender],
- "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0)
- esyslog("rpihddevice: failed creating video render!");
-
- //create clock
- if (ilclient_create_component(m_client, &m_comp[eClock],
- "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0)
- esyslog("rpihddevice: failed creating clock!");
-
- // create audio_render
- if (ilclient_create_component(m_client, &m_comp[eAudioRender],
- "audio_render", (ILCLIENT_CREATE_FLAGS_T)
- (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
- esyslog("rpihddevice: failed creating audio render!");
-
- //create video_scheduler
- if (ilclient_create_component(m_client, &m_comp[eVideoScheduler],
- "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0)
- esyslog("rpihddevice: failed creating video scheduler!");
-
- // setup tunnels
- set_tunnel(&m_tun[eVideoDecoderToVideoScheduler], m_comp[eVideoDecoder], 131, m_comp[eVideoScheduler], 10);
- set_tunnel(&m_tun[eVideoSchedulerToVideoRender], m_comp[eVideoScheduler], 11, m_comp[eVideoRender], 90);
- set_tunnel(&m_tun[eClockToVideoScheduler], m_comp[eClock], 80, m_comp[eVideoScheduler], 12);
- set_tunnel(&m_tun[eClockToAudioRender], m_comp[eClock], 81, m_comp[eAudioRender], 101);
-
- // setup clock tunnels first
- if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
-
- if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from clock to audio render!");
-
- OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refclock;
- OMX_INIT_STRUCT(refclock);
- refclock.eClock = OMX_TIME_RefClockAudio;
- // refclock.eClock = OMX_TIME_RefClockVideo;
-
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeActiveRefClock, &refclock) != OMX_ErrorNone)
- esyslog("rpihddevice: failed set active clock reference!");
-
- ilclient_change_component_state(m_comp[eClock], OMX_StateExecuting);
- ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle);
-
- // set up the number and size of buffers for audio render
- m_freeAudioBuffers = 16;
- OMX_PARAM_PORTDEFINITIONTYPE param;
- OMX_INIT_STRUCT(param);
- param.nPortIndex = 100;
- if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to get audio render port parameters!");
- param.nBufferSize = 128 * 1024;
- param.nBufferCountActual = m_freeAudioBuffers;
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render port parameters!");
-
- OMX_INIT_STRUCT(param);
- param.nPortIndex = 130;
- if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
- OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to get video decoder port parameters!");
-
- m_freeVideoBuffers = param.nBufferCountActual;
- dsyslog("rpihddevice: started with %d video and %d audio buffers",
- m_freeVideoBuffers, m_freeAudioBuffers);
-
-/* // configure video decoder stall callback
- OMX_CONFIG_BUFFERSTALLTYPE stallConf;
- OMX_INIT_STRUCT(stallConf);
- stallConf.nPortIndex = 131;
- stallConf.nDelay = 1500 * 1000;
- if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigBufferStall,
- &stallConf) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set video decoder stall config!");
-
- OMX_CONFIG_REQUESTCALLBACKTYPE reqCallback;
- OMX_INIT_STRUCT(reqCallback);
- reqCallback.nPortIndex = 131;
- reqCallback.nIndex = OMX_IndexConfigBufferStall;
- reqCallback.bEnable = OMX_TRUE;
- if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigRequestCallback,
- &reqCallback) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set video decoder stall callback!");
-*/
- // if (ilclient_enable_port_buffers(comp[eAudioRender], 100, NULL, NULL, NULL) != 0)
- // esyslog("rpihddevice: failed to enable port buffer on audio render!");
-
- return 0;
- }
-
- int DeInit(void)
- {
- ilclient_teardown_tunnels(m_tun);
- ilclient_state_transition(m_comp, OMX_StateIdle);
- ilclient_state_transition(m_comp, OMX_StateLoaded);
- ilclient_cleanup_components(m_comp);
-
- OMX_Deinit();
- ilclient_destroy(m_client);
-
- return 0;
- }
-
- static void PtsToTicks(uint64_t pts, OMX_TICKS &ticks)
- {
- // ticks = pts * OMX_TICKS_PER_SECOND / PTSTICKS
- pts = pts * 100 / 9;
- ticks.nLowPart = (OMX_U32)pts;
- ticks.nHighPart = (OMX_U32)(pts >> 32);
- }
-
- static uint64_t TicksToPts(OMX_TICKS &ticks)
- {
- // pts = ticks * PTSTICKS / OMX_TICKS_PER_SECOND
- uint64_t pts = ticks.nHighPart;
- pts = (pts << 32) + ticks.nLowPart;
- pts = pts * 9 / 100;
- return pts;
- }
-
- int64_t GetSTC(void)
- {
- int64_t stc = -1;
-
- OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp;
- OMX_INIT_STRUCT(timestamp);
- timestamp.nPortIndex = OMX_ALL;
-
- if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeCurrentMediaTime, &timestamp) != OMX_ErrorNone)
- esyslog("rpihddevice: failed get current clock reference!");
- else
- stc = TicksToPts(timestamp.nTimestamp);
-
- return stc;
- }
-
- bool IsClockRunning(void)
- {
- OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
- OMX_INIT_STRUCT(cstate);
-
- if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
- esyslog("rpihddevice: failed get clock state!");
-
- if (cstate.eState == OMX_TIME_ClockStateRunning)
- return true;
- else
- return false;
- }
-
- enum eClockState {
- eClockStateRun,
- eClockStateStop,
- eClockStateWaitForVideo,
- eClockStateWaitForAudio,
- eClockStateWaitForAudioVideo
- };
-
- void SetClockState(eClockState clockState)
- {
- dsyslog("rpihddevice: SetClockState(%s)",
- clockState == eClockStateRun ? "eClockStateRun" :
- clockState == eClockStateStop ? "eClockStateStop" :
- clockState == eClockStateWaitForVideo ? "eClockStateWaitForVideo" :
- clockState == eClockStateWaitForAudio ? "eClockStateWaitForAudio" :
- clockState == eClockStateWaitForAudioVideo ? "eClockStateWaitForAudioVideo" : "unknown");
-
- OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
- OMX_INIT_STRUCT(cstate);
-
- if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
- esyslog("rpihddevice: failed get clock state!");
-
- if ((cstate.eState == OMX_TIME_ClockStateRunning) &&
- (clockState == eClockStateWaitForVideo ||
- clockState == eClockStateWaitForAudio ||
- clockState == eClockStateWaitForAudioVideo))
- {
- // clock already running, need to stop it first
- cstate.eState = OMX_TIME_ClockStateStopped;
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
- esyslog("rpihddevice: failed set clock state!");
- }
-
- switch (clockState)
- {
- case eClockStateRun:
- cstate.eState = OMX_TIME_ClockStateRunning;
- break;
-
- case eClockStateStop:
- cstate.eState = OMX_TIME_ClockStateStopped;
- break;
-
- case eClockStateWaitForVideo:
- cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT0;
- m_firstVideoBuffer = true;
- break;
-
- case eClockStateWaitForAudio:
- cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT1;
- m_firstAudioBuffer = true;
- break;
-
- case eClockStateWaitForAudioVideo:
- cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1;
- m_firstVideoBuffer = true;
- m_firstAudioBuffer = true;
- break;
- }
-
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
- esyslog("rpihddevice: failed set clock state!");
- }
-
- void SetClockScale(float scale)
- {
- OMX_TIME_CONFIG_SCALETYPE scaleType;
- OMX_INIT_STRUCT(scaleType);
- scaleType.xScale = floor(scale * pow(2, 16));
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
- OMX_IndexConfigTimeScale, &scaleType) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set clock scale (%d)!", scaleType.xScale);
- else
- dsyslog("rpihddevice: set clock scale to %.2f (%d)", scale, scaleType.xScale);
- }
-
- void SetVolume(int vol)
- {
- dsyslog("rpihddevice: SetVolumeDevice(%d)", vol);
-
- OMX_AUDIO_CONFIG_VOLUMETYPE volume;
- OMX_INIT_STRUCT(volume);
- volume.nPortIndex = 100;
- volume.bLinear = OMX_TRUE;
- volume.sVolume.nValue = vol * 100 / 255;
-
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexConfigAudioVolume, &volume) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set volume!");
- }
-
- void SendEos(void)
- {
-#if 0
- OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 1);
- if (buf == NULL)
- return;
-
- buf->nFilledLen = 0;
- buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
-
- if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to send empty packet to video decoder!");
-
- if (!m_eosEvent->Wait(10000))
- esyslog("rpihddevice: time out waiting for EOS event!");
-#endif
- }
-
- void Stop(void)
- {
- // put video decoder into idle
- ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle);
-
- // put video scheduler into idle
- ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoScheduler], 1);
- ilclient_disable_tunnel(&m_tun[eVideoDecoderToVideoScheduler]);
- ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1);
- ilclient_disable_tunnel(&m_tun[eClockToVideoScheduler]);
- ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateIdle);
-
- // put video render into idle
- ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1);
- ilclient_disable_tunnel(&m_tun[eVideoSchedulerToVideoRender]);
- ilclient_change_component_state(m_comp[eVideoRender], OMX_StateIdle);
-
- // put audio render onto idle
- ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
- ilclient_disable_tunnel(&m_tun[eClockToAudioRender]);
- ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle);
-
- // disable port buffers and allow video decoder and audio render to reconfig
- ilclient_disable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL);
- ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL);
-
- SetClockState(eClockStateStop);
- }
-
- int SetVideoCodec(cOmxDevice::eVideoCodec codec)
- {
- // configure video decoder
- OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat;
- OMX_INIT_STRUCT(videoFormat);
- videoFormat.nPortIndex = 130;
- videoFormat.eCompressionFormat =
- codec == cOmxDevice::eMPEG2 ? OMX_VIDEO_CodingMPEG2 :
- codec == cOmxDevice::eH264 ? OMX_VIDEO_CodingAVC :
- OMX_VIDEO_CodingAutoDetect;
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
- OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set video decoder parameters!");
-
- OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ectype;
- OMX_INIT_STRUCT(ectype);
- ectype.bStartWithValidFrame = OMX_FALSE;
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
- OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ectype) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set video decode error concealment failed\n");
-
- if (ilclient_enable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL) != 0)
- esyslog("rpihddevice: failed to enable port buffer on video decoder!");
-
- if (ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateExecuting) != 0)
- esyslog("rpihddevice: failed to set video decoder to executing state!");
-
- // setup clock tunnels first
- if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
-
- return 0;
- }
-
- int SetupAudioRender(cAudioDecoder::eCodec outputFormat, int channels, int samplingRate,
- cAudioDecoder::ePort audioPort)
- {
- // put audio render onto idle
- ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
- ilclient_disable_tunnel(&m_tun[eClockToAudioRender]);
- ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle);
- ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL);
-
- OMX_AUDIO_PARAM_PORTFORMATTYPE format;
- OMX_INIT_STRUCT(format);
- format.nPortIndex = 100;
- if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to get audio port format parameters!");
-
- format.eEncoding =
- outputFormat == cAudioDecoder::ePCM ? OMX_AUDIO_CodingPCM :
- outputFormat == cAudioDecoder::eMPG ? OMX_AUDIO_CodingMP3 :
- outputFormat == cAudioDecoder::eAC3 ? OMX_AUDIO_CodingDDP :
- outputFormat == cAudioDecoder::eEAC3 ? OMX_AUDIO_CodingDDP :
- outputFormat == cAudioDecoder::eAAC ? OMX_AUDIO_CodingAAC :
- outputFormat == cAudioDecoder::eDTS ? OMX_AUDIO_CodingDTS :
- OMX_AUDIO_CodingAutoDetect;
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio port format parameters!");
-
- switch (outputFormat)
- {
- case cAudioDecoder::eMPG:
- OMX_AUDIO_PARAM_MP3TYPE mp3;
- OMX_INIT_STRUCT(mp3);
- mp3.nPortIndex = 100;
- mp3.nChannels = channels;
- mp3.nSampleRate = samplingRate;
- mp3.eChannelMode = OMX_AUDIO_ChannelModeStereo; // ?
- mp3.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; // should be MPEG-1 layer 2
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioMp3, &mp3) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render mp3 parameters!");
- break;
-
- case cAudioDecoder::eAC3:
- case cAudioDecoder::eEAC3:
- OMX_AUDIO_PARAM_DDPTYPE ddp;
- OMX_INIT_STRUCT(ddp);
- ddp.nPortIndex = 100;
- ddp.nChannels = channels;
- ddp.nSampleRate = samplingRate;
- OMX_AUDIO_CHANNEL_MAPPING(ddp, channels);
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioDdp, &ddp) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render ddp parameters!");
- break;
-
- case cAudioDecoder::eAAC:
- OMX_AUDIO_PARAM_AACPROFILETYPE aac;
- OMX_INIT_STRUCT(aac);
- aac.nPortIndex = 100;
- aac.nChannels = channels;
- aac.nSampleRate = samplingRate;
- aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4LATM;
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioAac, &aac) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render aac parameters!");
- break;
-
- case cAudioDecoder::eDTS:
- OMX_AUDIO_PARAM_DTSTYPE dts;
- OMX_INIT_STRUCT(aac);
- dts.nPortIndex = 100;
- dts.nChannels = channels;
- dts.nSampleRate = samplingRate;
- dts.nDtsType = 1; // ??
- dts.nFormat = 0; // ??
- dts.nDtsFrameSizeBytes = 0; // ?
- OMX_AUDIO_CHANNEL_MAPPING(dts, channels);
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioDts, &dts) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render dts parameters!");
- break;
-
- case cAudioDecoder::ePCM:
- OMX_AUDIO_PARAM_PCMMODETYPE pcm;
- OMX_INIT_STRUCT(pcm);
- pcm.nPortIndex = 100;
- pcm.nChannels = channels == 6 ? 8 : channels;
- pcm.eNumData = OMX_NumericalDataSigned;
- pcm.eEndian = OMX_EndianLittle;
- pcm.bInterleaved = OMX_TRUE;
- pcm.nBitPerSample = 16;
- pcm.nSamplingRate = samplingRate;
- pcm.ePCMMode = OMX_AUDIO_PCMModeLinear;
- OMX_AUDIO_CHANNEL_MAPPING(pcm, channels);
-
- if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexParamAudioPcm, &pcm) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio render pcm parameters!");
- break;
-
- default:
- break;
- }
-
- OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
- OMX_INIT_STRUCT(audioDest);
- strcpy((char *)audioDest.sName,
- audioPort == cAudioDecoder::eLocal ? "local" : "hdmi");
-
- if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]),
- OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone)
- esyslog("rpihddevice: failed to set audio destination!");
-
- if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0)
- esyslog("rpihddevice: failed to enable port buffer on audio render!");
-
- ilclient_change_component_state(m_comp[eAudioRender], OMX_StateExecuting);
-
- if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0)
- esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
-
- return 0;
- }
-
- OMX_BUFFERHEADERTYPE* GetAudioBuffer(uint64_t pts = 0)
- {
- m_mutex->Lock();
- OMX_BUFFERHEADERTYPE* buf = NULL;
-
- if (m_freeAudioBuffers > 0)
- {
- buf = ilclient_get_input_buffer(m_comp[eAudioRender], 100, 0);
-
- if (buf != NULL)
- {
- cOmx::PtsToTicks(pts, buf->nTimeStamp);
- buf->nFlags = m_firstAudioBuffer ? OMX_BUFFERFLAG_STARTTIME : 0; //OMX_BUFFERFLAG_TIME_UNKNOWN;
- m_firstAudioBuffer = false;
- m_freeAudioBuffers--;
- }
- }
- m_mutex->Unlock();
- return buf;
- }
-
- OMX_BUFFERHEADERTYPE* GetVideoBuffer(uint64_t pts = 0)
- {
- m_mutex->Lock();
- OMX_BUFFERHEADERTYPE* buf = NULL;
-
- if (m_freeVideoBuffers > 0)
- {
- buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 0);
-
- if (buf != NULL)
- {
- cOmx::PtsToTicks(pts, buf->nTimeStamp);
- buf->nFlags = m_firstVideoBuffer ? OMX_BUFFERFLAG_STARTTIME : 0; //OMX_BUFFERFLAG_TIME_UNKNOWN;
- m_firstVideoBuffer = false;
- m_freeVideoBuffers--;
- }
- }
- m_mutex->Unlock();
- return buf;
- }
-
- bool VideoBuffersAvailable(void)
- {
- return (m_freeVideoBuffers > 0);
- }
-
- bool EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf)
- {
- if (!buf)
- return false;
-
- return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf) == OMX_ErrorNone);
- }
-
- bool EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf)
- {
- if (!buf)
- return false;
-
- return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) == OMX_ErrorNone);
- }
-};
-
-/* ------------------------------------------------------------------------- */
-#if 0
-class cAudio
-{
-
-public:
-
- cAudio() :
- sampleRate(0),
- bitDepth(0),
- nChannels(0),
- encoding(0),
- m_handle(0)
- {
- int ret;
- mpg123_init();
- m_handle = mpg123_new(NULL, &ret);
- if (m_handle == NULL)
- esyslog("rpihddevice: failed to create mpg123 handle!");
-
- if (mpg123_open_feed(m_handle) == MPG123_ERR)
- esyslog("rpihddevice: failed to open mpg123 feed!");
-
- dsyslog("rpihddevice: new cAudio()");
- }
-
- ~cAudio()
- {
- mpg123_delete(m_handle);
- dsyslog("rpihddevice: delete cAudio()");
- }
-
- bool writeData(const unsigned char *buf, unsigned int length)
- {
- return (mpg123_feed(m_handle, buf, length) != MPG123_ERR);
- }
-
- unsigned int readSamples(unsigned char *buf, unsigned length, bool &done)
- {
- unsigned int read = 0;
- done = (mpg123_read(m_handle, buf, length, &read) == MPG123_NEED_MORE);
- mpg123_getformat(m_handle, &sampleRate, &nChannels, &encoding);
- return read;
- }
-
- long sampleRate;
- int bitDepth;
- int nChannels;
- int encoding;
-
- mpg123_handle *m_handle;
-};
-#endif
-/* ------------------------------------------------------------------------- */
-
cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) :
cDevice(),
m_onPrimaryDevice(onPrimaryDevice),
m_omx(new cOmx()),
- m_audio(new cAudioDecoder()),
+ m_audio(new cAudioDecoder(m_omx)),
m_mutex(new cMutex()),
- m_state(eStop),
- m_audioCodecReady(false),
- m_videoCodecReady(false),
+ m_state(eNone),
m_audioId(0)
{
}
@@ -880,14 +34,14 @@ cOmxDevice::~cOmxDevice()
int cOmxDevice::Init(void)
{
- if (m_audio->Init() < 0)
+ if (m_omx->Init() < 0)
{
- esyslog("rpihddevice: failed to initialize audio!");
+ esyslog("rpihddevice: failed to initialize OMX!");
return -1;
}
- if (m_omx->Init() < 0)
+ if (m_audio->Init() < 0)
{
- esyslog("rpihddevice: failed to initialize OMX!");
+ esyslog("rpihddevice: failed to initialize audio!");
return -1;
}
return 0;
@@ -895,14 +49,14 @@ int cOmxDevice::Init(void)
int cOmxDevice::DeInit(void)
{
- if (m_omx->DeInit() < 0)
+ if (m_audio->DeInit() < 0)
{
- esyslog("rpihddevice: failed to deinitialize OMX!");
+ esyslog("rpihddevice: failed to deinitialize audio!");
return -1;
}
- if (m_audio->DeInit() < 0)
+ if (m_omx->DeInit() < 0)
{
- esyslog("rpihddevice: failed to deinitialize audio!");
+ esyslog("rpihddevice: failed to deinitialize OMX!");
return -1;
}
return 0;
@@ -917,7 +71,7 @@ bool cOmxDevice::CanReplay(void) const
{
dsyslog("rpihddevice: CanReplay");
// video codec de-initialization done
- return (m_state == eStop);
+ return (m_state == eNone);
}
bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
@@ -936,19 +90,19 @@ bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
{
case pmNone:
m_mutex->Lock();
- m_state = eStop;
+ if (HasVideo())
+ m_omx->FlushVideo(true);
+ if (HasAudio())
+ {
+ m_audio->Reset();
+ m_omx->FlushAudio();
+ }
m_omx->Stop();
- m_audioCodecReady = false;
- m_videoCodecReady = false;
+ SetState(eNone);
m_mutex->Unlock();
break;
case pmAudioVideo:
- m_mutex->Lock();
- m_state = eStarting;
- m_mutex->Unlock();
- break;
-
case pmAudioOnly:
case pmAudioOnlyBlack:
case pmVideoOnly:
@@ -961,153 +115,123 @@ bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
{
m_mutex->Lock();
+ int ret = Length;
- if (m_state == eStarting)
- {
- m_omx->SetClockState(cOmx::eClockStateWaitForAudio);
- m_state = ePlay;
- }
- else if (m_state != ePlay)
- {
- m_mutex->Unlock();
- dsyslog("rpihddevice: PlayAudio() not replaying!");
- return 0;
- }
+ // if first packet is audio, we assume audio only
+ if (State() == eNone)
+ SetState(eAudioOnly);
- if (!PesHasLength(Data))
+ if (State() == eAudioOnly || State() == eAudioVideo)
{
- esyslog("rpihddevice: empty audio packet dropped!");
- m_mutex->Unlock();
- return Length;
- }
-
- int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
- const uchar *payload = Data + PesPayloadOffset(Data);
- int length = PesLength(Data) - PesPayloadOffset(Data);
-
- if (m_audioId != Id)
- {
- m_audioId = Id;
- m_audioCodecReady = false;
- }
-
- // try to init codec
- if (!m_audioCodecReady || cRpiSetup::HasAudioSetupChanged())
- {
- if (m_audio->SetupAudioCodec(Data, Length))
+ if (m_audio->Poll())
{
- m_audioCodecReady = true;
- m_omx->SetupAudioRender(
- m_audio->GetOutputFormat(),
- m_audio->GetChannels(),
- m_audio->GetSamplingrate(),
- m_audio->GetOutputPort());
+ if (m_audioId != Id)
+ {
+ m_audioId = Id;
+ m_audio->Reset();
+ }
+
+ //dsyslog("A %llu", PesGetPts(Data));
+ if (!m_audio->WriteData(Data + PesPayloadOffset(Data),
+ PesLength(Data) - PesPayloadOffset(Data),
+ PesHasPts(Data) ? PesGetPts(Data) : 0))
+ esyslog("rpihddevice: failed to write data to audio decoder!");
}
else
- {
- m_mutex->Unlock();
- return Length;
- }
- }
-
- OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(pts);
- if (buf == NULL)
- {
- m_mutex->Unlock();
- return 0;
- }
-
- // decode and write audio packet
- buf->nFilledLen = m_audio->DecodeAudio(payload, length, buf->pBuffer, buf->nAllocLen);
-
- // if decoding failed, reset audio codec
- if (!buf->nFilledLen)
- m_audioCodecReady = false;
-
- if (!m_omx->EmptyAudioBuffer(buf))
- {
- m_mutex->Unlock();
- return 0;
+ ret = 0;
}
m_mutex->Unlock();
- return Length;
+ return ret;
}
int cOmxDevice::PlayVideo(const uchar *Data, int Length)
{
m_mutex->Lock();
+ int ret = Length;
- if (m_state == eStarting)
- {
- m_omx->SetClockState(cOmx::eClockStateWaitForAudioVideo);
- m_state = ePlay;
- }
- else if (m_state != ePlay)
- {
- m_mutex->Unlock();
- dsyslog("rpihddevice: PlayVideo() not replaying!");
- return 0;
- }
+ if (State() == eNone)
+ SetState(eStartingVideo);
- if (!PesHasLength(Data))
+ if (State() == eStartingVideo)
{
- esyslog("rpihddevice: empty video packet dropped!");
- m_mutex->Unlock();
- return Length;
+ cVideoCodec::eCodec codec = ParseVideoCodec(Data, Length);
+ if (codec != cVideoCodec::eInvalid)
+ {
+ if (cRpiSetup::IsVideoCodecSupported(codec))
+ {
+ m_omx->SetVideoCodec(codec);
+ SetState(eAudioVideo);
+ dsyslog("rpihddevice: set video codec to %s",
+ cVideoCodec::Str(codec));
+ }
+ else
+ {
+ SetState(eAudioOnly);
+ esyslog("rpihddevice: %s video codec not supported!",
+ cVideoCodec::Str(codec));
+ }
+ }
}
- int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
-
- const uchar *payload = Data + PesPayloadOffset(Data);
- int length = PesLength(Data) - PesPayloadOffset(Data);
-
- // try to init codec if PTS is valid
- if (!m_videoCodecReady && pts != 0)
+ if (State() == eVideoOnly || State() == eAudioVideo)
{
- eVideoCodec codec = GetVideoCodec(Data, Length);
- if (cRpiSetup::IsVideoCodecSupported(codec))
+ int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
+ OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts);
+ if (buf)
{
- m_videoCodecReady = (m_omx->SetVideoCodec(codec) == 0);
- dsyslog("rpihddevice: set video codec to %s!",
- VideoCodecStr(codec));
+ //dsyslog("V %llu", PesGetPts(Data));
+ const uchar *payload = Data + PesPayloadOffset(Data);
+ int length = PesLength(Data) - PesPayloadOffset(Data);
+ if (length <= buf->nAllocLen)
+ {
+ memcpy(buf->pBuffer, payload, length);
+ buf->nFilledLen = length;
+ }
+ else
+ esyslog("rpihddevice: video packet too long for video buffer!");
+
+ if (!m_omx->EmptyVideoBuffer(buf))
+ {
+ ret = 0;
+ esyslog("rpihddevice: failed to pass buffer to video decoder!");
+ }
}
}
- if (!m_videoCodecReady)
- {
- m_mutex->Unlock();
- return Length;
- }
- OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts);
- if (buf == NULL)
- {
- //esyslog("rpihddevice: failed to get video buffer!");
- m_mutex->Unlock();
- return 0;
- }
+ m_mutex->Unlock();
+ return ret;
+}
- if (length <= buf->nAllocLen)
+void cOmxDevice::SetState(eState state)
+{
+ switch (state)
{
- memcpy(buf->pBuffer, payload, length);
- buf->nFilledLen = length;
- }
- else
- esyslog("rpihddevice: video packet too long for video buffer!");
+ case eNone:
+ m_omx->SetClockState(cOmx::eClockStateStop);
+ break;
-// dsyslog("V: %u.%u - f:%d %lld", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart, buf->nFlags, pts);
-// dsyslog("rpihddevice: PlayVideo(%u.%u, %02x %02x %02x %02x %02x %02x %02x %02x, %d)", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart,
-// buf->pBuffer[0], buf->pBuffer[1], buf->pBuffer[2], buf->pBuffer[3],
-// buf->pBuffer[4], buf->pBuffer[5], buf->pBuffer[6], buf->pBuffer[7], buf->nFilledLen);
- if (!m_omx->EmptyVideoBuffer(buf))
- esyslog("rpihddevice: failed to pass buffer to video decoder!");
+ case eStartingVideo:
+ break;
- m_mutex->Unlock();
- return Length;
+ case eAudioOnly:
+ m_omx->SetClockState(cOmx::eClockStateWaitForAudio);
+ break;
+
+ case eVideoOnly:
+ m_omx->SetClockState(cOmx::eClockStateWaitForVideo);
+ break;
+
+ case eAudioVideo:
+ m_omx->SetClockState(cOmx::eClockStateWaitForAudioVideo);
+ break;
+ }
+ m_state = state;
}
int64_t cOmxDevice::GetSTC(void)
{
+ //dsyslog("S %llu", m_omx->GetSTC());
return m_omx->GetSTC();
}
@@ -1139,7 +263,22 @@ bool cOmxDevice::Flush(int TimeoutMs)
void cOmxDevice::Clear(void)
{
+ m_mutex->Lock();
dsyslog("rpihddevice: Clear()");
+
+ m_omx->SetClockScale(1.0f);
+ m_omx->SetMediaTime(0);
+
+ if (HasAudio())
+ {
+ m_audio->Reset();
+ m_omx->FlushAudio();
+ }
+
+ if (HasVideo())
+ m_omx->FlushVideo(false);
+
+ m_mutex->Unlock();
cDevice::Clear();
}
@@ -1152,7 +291,7 @@ bool cOmxDevice::Poll(cPoller &Poller, int TimeoutMs)
{
cTimeMs time;
time.Set();
- while (!m_omx->VideoBuffersAvailable())
+ while (!m_omx->PollVideoBuffers() || !m_audio->Poll())
{
if (time.Elapsed() >= TimeoutMs)
return false;
@@ -1168,26 +307,38 @@ void cOmxDevice::MakePrimaryDevice(bool On)
cDevice::MakePrimaryDevice(On);
}
-cOmxDevice::eVideoCodec cOmxDevice::GetVideoCodec(const uchar *data, int length)
+cVideoCodec::eCodec cOmxDevice::ParseVideoCodec(const uchar *data, int length)
{
+ if (!PesHasPts(data))
+ return cVideoCodec::eInvalid;
+
if (PesLength(data) - PesPayloadOffset(data) < 6)
- return eUnknown;
+ return cVideoCodec::eInvalid;
const uchar *p = data + PesPayloadOffset(data);
+ for (int i = 0; i < 5; i++)
+ {
+ // find start code prefix - should be right at the beginning of payload
+ if ((!p[i] && !p[i + 1] && p[i + 2] == 0x01))
+ {
+ if (p[i + 3] == 0xb3) // sequence header
+ return cVideoCodec::eMPEG2;
- if (p[0] != 0x00 || p[1] != 0x00)
- return eUnknown;
-
- if (p[2] == 0x01 && p[3] == 0xb3)
- return eMPEG2;
-
- else if ((p[2] == 0x01 && p[3] == 0x09 && p[4] == 0x10) ||
- (p[2] == 0x00 && p[3] == 0x01 && p[4] == 0x09 && p[5] == 0x10))
- return eH264;
-
- //esyslog("rpihddevice: invalid start sequence: "
- // "%02x %02x %02x %02x %02x %02x %02x %02x",
- // p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
-
- return eUnknown;
+ else if (p[i + 3] == 0x09) // slice
+ {
+ switch (p[i + 4] >> 5)
+ {
+ case 0: case 3: case 5: // I frame
+ return cVideoCodec::eH264;
+
+ case 1: case 4: case 6: // P frame
+ case 2: case 7: // B frame
+ default:
+ return cVideoCodec::eInvalid;
+ }
+ }
+ return cVideoCodec::eInvalid;
+ }
+ }
+ return cVideoCodec::eInvalid;
}
diff --git a/omxdevice.h b/omxdevice.h
index affe2a9..52a94a3 100644
--- a/omxdevice.h
+++ b/omxdevice.h
@@ -10,6 +10,8 @@
#include <vdr/device.h>
#include <vdr/thread.h>
+#include "types.h"
+
class cOmx;
class cAudioDecoder;
@@ -19,23 +21,13 @@ class cOmxDevice : cDevice
public:
enum eState {
- eStop,
- eStarting,
- ePlay
- };
-
- enum eVideoCodec {
- eMPEG2,
- eH264,
- eUnknown
+ eNone,
+ eStartingVideo,
+ eAudioOnly,
+ eVideoOnly,
+ eAudioVideo
};
- static const char* VideoCodecStr(eVideoCodec codec)
- {
- return (codec == eMPEG2) ? "MPEG2" :
- (codec == eH264) ? "H264" : "unknown";
- }
-
cOmxDevice(void (*onPrimaryDevice)(void));
virtual ~cOmxDevice();
@@ -74,17 +66,26 @@ private:
void (*m_onPrimaryDevice)(void);
- virtual eVideoCodec GetVideoCodec(const uchar *data, int length);
+ virtual cVideoCodec::eCodec ParseVideoCodec(const uchar *data, int length);
+ virtual void SetState(eState state);
+ virtual inline eState State() { return m_state; }
+
+ inline bool HasVideo() {
+ return m_state == eStartingVideo ||
+ m_state == eVideoOnly ||
+ m_state == eAudioVideo;
+ };
+
+ inline bool HasAudio() {
+ return m_state == eAudioOnly ||
+ m_state == eAudioVideo;
+ };
cOmx *m_omx;
cAudioDecoder *m_audio;
cMutex *m_mutex;
eState m_state;
-
- bool m_audioCodecReady;
- bool m_videoCodecReady;
-
uchar m_audioId;
};
diff --git a/ovgosd.c b/ovgosd.c
index 4513a98..f8de9d2 100644
--- a/ovgosd.c
+++ b/ovgosd.c
@@ -279,7 +279,6 @@ void cOvgOsd::Flush(void)
pm->ViewPort().Width() * sizeof(tColor), pm->Data());
delete pm;
}
-
return;
}
diff --git a/rpihddevice.c b/rpihddevice.c
index fb90c11..fa21315 100644
--- a/rpihddevice.c
+++ b/rpihddevice.c
@@ -10,8 +10,9 @@
#include "ovgosd.h"
#include "omxdevice.h"
#include "setup.h"
+#include "types.h"
-static const char *VERSION = "0.0.4";
+static const char *VERSION = "0.0.5";
static const char *DESCRIPTION = "HD output device for Raspberry Pi";
class cDummyDevice : cDevice
@@ -79,7 +80,8 @@ bool cPluginRpiHdDevice::Initialize(void)
if (!cRpiSetup::HwInit())
return false;
- if (!cRpiSetup::IsVideoCodecSupported(cOmxDevice::eMPEG2))
+ // test whether MPEG2 license is available
+ if (!cRpiSetup::IsVideoCodecSupported(cVideoCodec::eMPEG2))
esyslog("rpihddevice: WARNING: MPEG2 video decoder not enabled!");
m_device = new cOmxDevice(&OnPrimaryDevice);
@@ -111,4 +113,4 @@ bool cPluginRpiHdDevice::SetupParse(const char *Name, const char *Value)
return cRpiSetup::GetInstance()->Parse(Name, Value);
}
-VDRPLUGINCREATOR(cPluginRpiHdDevice); // Don't touch this!
+VDRPLUGINCREATOR(cPluginRpiHdDevice); // Don't touch this! okay.
diff --git a/setup.c b/setup.c
index 7ce7d71..a482f7d 100644
--- a/setup.c
+++ b/setup.c
@@ -44,8 +44,10 @@ public:
m_newAudioPort = *m_audioPort;
m_newPassthrough = *m_passthrough;
- Add(new cMenuEditStraItem(tr("Audio Port"), &m_newAudioPort, 2, audioport));
- Add(new cMenuEditBoolItem(tr("Digital Audio Pass-Through"), &m_newPassthrough));
+ Add(new cMenuEditStraItem(
+ tr("Audio Port"), &m_newAudioPort, 2, audioport));
+ Add(new cMenuEditBoolItem(
+ tr("Digital Audio Pass-Through"), &m_newPassthrough));
}
protected:
@@ -108,14 +110,20 @@ bool cRpiSetup::HwInit(void)
return true;
}
-bool cRpiSetup::IsAudioFormatSupported(cAudioDecoder::eCodec codec, int channels, int samplingRate)
+bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec,
+ int channels, int samplingRate)
{
+ // AAC and DTS pass-through currently not supported
+ if (codec == cAudioCodec::eAAC ||
+ codec == cAudioCodec::eDTS)
+ return false;
+
if (vc_tv_hdmi_audio_supported(
- codec == cAudioDecoder::eMPG ? EDID_AudioFormat_eMPEG1 :
- codec == cAudioDecoder::eAC3 ? EDID_AudioFormat_eAC3 :
- codec == cAudioDecoder::eEAC3 ? EDID_AudioFormat_eEAC3 :
- codec == cAudioDecoder::eAAC ? EDID_AudioFormat_eAAC :
- codec == cAudioDecoder::eDTS ? EDID_AudioFormat_eDTS :
+ codec == cAudioCodec::eMPG ? EDID_AudioFormat_eMPEG1 :
+ codec == cAudioCodec::eAC3 ? EDID_AudioFormat_eAC3 :
+ codec == cAudioCodec::eEAC3 ? EDID_AudioFormat_eEAC3 :
+ codec == cAudioCodec::eAAC ? EDID_AudioFormat_eAAC :
+ codec == cAudioCodec::eDTS ? EDID_AudioFormat_eDTS :
EDID_AudioFormat_ePCM, channels,
samplingRate == 32000 ? EDID_AudioSampleRate_e32KHz :
samplingRate == 44000 ? EDID_AudioSampleRate_e44KHz :
@@ -123,7 +131,8 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioDecoder::eCodec codec, int channels
samplingRate == 96000 ? EDID_AudioSampleRate_e96KHz :
samplingRate == 176000 ? EDID_AudioSampleRate_e176KHz :
samplingRate == 192000 ? EDID_AudioSampleRate_e192KHz :
- EDID_AudioSampleRate_e48KHz, EDID_AudioSampleSize_16bit) == 0)
+ EDID_AudioSampleRate_e48KHz,
+ EDID_AudioSampleSize_16bit) == 0)
return true;
return false;
diff --git a/setup.h b/setup.h
index 39735eb..761e14b 100644
--- a/setup.h
+++ b/setup.h
@@ -7,10 +7,8 @@
#ifndef SETUP_H
#define SETUP_H
-#include "audio.h"
-#include "omxdevice.h"
-
-class cMenuSetupPage;
+#include "omx.h"
+#include "types.h"
class cRpiSetup
{
@@ -19,16 +17,20 @@ public:
static bool HwInit(void);
- static cAudioDecoder::ePort GetAudioPort(void) {
- return (GetInstance()->m_audioPort) ? cAudioDecoder::eHDMI : cAudioDecoder::eLocal; }
- static bool IsAudioPassthrough(void) { return GetInstance()->m_passthrough; }
+ static cAudioPort::ePort GetAudioPort(void) {
+ return (GetInstance()->m_audioPort) ? cAudioPort::eHDMI : cAudioPort::eLocal; }
+
+ static bool IsAudioPassthrough(void) {
+ return GetInstance()->m_passthrough; }
+
static bool HasAudioSetupChanged(void);
- static bool IsAudioFormatSupported(cAudioDecoder::eCodec codec, int channels, int samplingRate);
+ static bool IsAudioFormatSupported(cAudioCodec::eCodec codec,
+ int channels, int samplingRate);
- static bool IsVideoCodecSupported(cOmxDevice::eVideoCodec codec) {
- return codec == cOmxDevice::eMPEG2 ? GetInstance()->m_mpeg2Enabled :
- codec == cOmxDevice::eH264 ? true : false;
+ static bool IsVideoCodecSupported(cVideoCodec::eCodec codec) {
+ return codec == cVideoCodec::eMPEG2 ? GetInstance()->m_mpeg2Enabled :
+ codec == cVideoCodec::eH264 ? true : false;
}
static int GetDisplaySize(int &width, int &height, double &aspect);
@@ -36,7 +38,7 @@ public:
static cRpiSetup* GetInstance(void);
static void DropInstance(void);
- cMenuSetupPage* GetSetupPage(void);
+ class cMenuSetupPage* GetSetupPage(void);
bool Parse(const char *name, const char *value);
private:
diff --git a/types.h b/types.h
new file mode 100644
index 0000000..f39fbbc
--- /dev/null
+++ b/types.h
@@ -0,0 +1,67 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+class cAudioCodec
+{
+public:
+
+ enum eCodec {
+ ePCM,
+ eMPG,
+ eAC3,
+ eEAC3,
+ eAAC,
+ eDTS,
+ eNumCodecs,
+ eInvalid
+ };
+
+ static const char* Str(eCodec codec) {
+ return (codec == ePCM) ? "PCM" :
+ (codec == eMPG) ? "MPEG" :
+ (codec == eAC3) ? "AC3" :
+ (codec == eEAC3) ? "E-AC3" :
+ (codec == eAAC) ? "AAC" :
+ (codec == eDTS) ? "DTS" : "unknown";
+ }
+};
+
+class cVideoCodec
+{
+public:
+
+ enum eCodec {
+ eMPEG2,
+ eH264,
+ eNumCodecs,
+ eInvalid
+ };
+
+ static const char* Str(eCodec codec) {
+ return (codec == eMPEG2) ? "MPEG2" :
+ (codec == eH264) ? "H264" : "unknown";
+ }
+};
+
+class cAudioPort
+{
+public:
+
+ enum ePort {
+ eLocal,
+ eHDMI
+ };
+
+ static const char* Str(ePort port) {
+ return (port == eLocal) ? "local" :
+ (port == eHDMI) ? "HDMI" : "unknown";
+ }
+};
+
+#endif