summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY16
-rw-r--r--audio.c729
-rw-r--r--audio.h37
-rw-r--r--omx.c165
-rw-r--r--omx.h28
-rw-r--r--omxdevice.c328
-rw-r--r--omxdevice.h44
-rw-r--r--ovgosd.c5
-rw-r--r--po/de_DE.po7
-rw-r--r--rpihddevice.c6
-rw-r--r--setup.c3
-rw-r--r--types.h4
12 files changed, 834 insertions, 538 deletions
diff --git a/HISTORY b/HISTORY
index 5faf55a..240d47b 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,22 @@
VDR Plugin 'rpihddevice' Revision History
-----------------------------------------
+2013-12-15: Version 0.0.6
+-------------------------
+- new:
+ - still picture
+ - trick speeds
+- fixed:
+ - reworked audio detection and decoding, fixed several issues
+ - reworked stream starting behavior, fixed audio-/video-only play back
+ - fixed several issues with unsupported video codec (e.g. without MPEG2 key)
+ - improved fast forward/reverse mode
+ - several minor bugfixes
+- missing:
+ - deinterlacer
+ - image grabbing
+ - video format/output options
+
2013-11-17: Version 0.0.5
-------------------------
- new:
diff --git a/audio.c b/audio.c
index 25ee90d..1cb3734 100644
--- a/audio.c
+++ b/audio.c
@@ -13,6 +13,8 @@
#include <string.h>
+#define AVPKT_BUFFER_SIZE (64 * 1024) /* 1 PES packet */
+
class cAudioParser
{
@@ -21,13 +23,40 @@ 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; }
+ AVPacket* Packet(void)
+ {
+ return &m_packet;
+ }
+
+ cAudioCodec::eCodec GetCodec(void)
+ {
+ if (!m_parsed)
+ Parse();
+ return m_codec;
+ }
+
+ unsigned int GetChannels(void)
+ {
+ if (!m_parsed)
+ Parse();
+ return m_channels;
+ }
+
+ bool Empty(void)
+ {
+ if (!m_parsed)
+ Parse();
+ return m_packet.size == 0;
+ }
int Init(void)
{
- return av_new_packet(&m_packet, 64 * 1024 /* 1 PES packet */);
+ if (!av_new_packet(&m_packet, AVPKT_BUFFER_SIZE))
+ {
+ Reset();
+ return 0;
+ }
+ return -1;
}
int DeInit(void)
@@ -38,42 +67,66 @@ public:
void Reset(void)
{
- m_packet.stream_index = 0;
+ m_codec = cAudioCodec::eInvalid;
+ m_channels = 0;
+ m_packet.size = 0;
+ m_size = 0;
+ m_parsed = false;
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)
+ if (m_size + length + FF_INPUT_BUFFER_PADDING_SIZE > AVPKT_BUFFER_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);
+ memcpy(m_packet.data + m_size, data, length);
+ m_size += length;
+ memset(m_packet.data + m_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
+ m_parsed = false;
return true;
}
void Shrink(unsigned int length)
{
- if (length < m_packet.stream_index)
+ if (length < m_size)
{
- 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);
+ memmove(m_packet.data, m_packet.data + length, m_size - length);
+ m_size -= length;
+ memset(m_packet.data + m_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
+ m_parsed = false;
}
else
Reset();
}
- cAudioCodec::eCodec Parse(unsigned int &offset)
+private:
+
+ // Check format of first audio packet in buffer. If format has been
+ // guessed, but packet is not yet complete, codec is set with a length
+ // of 0. Once the buffer contains either the exact amount of expected
+ // data or another valid packet start after the first frame, packet
+ // size is set to the first frame length.
+ // Valid packets are always moved to the buffer start, if no valid
+ // audio frame has been found, packet gets cleared.
+ //
+ // To do:
+ // - parse sampling rate to allow non-48kHz audio
+ // - consider codec change for next frame check
+
+ void Parse()
{
cAudioCodec::eCodec codec = cAudioCodec::eInvalid;
-
- while (Size() - offset >= 5)
+ int channels = 0;
+ int offset = 0;
+ int frameSize = 0;
+
+ while (m_size - offset >= 3)
{
- const uint8_t *p = Data() + offset;
- int n = Size() - offset;
- int r = 0;
+ const uint8_t *p = m_packet.data + offset;
+ int n = m_size - offset;
// 4 bytes 0xFFExxxxx MPEG audio
// 3 bytes 0x56Exxx AAC LATM audio
@@ -84,49 +137,73 @@ public:
if (FastMpegCheck(p))
{
- r = MpegCheck(p, n);
- codec = cAudioCodec::eMPG;
+ if (MpegCheck(p, n, frameSize))
+ {
+ codec = cAudioCodec::eMPG;
+ channels = 2;
+ }
+ break;
}
else if (FastAc3Check(p))
{
- r = Ac3Check(p, n);
- codec = cAudioCodec::eAC3;
-
- if (r > 0 && p[5] > (10 << 3))
- codec = cAudioCodec::eEAC3;
+ if (Ac3Check(p, n, frameSize, channels))
+ {
+ codec = cAudioCodec::eAC3;
+ if (n > 5 && p[5] > (10 << 3))
+ codec = cAudioCodec::eEAC3;
+ }
+ break;
}
else if (FastLatmCheck(p))
{
- r = LatmCheck(p, n);
- codec = cAudioCodec::eAAC;
+ if (LatmCheck(p, n, frameSize))
+ {
+ codec = cAudioCodec::eAAC;
+ channels = 2;
+ }
+ break;
}
else if (FastAdtsCheck(p))
{
- r = AdtsCheck(p, n);
- codec = cAudioCodec::eDTS;
+ if (AdtsCheck(p, n, frameSize, channels))
+ codec = cAudioCodec::eADTS;
+ break;
}
- if (r < 0) // need more bytes
- break;
+ ++offset;
+ }
- if (r > 0)
- return codec;
+ if (codec != cAudioCodec::eInvalid)
+ {
+ if (offset)
+ {
+ dsyslog("rpihddevice: audio packet shrinked by %d bytes", offset);
+ Shrink(offset);
+ }
- ++offset;
+ m_codec = codec;
+ m_channels = channels;
+ m_packet.size = frameSize;
}
- return cAudioCodec::eInvalid;
- }
+ else
+ Reset();
-private:
+ m_parsed = true;
+ }
- AVPacket m_packet;
+ AVPacket m_packet;
+ cAudioCodec::eCodec m_codec;
+ unsigned int m_channels;
+ unsigned int m_size;
+ bool m_parsed;
/* ------------------------------------------------------------------------- */
/* 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 MpegSampleRateTable[4];
+ static const uint16_t Ac3SampleRateTable[4];
static const uint16_t Ac3FrameSizeTable[38][3];
///
@@ -155,13 +232,6 @@ private:
///
/// 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
@@ -170,7 +240,7 @@ private:
/// 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 f 2x SampleRate index (41000, 48000, 32000, 0)
/// o g 1x Padding bit
/// o .. Doesn't care
///
@@ -180,24 +250,27 @@ private:
/// Layer II & III:
/// FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
///
- static int MpegCheck(const uint8_t *data, int size)
+ static bool MpegCheck(const uint8_t *data, int size, int &frame_size)
{
- int frame_size;
+ frame_size = 0;
+ if (size < 3)
+ return true;
+
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];
+ int sample_rate = MpegSampleRateTable[(data[2] >> 2) & 0x03];
if (!sample_rate)
- return 0;
+ return false;
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;
+ return false;
switch (layer)
{
@@ -212,14 +285,14 @@ private:
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;
+ if (size >= frame_size + 3 && !FastMpegCheck(data + frame_size))
+ return false;
- return 0;
+ if (frame_size > size)
+ frame_size = 0;
+
+ return true;
}
///
@@ -241,22 +314,16 @@ private:
///
/// 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
+ /// AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB CCDDDDDD EEEEEFFF GGGxxxxx
///
/// o a 16x Frame sync, always 0x0B77
/// o b 16x CRC 16
- /// o c 2x Sample rate
+ /// o c 2x Sample rate ( 48000, 44100, 32000, reserved )
/// o d 6x Frame size code
/// o e 5x Bit stream ID
/// o f 3x Bit stream mode
+ /// o g 3x Audio coding mode
///
/// o E-AC-3 Header
/// AAAAAAAA AAAAAAAA BBCCCDDD DDDDDDDD EEFFGGGH IIIII...
@@ -267,43 +334,76 @@ private:
/// o d 10x Frame size - 1 in words
/// o e 2x Frame size code
/// o f 2x Frame size code 2
+ /// o g 3x Channel mode
+ /// 0 h 1x LFE on
///
- static int Ac3Check(const uint8_t *p, int size)
+ static bool Ac3Check(const uint8_t *p, int size, int &frame_size, int &channels)
{
- int frame_size;
+ frame_size = 0;
+ if (size < 7)
+ return true;
- if (size < 5) // need 5 bytes to see if AC-3/E-AC-3
- return -5;
+ int acmod;
+ bool lfe;
+ int sample_rate; // for future use, E-AC3 t.b.d.
- if (p[5] > (10 << 3)) // E-AC-3
+ if (p[5] > (10 << 3)) // E-AC-3
{
if ((p[4] & 0xF0) == 0xF0) // invalid fscod fscod2
- return 0;
+ return false;
+
+ acmod = (p[4] & 0x0E) >> 1; // number of channels, LFE excluded
+ lfe = p[4] & 0x01;
frame_size = ((p[2] & 0x03) << 8) + p[3] + 1;
frame_size *= 2;
}
else // AC-3
{
+ sample_rate = Ac3SampleRateTable[(p[4] >> 6) & 0x03];
+
int fscod = p[4] >> 6;
if (fscod == 0x03) // invalid sample rate
- return 0;
+ return false;
int frmsizcod = p[4] & 0x3F;
if (frmsizcod > 37) // invalid frame size
- return 0;
+ return false;
+
+ acmod = p[6] >> 5; // number of channels, LFE excluded
+
+ int lfe_bptr = 51; // position of LFE bit in header for 2.0
+ if ((acmod & 0x01) && (acmod != 0x01))
+ lfe_bptr += 2; // skip center mix level
+ if (acmod & 0x04)
+ lfe_bptr += 2; // skip surround mix level
+ if (acmod == 0x02)
+ lfe_bptr += 2; // skip surround mode
+ lfe = (p[lfe_bptr / 8] & (1 << (7 - (lfe_bptr % 8))));
// 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;
+ channels =
+ acmod == 0x00 ? 2 : // Ch1, Ch2
+ acmod == 0x01 ? 1 : // C
+ acmod == 0x02 ? 2 : // L, R
+ acmod == 0x03 ? 3 : // L, C, R
+ acmod == 0x04 ? 3 : // L, R, S
+ acmod == 0x05 ? 4 : // L, C, R, S
+ acmod == 0x06 ? 4 : // L, R, RL, RR
+ acmod == 0x07 ? 5 : 0; // L, C, R, RL, RR
- return 0;
+ if (lfe) channels++;
+
+ if (size >= frame_size + 2 && !FastAc3Check(p + frame_size))
+ return false;
+
+ if (frame_size > size)
+ frame_size = 0;
+
+ return true;
}
///
@@ -325,27 +425,23 @@ private:
///
/// 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)
+ static bool LatmCheck(const uint8_t *p, int size, int &frame_size)
{
+ frame_size = 0;
+ if (size < 3)
+ return true;
+
// 13 bit frame size without header
- int frame_size = ((p[1] & 0x1F) << 8) + p[2];
+ frame_size = ((p[1] & 0x1F) << 8) + p[2];
frame_size += 3;
- if (frame_size + 2 > size)
- return -frame_size - 2;
+ if (size >= frame_size + 3 && !FastLatmCheck(p + frame_size))
+ return false;
- // check if after this frame a new AAC LATM frame starts
- if (FastLatmCheck(p + frame_size))
- return frame_size;
+ if (frame_size > size)
+ frame_size = 0;
- return 0;
+ return true;
}
///
@@ -369,13 +465,6 @@ private:
///
/// 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)
///
@@ -385,25 +474,40 @@ private:
/// o ..
/// o F*4 sampling frequency index (15 is invalid)
/// o ..
+ /// o H*3 MPEG-4 channel configuration
+ /// o ...
/// o M*13 frame length
///
- static int AdtsCheck(const uint8_t *p, int size)
+ static bool AdtsCheck(const uint8_t *p, int size, int &frame_size, int &channels)
{
- if (size < 6)
- return -6;
+ frame_size = 0;
+ if (size < 6)
+ return true;
- int frame_size = (p[3] & 0x03) << 11;
+ 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;
+ int ch_config = (p[2] & 0x01) << 7;
+ ch_config |= (p[3] & 0xC0) >> 6;
+ channels =
+ ch_config == 0x00 ? 0 : // defined in AOT specific config
+ ch_config == 0x01 ? 1 : // C
+ ch_config == 0x02 ? 2 : // L, R
+ ch_config == 0x03 ? 3 : // C, L, R
+ ch_config == 0x04 ? 4 : // C, L, R, RC
+ ch_config == 0x05 ? 5 : // C, L, R, RL, RR
+ ch_config == 0x06 ? 6 : // C, L, R, RL, RR, LFE
+ ch_config == 0x07 ? 8 : // C, L, R, SL, SR, RL, RR, LFE
+ 0;
+
+ if (size >= frame_size + 3 && !FastAdtsCheck(p + frame_size))
+ return false;
- // check if after this frame a new ADTS frame starts
- if (FastAdtsCheck(p + frame_size))
- return frame_size;
+ if (frame_size > size)
+ frame_size = 0;
- return 0;
+ return true;
}
};
@@ -429,7 +533,12 @@ const uint16_t cAudioParser::BitRateTable[2][3][16] =
///
/// MPEG sample rate table.
///
-const uint16_t cAudioParser::SampleRateTable[4] = { 44100, 48000, 32000, 0 };
+const uint16_t cAudioParser::MpegSampleRateTable[4] = { 44100, 48000, 32000, 0 };
+
+///
+/// AC-3 sample rate table.
+///
+const uint16_t cAudioParser::Ac3SampleRateTable[4] = { 48000, 44100, 32000, 0 };
///
/// Possible AC-3 frame sizes.
@@ -454,17 +563,12 @@ const uint16_t cAudioParser::Ac3FrameSizeTable[38][3] =
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_reset(false),
+ m_ready(false),
m_pts(0),
- m_frame(0),
m_mutex(new cMutex()),
- m_newData(new cCondWait()),
+ m_wait(new cCondWait()),
m_parser(new cAudioParser()),
m_omx(omx)
{
@@ -473,7 +577,7 @@ cAudioDecoder::cAudioDecoder(cOmx *omx) :
cAudioDecoder::~cAudioDecoder()
{
delete m_parser;
- delete m_newData;
+ delete m_wait;
delete m_mutex;
}
@@ -490,7 +594,7 @@ int cAudioDecoder::Init(void)
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);
+ m_codecs[cAudioCodec::eADTS].codec = avcodec_find_decoder(CODEC_ID_AAC);
for (int i = 0; i < cAudioCodec::eNumCodecs; i++)
{
@@ -513,14 +617,6 @@ int cAudioDecoder::Init(void)
}
}
- m_frame = avcodec_alloc_frame();
- if (!m_frame)
- {
- esyslog("rpihddevice: failed to allocate audio frame!");
- ret = -1;
- }
- avcodec_get_frame_defaults(m_frame);
-
if (ret < 0)
DeInit();
@@ -540,41 +636,42 @@ int cAudioDecoder::DeInit(void)
av_free(m_codecs[codec].context);
}
- av_free(m_frame);
m_parser->DeInit();
return 0;
}
-bool cAudioDecoder::WriteData(const unsigned char *buf, unsigned int length, uint64_t pts)
+int cAudioDecoder::WriteData(const unsigned char *buf, unsigned int length, uint64_t pts)
{
- m_mutex->Lock();
-
- bool ret = m_parser->Append(buf, length);
- if (ret)
+ int ret = 0;
+ if (m_ready)
{
- // set current pts as reference
- m_pts = pts;
-
- ProbeCodec();
- m_newData->Signal();
+ m_mutex->Lock();
+ if (m_parser->Append(buf, length))
+ {
+ m_pts = pts;
+ if (!m_parser->Empty())
+ {
+ m_ready = false;
+ m_wait->Signal();
+ }
+ ret = length;
+ }
+ m_mutex->Unlock();
}
-
- m_mutex->Unlock();
return ret;
}
void cAudioDecoder::Reset(void)
{
m_mutex->Lock();
- m_parser->Reset();
- m_codec = cAudioCodec::eInvalid;
- avcodec_get_frame_defaults(m_frame);
+ m_reset = true;
+ m_wait->Signal();
m_mutex->Unlock();
}
bool cAudioDecoder::Poll(void)
{
- return !m_parser->Size() && m_omx->PollAudioBuffers();
+ return m_ready && m_omx->PollAudioBuffers();
}
void cAudioDecoder::Action(void)
@@ -583,214 +680,232 @@ void cAudioDecoder::Action(void)
while (Running())
{
- if (m_parser->Size())
+ unsigned int channels = 0;
+ unsigned int outputChannels = 0;
+
+ bool bufferFull = false;
+
+ cAudioCodec::eCodec codec = cAudioCodec::eInvalid;
+ OMX_BUFFERHEADERTYPE *buf = 0;
+
+ AVFrame *frame = avcodec_alloc_frame();
+ if (!frame)
{
- m_mutex->Lock();
- if (m_outputFormatChanged)
- {
- m_outputFormatChanged = false;
- m_omx->SetupAudioRender(
- m_outputFormat, m_channels,
- m_samplingRate, m_outputPort);
- }
+ esyslog("rpihddevice: failed to allocate audio frame!");
+ return;
+ }
- OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(m_pts);
- if (buf)
+ m_reset = false;
+ m_ready = true;
+
+ while (!m_reset)
+ {
+ // check for data if no decoded samples are pending
+ if (!m_parser->Empty() && !frame->nb_samples)
{
- while (DecodeFrame())
- buf->nFilledLen += ReadFrame(
- buf->pBuffer + buf->nFilledLen,
- buf->nAllocLen - buf->nFilledLen);
+ if (codec != m_parser->GetCodec() ||
+ channels != m_parser->GetChannels())
+ {
+ // to change codec config, we need to empty buffer first
+ if (buf)
+ bufferFull = true;
+ else
+ {
+ codec = m_parser->GetCodec();
+ channels = m_parser->GetChannels();
- if (!m_omx->EmptyAudioBuffer(buf))
- esyslog("rpihddevice: failed to empty audio buffer!");
+ outputChannels = channels;
+ SetCodec(codec, outputChannels);
+ }
+ }
}
- else
+
+ // if codec has been configured but we don't have a buffer, get one
+ while (codec != cAudioCodec::eInvalid && !buf && !m_reset)
{
- esyslog("rpihddevice: failed to get audio buffer!");
- cCondWait::SleepMs(5);
+ buf = m_omx->GetAudioBuffer(m_pts);
+ if (buf)
+ m_pts = 0;
+ else
+ m_wait->Wait(10);
}
- m_mutex->Unlock();
- }
- else
- m_newData->Wait(50);
- }
- dsyslog("rpihddevice: cAudioDecoder() thread ended");
-}
-unsigned int cAudioDecoder::DecodeFrame()
-{
- unsigned int ret = 0;
+ // we have a non-full buffer and data to encode / copy
+ if (buf && !bufferFull && !m_parser->Empty())
+ {
+ int copied = 0;
+ if (m_passthrough)
+ {
+ // for pass-through directly copy AV packet to buffer
+ if (m_parser->Packet()->size <= buf->nAllocLen - buf->nFilledLen)
+ {
+ m_mutex->Lock();
- if (m_passthrough)
- ret = m_parser->Size();
+ memcpy(buf->pBuffer + buf->nFilledLen,
+ m_parser->Packet()->data, m_parser->Packet()->size);
+ buf->nFilledLen += m_parser->Packet()->size;
+ m_parser->Shrink(m_parser->Packet()->size);
- else if (m_parser->Size())
- {
- int frame = 0;
- int len = avcodec_decode_audio4(m_codecs[m_codec].context,
- m_frame, &frame, m_parser->Packet());
+ m_mutex->Unlock();
+ }
+ else
+ if (m_parser->Packet()->size > buf->nAllocLen)
+ {
+ esyslog("rpihddevice: encoded audio frame too big!");
+ m_reset = true;
+ break;
+ }
+ else
+ bufferFull = true;
+ }
+ else
+ {
+ // decode frame if we do not pass-through
+ m_mutex->Lock();
- // 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;
-}
+ int gotFrame = 0;
+ int len = avcodec_decode_audio4(m_codecs[codec].context,
+ frame, &gotFrame, m_parser->Packet());
-unsigned int cAudioDecoder::ReadFrame(unsigned char *buf, unsigned int bufsize)
-{
- unsigned int ret = 0;
+ if (len > 0)
+ m_parser->Shrink(len);
- if (m_passthrough)
- {
- // for pass-through directly read from AV packet
- if (m_parser->Size() > bufsize)
- ret = bufsize;
- else
- ret = m_parser->Size();
+ m_mutex->Unlock();
- memcpy(buf, m_parser->Data(), ret);
- m_parser->Shrink(ret);
- }
- else
- {
- if (m_frame->nb_samples > 0)
- {
- 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 (ret > bufsize)
- {
- esyslog("rpihddevice: decoded audio frame too big!");
- ret = 0;
+ if (len < 0)
+ {
+ esyslog("rpihddevice: failed to decode audio frame!");
+ m_reset = true;
+ break;
+ }
+ }
}
- else
+
+ // we have decoded samples we need to copy to buffer
+ if (buf && !bufferFull && frame->nb_samples > 0)
{
- 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*)buf;
+ int length = av_samples_get_buffer_size(NULL,
+ outputChannels == 6 ? 8 : outputChannels, frame->nb_samples,
+ m_codecs[codec].context->sample_fmt, 1);
- for (int i = 0; i < m_frame->nb_samples; i++)
+ if (length <= buf->nAllocLen - buf->nFilledLen)
+ {
+ if (outputChannels == 6)
{
- *dst++ = *src++; // LF & RF
- *dst++ = *src++; // CF & LFE
- *dst++ = *src++; // LR & RR
- *dst++ = 0; // empty channels
+ // interleaved copy to fit 5.1 data into 8 channels
+ int32_t* src = (int32_t*)frame->data[0];
+ int32_t* dst = (int32_t*)buf->pBuffer + buf->nFilledLen;
+
+ for (int i = 0; i < frame->nb_samples; i++)
+ {
+ *dst++ = *src++; // LF & RF
+ *dst++ = *src++; // CF & LFE
+ *dst++ = *src++; // LR & RR
+ *dst++ = 0; // empty channels
+ }
}
+ else
+ memcpy(buf->pBuffer + buf->nFilledLen, frame->data[0], length);
+
+ buf->nFilledLen += length;
+ frame->nb_samples = 0;
}
else
- memcpy(buf, m_frame->data[0], ret);
+ {
+ if (length > buf->nAllocLen)
+ {
+ esyslog("rpihddevice: decoded audio frame too big!");
+ m_reset = true;
+ break;
+ }
+ else
+ bufferFull = true;
+ }
}
- }
- }
- 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());
+ // check if no decoded samples are pending and parser is empty
+ if (!frame->nb_samples && m_parser->Empty())
+ {
+ // if no more data but buffer with data -> end of PES packet
+ if (buf && buf->nFilledLen > 0)
+ bufferFull = true;
+ else
+ if (m_ready)
+ m_wait->Wait(50);
+ }
- if (len > 0 && frame)
+ // we have a buffer to empty
+ if (buf && bufferFull)
{
- SetCodec(codec);
- ret = true;
+ if (m_omx->EmptyAudioBuffer(buf))
+ {
+ bufferFull = false;
+ buf = 0;
+
+ // if parser is empty, get new data
+ if (m_parser->Empty())
+ m_ready = true;
+ }
+ else
+ {
+ esyslog("rpihddevice: failed to empty audio buffer!");
+ m_reset = true;
+ break;
+ }
}
}
+
+ dsyslog("reset");
+ if (buf && m_omx->EmptyAudioBuffer(buf))
+ buf = 0;
+
+ av_free(frame);
+ m_parser->Reset();
}
- return ret;
+ dsyslog("rpihddevice: cAudioDecoder() thread ended");
}
-void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec)
+void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels)
{
- if (codec != cAudioCodec::eInvalid)
+ if (codec != cAudioCodec::eInvalid && channels > 0)
{
- if (m_codec == cAudioCodec::eInvalid)
- m_outputFormatChanged = true;
+ dsyslog("rpihddevice: set audio codec to %dch %s",
+ channels, cAudioCodec::Str(codec));
- m_codec = codec;
- m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
- m_codecs[m_codec].context->request_channels = 0;
+ m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE;
+ m_codecs[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))
+ cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, channels, 48000))
{
outputPort = cAudioPort::eHDMI;
if (cRpiSetup::IsAudioPassthrough() &&
- cRpiSetup::IsAudioFormatSupported(m_codec,
- m_codecs[m_codec].context->channels,
- m_codecs[m_codec].context->sample_rate))
+ cRpiSetup::IsAudioFormatSupported(codec, channels, 48000))
{
m_passthrough = true;
- outputFormat = m_codec;
+ outputFormat = codec;
}
}
else
{
- m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
- m_codecs[m_codec].context->request_channels = 2;
+ m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
+ m_codecs[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)" : "");
+ // if 2ch PCM audio on HDMI is supported
+ if (cRpiSetup::GetAudioPort() == cAudioPort::eHDMI &&
+ cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, 2, 48000))
+ outputPort = cAudioPort::eHDMI;
}
+
+ m_omx->SetupAudioRender(outputFormat, channels, outputPort, 48000);
+ dsyslog("rpihddevice: set %s audio output format to %dch %s%s",
+ cAudioPort::Str(outputPort), channels, cAudioCodec::Str(outputFormat),
+ m_passthrough ? " (pass-through)" : "");
}
}
diff --git a/audio.h b/audio.h
index 327d312..dd1f9f5 100644
--- a/audio.h
+++ b/audio.h
@@ -7,8 +7,7 @@
#ifndef AUDIO_H
#define AUDIO_H
-extern "C"
-{
+extern "C" {
#include <libavcodec/avcodec.h>
}
@@ -30,7 +29,7 @@ public:
virtual int Init(void);
virtual int DeInit(void);
- virtual bool WriteData(const unsigned char *buf, unsigned int length, uint64_t pts = 0);
+ virtual int WriteData(const unsigned char *buf, unsigned int length, uint64_t pts = 0);
virtual bool Poll(void);
virtual void Reset(void);
@@ -38,12 +37,7 @@ public:
protected:
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);
+ void SetCodec(cAudioCodec::eCodec codec, unsigned int &channels);
struct Codec
{
@@ -53,21 +47,16 @@ protected:
private:
- 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;
+ Codec m_codecs[cAudioCodec::eNumCodecs];
+ bool m_passthrough;
+ bool m_reset;
+ bool m_ready;
+ uint64_t m_pts;
+
+ cMutex *m_mutex;
+ cCondWait *m_wait;
+ cAudioParser *m_parser;
+ cOmx *m_omx;
};
#endif
diff --git a/omx.c b/omx.c
index 7c0ddcf..fd6e58d 100644
--- a/omx.c
+++ b/omx.c
@@ -9,8 +9,7 @@
#include <vdr/tools.h>
#include <vdr/thread.h>
-extern "C"
-{
+extern "C" {
#include "ilclient.h"
}
@@ -181,28 +180,28 @@ void cOmx::OnBufferEmpty(void *instance, COMPONENT_T *comp)
omx->HandleBufferEmpty(comp);
}
-void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, unsigned int data)
+void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data)
{
cOmx* omx = static_cast <cOmx*> (instance);
omx->HandlePortSettingsChanged(data);
}
-void cOmx::OnEndOfStream(void *instance, COMPONENT_T *comp, unsigned int data)
+void cOmx::OnEndOfStream(void *instance, COMPONENT_T *comp, OMX_U32 data)
{
cOmx* omx = static_cast <cOmx*> (instance);
omx->HandleEndOfStream(data);
}
-void cOmx::OnError(void *instance, COMPONENT_T *comp, unsigned int data)
+void cOmx::OnError(void *instance, COMPONENT_T *comp, OMX_U32 data)
{
- if (data != OMX_ErrorSameState)
+ if ((OMX_S32)data != OMX_ErrorSameState)
esyslog("rpihddevice: OmxError(%s)", errStr((int)data));
}
cOmx::cOmx() :
m_mutex(new cMutex()),
- m_setVideoStartTime(true),
- m_setAudioStartTime(true),
+ m_setAudioStartTime(false),
+ m_setVideoStartTime(false),
m_setVideoDiscontinuity(false),
m_freeAudioBuffers(0),
m_freeVideoBuffers(0),
@@ -281,6 +280,7 @@ int cOmx::Init(void)
// 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;
@@ -298,8 +298,8 @@ int cOmx::Init(void)
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);
@@ -324,12 +324,7 @@ int cOmx::Init(void)
// 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");
+ SetClockState(cOmx::eClockStateRun);
return 0;
}
@@ -432,24 +427,20 @@ void cOmx::SetClockState(eClockState clockState)
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 clock is already running, we need to stop it first
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!");
}
+ cstate.nWaitMask = 0;
+
switch (clockState)
{
case eClockStateRun:
@@ -462,27 +453,28 @@ void cOmx::SetClockState(eClockState clockState)
case eClockStateWaitForVideo:
cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT0;
m_setVideoStartTime = true;
- SetClockReference(eClockRefVideo);
+ cstate.nWaitMask = OMX_CLOCKPORT0;
break;
case eClockStateWaitForAudio:
cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT1;
m_setAudioStartTime = true;
- SetClockReference(eClockRefAudio);
+ cstate.nWaitMask = OMX_CLOCKPORT1;
break;
case eClockStateWaitForAudioVideo:
cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
- cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1;
- m_setVideoStartTime = true;
m_setAudioStartTime = true;
- SetClockReference(eClockRefAudio);
+ m_setVideoStartTime = true;
+ cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1;
break;
}
+ if (cstate.eState == OMX_TIME_ClockStateWaitingForStartTime)
+ // 200ms pre roll, value taken from omxplayer
+ cstate.nOffset = ToOmxTicks(-1000LL * 400);
+
if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
esyslog("rpihddevice: failed to set clock state!");
@@ -499,23 +491,32 @@ void cOmx::SetClockScale(float scale)
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)
+void cOmx::SetStartTime(uint64_t pts)
{
OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
OMX_INIT_STRUCT(timeStamp);
- timeStamp.nPortIndex = m_clockReference == eClockRefAudio ? 81 : 80;
+ timeStamp.nPortIndex = 80; //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!",
+ esyslog("rpihddevice: failed to set current start time!");
+}
+
+void cOmx::SetCurrentReferenceTime(uint64_t pts)
+{
+ OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
+ OMX_INIT_STRUCT(timeStamp);
+ timeStamp.nPortIndex = 80; //OMX_ALL; //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, &timeStamp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set current %s reference time!",
m_clockReference == eClockRefAudio ? "audio" : "video");
}
@@ -538,6 +539,8 @@ unsigned int cOmx::GetMediaTime(void)
void cOmx::SetClockReference(eClockReference clockReference)
{
+ m_mutex->Lock();
+
m_clockReference = clockReference;
OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock;
@@ -548,6 +551,11 @@ void cOmx::SetClockReference(eClockReference clockReference)
if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]),
OMX_IndexConfigTimeActiveRefClock, &refClock) != OMX_ErrorNone)
esyslog("rpihddevice: failed set active clock reference!");
+ else
+ dsyslog("rpihddevice: set active clock reference to %s",
+ m_clockReference == eClockRefAudio ? "audio" : "video");
+
+ m_mutex->Unlock();
}
void cOmx::SetVolume(int vol)
@@ -583,8 +591,10 @@ void cOmx::SendEos(void)
#endif
}
-void cOmx::Stop(void)
+void cOmx::StopVideo(void)
{
+ dsyslog("rpihddevice: StopVideo()");
+
// put video decoder into idle
ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle);
@@ -600,16 +610,43 @@ void cOmx::Stop(void)
ilclient_disable_tunnel(&m_tun[eVideoSchedulerToVideoRender]);
ilclient_change_component_state(m_comp[eVideoRender], OMX_StateIdle);
+ // disable port buffers and allow video decoder to reconfig
+ ilclient_disable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL);
+}
+
+void cOmx::StopAudio(void)
+{
// put audio render onto idle
ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
- ilclient_disable_tunnel(&m_tun[eClockToAudioRender]);
+// 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);
+}
+
+void cOmx::SetVideoDataUnitType(eDataUnitType dataUnitType)
+{
+ OMX_PARAM_DATAUNITTYPE dataUnit;
+ OMX_INIT_STRUCT(dataUnit);
+ dataUnit.nPortIndex = 130;
+
+ dataUnit.eEncapsulationType = OMX_DataEncapsulationElementaryStream;
+ dataUnit.eUnitType = dataUnitType == eCodedPicture ?
+ OMX_DataUnitCodedPicture : OMX_DataUnitArbitraryStreamSection;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
+ OMX_IndexParamBrcmDataUnit, &dataUnit) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder data unit type!");
- SetClockState(eClockStateStop);
+}
+
+void cOmx::SetVideoErrorConcealment(bool startWithValidFrame)
+{
+ OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ectype;
+ OMX_INIT_STRUCT(ectype);
+ ectype.bStartWithValidFrame = startWithValidFrame ? OMX_TRUE : 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");
}
void cOmx::FlushAudio(void)
@@ -618,8 +655,6 @@ void cOmx::FlushAudio(void)
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)
@@ -634,12 +669,16 @@ void cOmx::FlushVideo(bool flushRender)
if (flushRender)
ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1);
- m_setVideoStartTime = true;
m_setVideoDiscontinuity = true;
}
-int cOmx::SetVideoCodec(cVideoCodec::eCodec codec)
+int cOmx::SetVideoCodec(cVideoCodec::eCodec codec, eDataUnitType dataUnit)
{
+ dsyslog("rpihddevice: SetVideoCodec()");
+
+ if (ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle) != 0)
+ esyslog("rpihddevice: failed to set video decoder to idle state!");
+
// configure video decoder
OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat;
OMX_INIT_STRUCT(videoFormat);
@@ -653,6 +692,11 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec)
OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone)
esyslog("rpihddevice: failed to set video decoder parameters!");
+ // start with valid frames only if codec is MPEG2
+ SetVideoErrorConcealment(codec == cVideoCodec::eMPEG2);
+ SetVideoDataUnitType(dataUnit);
+ //SetVideoDecoderExtraBuffers(3);
+
if (ilclient_enable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL) != 0)
esyslog("rpihddevice: failed to enable port buffer on video decoder!");
@@ -666,8 +710,19 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec)
return 0;
}
-int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, int samplingRate,
- cAudioPort::ePort audioPort)
+void cOmx::SetVideoDecoderExtraBuffers(int extraBuffers)
+{
+ OMX_PARAM_U32TYPE u32;
+ OMX_INIT_STRUCT(u32);
+ u32.nPortIndex = 130;
+ u32.nU32 = extraBuffers;
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]),
+ OMX_IndexParamBrcmExtraBuffers, &u32) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder extra buffers!");
+}
+
+int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels,
+ cAudioPort::ePort audioPort, int samplingRate)
{
// put audio render onto idle
ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1);
@@ -691,7 +746,7 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, int s
outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP :
outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP :
outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC :
- outputFormat == cAudioCodec::eDTS ? OMX_AUDIO_CodingDTS :
+ outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_CodingDTS :
OMX_AUDIO_CodingAutoDetect;
if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]),
@@ -741,7 +796,7 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, int s
esyslog("rpihddevice: failed to set audio render aac parameters!");
break;
- case cAudioCodec::eDTS:
+ case cAudioCodec::eADTS:
OMX_AUDIO_PARAM_DTSTYPE dts;
OMX_INIT_STRUCT(aac);
dts.nPortIndex = 100;
@@ -812,8 +867,8 @@ OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts)
if (buf)
{
cOmx::PtsToTicks(pts, buf->nTimeStamp);
- buf->nFlags = m_setAudioStartTime ? OMX_BUFFERFLAG_STARTTIME : 0;
- buf->nFlags |= pts ? 0 : OMX_BUFFERFLAG_TIME_UNKNOWN;
+ buf->nFlags = pts ? 0 : OMX_BUFFERFLAG_TIME_UNKNOWN;
+ buf->nFlags |= m_setAudioStartTime ? OMX_BUFFERFLAG_STARTTIME : 0;
m_setAudioStartTime = false;
m_freeAudioBuffers--;
@@ -834,8 +889,8 @@ OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts)
if (buf)
{
cOmx::PtsToTicks(pts, buf->nTimeStamp);
- buf->nFlags = m_setVideoStartTime ?
- OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN;
+ buf->nFlags = pts ? 0 : OMX_BUFFERFLAG_TIME_UNKNOWN;
+ buf->nFlags |= m_setVideoStartTime ? OMX_BUFFERFLAG_STARTTIME : 0;
buf->nFlags |= m_setVideoDiscontinuity ? OMX_BUFFERFLAG_DISCONTINUITY : 0;
m_setVideoStartTime = false;
diff --git a/omx.h b/omx.h
index 08c56b6..10efdce 100644
--- a/omx.h
+++ b/omx.h
@@ -44,7 +44,8 @@ public:
void SetClockState(eClockState clockState);
void SetClockScale(float scale);
- void SetMediaTime(uint64_t pts);
+ void SetStartTime(uint64_t pts);
+ void SetCurrentReferenceTime(uint64_t pts);
unsigned int GetMediaTime(void);
enum eClockReference {
@@ -55,14 +56,25 @@ public:
void SetClockReference(eClockReference clockReference);
void SetVolume(int vol);
void SendEos(void);
- void Stop(void);
+ void StopVideo(void);
+ void StopAudio(void);
+
+ enum eDataUnitType {
+ eCodedPicture,
+ eArbitraryStreamSection
+ };
+
+ void SetVideoDataUnitType(eDataUnitType dataUnitType);
+ void SetVideoErrorConcealment(bool startWithValidFrame);
+ void SetVideoDecoderExtraBuffers(int extraBuffers);
void FlushAudio(void);
void FlushVideo(bool flushRender = false);
- int SetVideoCodec(cVideoCodec::eCodec codec);
+ int SetVideoCodec(cVideoCodec::eCodec codec,
+ eDataUnitType dataUnit = eArbitraryStreamSection);
int SetupAudioRender(cAudioCodec::eCodec outputFormat,
- int channels, int samplingRate, cAudioPort::ePort audioPort);
+ int channels, cAudioPort::ePort audioPort, int samplingRate = 0);
OMX_BUFFERHEADERTYPE* GetAudioBuffer(uint64_t pts = 0);
OMX_BUFFERHEADERTYPE* GetVideoBuffer(uint64_t pts = 0);
@@ -99,8 +111,8 @@ private:
cMutex *m_mutex;
- bool m_setVideoStartTime;
bool m_setAudioStartTime;
+ bool m_setVideoStartTime;
bool m_setVideoDiscontinuity;
int m_freeAudioBuffers;
@@ -113,9 +125,9 @@ private:
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);
+ static void OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data);
+ static void OnEndOfStream(void *instance, COMPONENT_T *comp, OMX_U32 data);
+ static void OnError(void *instance, COMPONENT_T *comp, OMX_U32 data);
};
diff --git a/omxdevice.c b/omxdevice.c
index d1893e5..24da8bd 100644
--- a/omxdevice.c
+++ b/omxdevice.c
@@ -11,6 +11,7 @@
#include <vdr/remux.h>
#include <vdr/tools.h>
+#include <vdr/skins.h>
#include <string.h>
@@ -20,10 +21,15 @@ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) :
m_omx(new cOmx()),
m_audio(new cAudioDecoder(m_omx)),
m_mutex(new cMutex()),
- m_state(eNone),
- m_audioId(0)
-{
-}
+ m_videoCodec(cVideoCodec::eInvalid),
+ m_hasVideo(false),
+ m_hasAudio(false),
+ m_skipAudio(false),
+ m_playDirection(0),
+ m_trickRequest(0),
+ m_audioPts(0),
+ m_videoPts(0)
+{ }
cOmxDevice::~cOmxDevice()
{
@@ -71,14 +77,12 @@ bool cOmxDevice::CanReplay(void) const
{
dsyslog("rpihddevice: CanReplay");
// video codec de-initialization done
- return (m_state == eNone);
+ return true; //(m_state == eNone);
}
bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
{
- // in case we were in some trick mode
- m_omx->SetClockScale(1.0f);
-
+ m_mutex->Lock();
dsyslog("rpihddevice: SetPlayMode(%s)",
PlayMode == pmNone ? "none" :
PlayMode == pmAudioVideo ? "Audio/Video" :
@@ -86,20 +90,19 @@ bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
PlayMode == pmAudioOnlyBlack ? "Audio only, black" :
PlayMode == pmVideoOnly ? "Video only" :
"unsupported");
+
+ // Stop audio / video if play mode is set to pmNone. Start
+ // is triggered once a packet is going to be played, since
+ // we don't know what kind of stream we'll get (audio-only,
+ // video-only or both) after SetPlayMode() - VDR will always
+ // pass pmAudioVideo as argument.
+
switch (PlayMode)
{
case pmNone:
- m_mutex->Lock();
- if (HasVideo())
- m_omx->FlushVideo(true);
- if (HasAudio())
- {
- m_audio->Reset();
- m_omx->FlushAudio();
- }
- m_omx->Stop();
- SetState(eNone);
- m_mutex->Unlock();
+ ResetAudioVideo(true);
+ m_omx->StopVideo();
+ m_videoCodec = cVideoCodec::eInvalid;
break;
case pmAudioVideo:
@@ -107,95 +110,143 @@ bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
case pmAudioOnlyBlack:
case pmVideoOnly:
break;
+
+ default:
+ break;
}
+ m_mutex->Unlock();
return true;
}
+void cOmxDevice::StillPicture(const uchar *Data, int Length)
+{
+ if (Data[0] == 0x47)
+ cDevice::StillPicture(Data, Length);
+ else
+ {
+ // to get a picture displayed, PlayVideo() needs to be called
+ // twice for MPEG2 and 6x for H264... ?
+ for (int i = 0; i < (m_videoCodec == cVideoCodec::eMPEG2 ? 2 : 6); i++)
+ PlayVideo(Data, Length, true);
+ }
+}
+
int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
{
+ if (m_skipAudio)
+ return Length;
+
m_mutex->Lock();
- int ret = Length;
+ int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
- // if first packet is audio, we assume audio only
- if (State() == eNone)
- SetState(eAudioOnly);
+ if (!m_hasAudio)
+ {
+ m_omx->SetClockReference(cOmx::eClockRefAudio);
+ m_hasAudio = true;
+ }
- if (State() == eAudioOnly || State() == eAudioVideo)
+ // keep track of direction in case of trick speed
+ if (m_trickRequest && pts)
{
- if (m_audio->Poll())
- {
- if (m_audioId != Id)
- {
- m_audioId = Id;
- m_audio->Reset();
- }
+ if (m_audioPts)
+ PtsTracker(PtsDiff(m_audioPts, pts));
- //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
- ret = 0;
+ m_audioPts = pts;
}
+ int ret = m_audio->WriteData(Data + PesPayloadOffset(Data),
+ Length - PesPayloadOffset(Data), pts);
+
m_mutex->Unlock();
return ret;
}
-int cOmxDevice::PlayVideo(const uchar *Data, int Length)
+int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame)
{
m_mutex->Lock();
int ret = Length;
- if (State() == eNone)
- SetState(eStartingVideo);
+ int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
+ cVideoCodec::eCodec codec = ParseVideoCodec(Data, Length);
+
+ // video restart after Clear() with same codec
+ bool videoRestart = (!m_hasVideo && codec == m_videoCodec &&
+ cRpiSetup::IsVideoCodecSupported(codec));
- if (State() == eStartingVideo)
+ // video restart after SetPlayMode() or codec changed
+ if (codec != cVideoCodec::eInvalid && codec != m_videoCodec)
{
- cVideoCodec::eCodec codec = ParseVideoCodec(Data, Length);
- if (codec != cVideoCodec::eInvalid)
+ m_videoCodec = codec;
+
+ if (m_hasVideo)
{
- 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));
- }
+ m_omx->StopVideo();
+ m_hasVideo = false;
+ }
+
+ if (cRpiSetup::IsVideoCodecSupported(codec))
+ {
+ videoRestart = true;
+ m_omx->SetVideoCodec(codec, cOmx::eArbitraryStreamSection);
+ dsyslog("rpihddevice: set video codec to %s",
+ cVideoCodec::Str(codec));
+ }
+ else
+ Skins.QueueMessage(mtError, tr("video format not supported!"));
+ }
+
+ if (videoRestart)
+ {
+ m_hasVideo = true;
+
+ if (!m_hasAudio)
+ {
+ m_omx->SetClockReference(cOmx::eClockRefVideo);
+ m_omx->SetCurrentReferenceTime(0);
+ m_omx->SetClockState(cOmx::eClockStateWaitForVideo);
}
}
- if (State() == eVideoOnly || State() == eAudioVideo)
+ // keep track of direction in case of trick speed
+ if (m_trickRequest && pts)
{
- int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
- OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts);
- if (buf)
+ if (m_videoPts)
+ PtsTracker(PtsDiff(m_videoPts, pts));
+
+ m_videoPts = pts;
+ }
+
+ if (m_hasVideo)
+ {
+ while (Length)
{
- //dsyslog("V %llu", PesGetPts(Data));
const uchar *payload = Data + PesPayloadOffset(Data);
- int length = PesLength(Data) - PesPayloadOffset(Data);
- if (length <= buf->nAllocLen)
+ unsigned int length = PesLength(Data) - PesPayloadOffset(Data);
+
+ OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts);
+ if (buf)
{
- memcpy(buf->pBuffer, payload, length);
buf->nFilledLen = length;
+ memcpy(buf->pBuffer, payload, length);
+
+ if (singleFrame && Length == PesLength(Data))
+ buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
+
+ if (!m_omx->EmptyVideoBuffer(buf))
+ {
+ ret = 0;
+ esyslog("rpihddevice: failed to pass buffer to video decoder!");
+ break;
+ }
}
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!");
+ break;
}
+ Length -= PesLength(Data);
+ Data += PesLength(Data);
}
}
@@ -203,85 +254,133 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length)
return ret;
}
-void cOmxDevice::SetState(eState state)
-{
- switch (state)
- {
- case eNone:
- m_omx->SetClockState(cOmx::eClockStateStop);
- break;
-
- case eStartingVideo:
- break;
-
- 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();
}
void cOmxDevice::Play(void)
{
dsyslog("rpihddevice: Play()");
+
+ Clear();
+ return;
+
+ m_mutex->Lock();
+
+ m_skipAudio = false;
m_omx->SetClockScale(1.0f);
+ m_omx->SetClockReference(m_hasAudio ?
+ cOmx::eClockRefAudio : cOmx::eClockRefVideo);
+
+ m_mutex->Unlock();
cDevice::Play();
}
void cOmxDevice::Freeze(void)
{
- dsyslog("rpihddevice: Freeze()");
+ m_mutex->Lock();
m_omx->SetClockScale(0.0f);
+ m_mutex->Unlock();
cDevice::Freeze();
}
void cOmxDevice::TrickSpeed(int Speed)
{
- dsyslog("rpihddevice: TrickSpeed(%d)", Speed);
+ m_mutex->Lock();
+ m_audioPts = 0;
+ m_videoPts = 0;
+ m_playDirection = 0;
+
+ // play direction is ambiguous for fast modes, start PTS tracking
+ if (Speed == 1 || Speed == 3 || Speed == 6)
+ m_trickRequest = Speed;
+ else
+ ApplyTrickSpeed(Speed);
+
+ m_mutex->Unlock();
+}
+
+void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool reverse)
+{
+ float scale =
+ // slow forward
+ trickSpeed == 8 ? 0.125f :
+ trickSpeed == 4 ? 0.25f :
+ trickSpeed == 2 ? 0.5f :
+
+ // fast for-/backward
+ trickSpeed == 6 ? (reverse ? -2.0f : 2.0f) :
+ trickSpeed == 3 ? (reverse ? -4.0f : 4.0f) :
+ trickSpeed == 1 ? (reverse ? -12.0f : 12.0f) :
+
+ // slow backward
+ trickSpeed == 63 ? -0.125f :
+ trickSpeed == 48 ? -0.25f :
+ trickSpeed == 24 ? -0.5f : 1.0f;
+
+ m_omx->SetClockScale(scale);
+ m_omx->SetClockReference(cOmx::eClockRefVideo);
+
+ m_trickRequest = 0;
+ m_skipAudio = true;
+
+ dsyslog("rpihddevice: ApplyTrickSpeed(%.3f, %sward)",
+ scale, reverse ? "back" : "for");
+
+}
+
+void cOmxDevice::PtsTracker(int64_t ptsDiff)
+{
+ if (ptsDiff < 0)
+ --m_playDirection;
+ else if (ptsDiff > 0)
+ m_playDirection += 2;
+
+ if (m_playDirection < -2 || m_playDirection > 3)
+ ApplyTrickSpeed(m_trickRequest, m_playDirection < 0);
}
bool cOmxDevice::Flush(int TimeoutMs)
{
dsyslog("rpihddevice: Flush()");
-
return true;
}
void cOmxDevice::Clear(void)
{
- m_mutex->Lock();
dsyslog("rpihddevice: Clear()");
+ m_mutex->Lock();
+ ResetAudioVideo();
+ m_omx->SetStartTime(0);
+
+ m_mutex->Unlock();
+ cDevice::Clear();
+}
+
+void cOmxDevice::ResetAudioVideo(bool flushVideoRender)
+{
m_omx->SetClockScale(1.0f);
- m_omx->SetMediaTime(0);
+ m_skipAudio = false;
+ m_trickRequest = 0;
+ m_audioPts = 0;
+ m_videoPts = 0;
- if (HasAudio())
+ if (m_hasAudio)
{
m_audio->Reset();
m_omx->FlushAudio();
}
- if (HasVideo())
- m_omx->FlushVideo(false);
+ if (m_hasVideo)
+ m_omx->FlushVideo(flushVideoRender);
- m_mutex->Unlock();
- cDevice::Clear();
+ m_hasAudio = false;
+ m_hasVideo = false;
}
+
void cOmxDevice::SetVolumeDevice(int Volume)
{
m_omx->SetVolume(Volume);
@@ -293,7 +392,7 @@ bool cOmxDevice::Poll(cPoller &Poller, int TimeoutMs)
time.Set();
while (!m_omx->PollVideoBuffers() || !m_audio->Poll())
{
- if (time.Elapsed() >= TimeoutMs)
+ if (time.Elapsed() >= (unsigned)TimeoutMs)
return false;
cCondWait::SleepMs(5);
}
@@ -316,6 +415,7 @@ cVideoCodec::eCodec cOmxDevice::ParseVideoCodec(const uchar *data, int length)
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
@@ -324,17 +424,23 @@ cVideoCodec::eCodec cOmxDevice::ParseVideoCodec(const uchar *data, int length)
if (p[i + 3] == 0xb3) // sequence header
return cVideoCodec::eMPEG2;
+ //p[i + 3] = 0xf0
else if (p[i + 3] == 0x09) // slice
{
+ // quick hack for converted mkvs
+ if (p[i + 4] == 0xf0)
+ return cVideoCodec::eH264;
+
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
+ case 2: case 7: // B frame
+ case 1: case 4: case 6: // P frame
default:
- return cVideoCodec::eInvalid;
+// return cVideoCodec::eInvalid;
+ return cVideoCodec::eH264;
}
}
return cVideoCodec::eInvalid;
diff --git a/omxdevice.h b/omxdevice.h
index 52a94a3..10efea4 100644
--- a/omxdevice.h
+++ b/omxdevice.h
@@ -34,21 +34,24 @@ public:
virtual int Init(void);
virtual int DeInit(void);
- virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect);
-
virtual bool HasDecoder(void) const { return true; };
+ virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect);
+
virtual bool SetPlayMode(ePlayMode PlayMode);
virtual bool CanReplay(void) const;
- virtual int PlayVideo(const uchar *Data, int Length);
+ virtual void StillPicture(const uchar *Data, int Length);
+
virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
+ virtual int PlayVideo(const uchar *Data, int Length)
+ { return PlayVideo(Data, Length, false); }
- virtual int64_t GetSTC(void);
+ virtual int PlayVideo(const uchar *Data, int Length, bool singleFrame = false);
- virtual bool Flush(int TimeoutMs = 0);
+ virtual int64_t GetSTC(void);
- virtual bool HasIBPTrickSpeed(void) { return false; }
+ virtual bool HasIBPTrickSpeed(void) { return true; }
virtual void TrickSpeed(int Speed);
virtual void Clear(void);
virtual void Play(void);
@@ -56,6 +59,7 @@ public:
virtual void SetVolumeDevice(int Volume);
+ virtual bool Flush(int TimeoutMs = 0);
virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
protected:
@@ -65,28 +69,28 @@ protected:
private:
void (*m_onPrimaryDevice)(void);
-
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;
- };
+ void ResetAudioVideo(bool flushVideoRender = false);
- inline bool HasAudio() {
- return m_state == eAudioOnly ||
- m_state == eAudioVideo;
- };
+ void ApplyTrickSpeed(int trickSpeed, bool reverse = false);
+ void PtsTracker(int64_t ptsDiff);
cOmx *m_omx;
cAudioDecoder *m_audio;
cMutex *m_mutex;
- eState m_state;
- uchar m_audioId;
+ cVideoCodec::eCodec m_videoCodec;
+
+ bool m_hasVideo;
+ bool m_hasAudio;
+
+ bool m_skipAudio;
+ int m_playDirection;
+ int m_trickRequest;
+
+ int64_t m_audioPts;
+ int64_t m_videoPts;
};
#endif
diff --git a/ovgosd.c b/ovgosd.c
index f8de9d2..50b75bd 100644
--- a/ovgosd.c
+++ b/ovgosd.c
@@ -24,8 +24,8 @@ public:
m_mutex(0),
m_width(0),
m_height(0),
- m_pixmap(0),
m_aspect(0),
+ m_pixmap(0),
m_d(0),
m_x(0),
m_y(0),
@@ -270,9 +270,6 @@ void cOvgOsd::Flush(void)
LOCK_PIXMAPS;
while (cPixmapMemory *pm = RenderPixmaps()) {
- int w = pm->ViewPort().Width();
- int h = pm->ViewPort().Height();
- int d = w * sizeof(tColor);
m_ovg->DrawPixmap(
Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(),
pm->ViewPort().Width(), pm->ViewPort().Height(),
diff --git a/po/de_DE.po b/po/de_DE.po
index 7d6bc20..6976645 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,16 +7,19 @@ msgid ""
msgstr ""
"Project-Id-Version: vdr-rpihddevice 0.0.4\n"
"Report-Msgid-Bugs-To: <see README>\n"
-"POT-Creation-Date: 2013-10-14 13:33+0200\n"
+"POT-Creation-Date: 2013-12-08 12:29+0100\n"
"PO-Revision-Date: 2013-10-14 13:36+0200\n"
"Last-Translator: <thomas@reufer.ch>\n"
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ASCII\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+msgid "video format not supported!"
+msgstr "Videoformat nicht unterstützt!"
+
msgid "analog"
msgstr "analog"
diff --git a/rpihddevice.c b/rpihddevice.c
index fa21315..68c78bb 100644
--- a/rpihddevice.c
+++ b/rpihddevice.c
@@ -12,7 +12,7 @@
#include "setup.h"
#include "types.h"
-static const char *VERSION = "0.0.5";
+static const char *VERSION = "0.0.6";
static const char *DESCRIPTION = "HD output device for Raspberry Pi";
class cDummyDevice : cDevice
@@ -82,12 +82,12 @@ bool cPluginRpiHdDevice::Initialize(void)
// test whether MPEG2 license is available
if (!cRpiSetup::IsVideoCodecSupported(cVideoCodec::eMPEG2))
- esyslog("rpihddevice: WARNING: MPEG2 video decoder not enabled!");
+ dsyslog("rpihddevice: MPEG2 video decoder not enabled!");
m_device = new cOmxDevice(&OnPrimaryDevice);
if (m_device)
- return (m_device->Init() == 0);
+ return !m_device->Init();
return false;
}
diff --git a/setup.c b/setup.c
index a482f7d..a763ef1 100644
--- a/setup.c
+++ b/setup.c
@@ -115,7 +115,7 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec,
{
// AAC and DTS pass-through currently not supported
if (codec == cAudioCodec::eAAC ||
- codec == cAudioCodec::eDTS)
+ codec == cAudioCodec::eADTS)
return false;
if (vc_tv_hdmi_audio_supported(
@@ -123,7 +123,6 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec,
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 :
diff --git a/types.h b/types.h
index f39fbbc..02dcfdb 100644
--- a/types.h
+++ b/types.h
@@ -17,7 +17,7 @@ public:
eAC3,
eEAC3,
eAAC,
- eDTS,
+ eADTS,
eNumCodecs,
eInvalid
};
@@ -28,7 +28,7 @@ public:
(codec == eAC3) ? "AC3" :
(codec == eEAC3) ? "E-AC3" :
(codec == eAAC) ? "AAC" :
- (codec == eDTS) ? "DTS" : "unknown";
+ (codec == eADTS) ? "ADTS" : "unknown";
}
};