diff options
-rw-r--r-- | HISTORY | 19 | ||||
-rw-r--r-- | audio.c | 278 | ||||
-rw-r--r-- | audio.h | 2 | ||||
-rw-r--r-- | ilclient/ilclient.c | 2 | ||||
-rw-r--r-- | omx.c | 167 | ||||
-rw-r--r-- | omx.h | 17 | ||||
-rw-r--r-- | omxdevice.c | 51 | ||||
-rw-r--r-- | omxdevice.h | 20 | ||||
-rw-r--r-- | rpihddevice.c | 3 | ||||
-rw-r--r-- | setup.c | 37 | ||||
-rw-r--r-- | setup.h | 1 |
11 files changed, 370 insertions, 227 deletions
@@ -1,6 +1,25 @@ VDR Plugin 'rpihddevice' Revision History ----------------------------------------- +2013-12-30: Version 0.0.7 +------------------------- +- new: + - support audio sampling rates other than 48kHz + - changed setting of trick speed with APIVERSNUM >= 20103 + - added deinterlacer +- fixed: + - improved audio parser + - fixed still image for H264 video + - mute audio render if volume is set to zero +- missing: + - image grabbing + - video format/output options + +2013-12-16: Version 0.0.6a +-------------------------- +- fixed: + - removed OMX mutex + 2013-12-15: Version 0.0.6 ------------------------- - new: @@ -42,6 +42,13 @@ public: return m_channels; } + unsigned int GetSamplingRate(void) + { + if (!m_parsed) + Parse(); + return m_samplingRate; + } + bool Empty(void) { if (!m_parsed) @@ -69,6 +76,7 @@ public: { m_codec = cAudioCodec::eInvalid; m_channels = 0; + m_samplingRate = 0; m_packet.size = 0; m_size = 0; m_parsed = false; @@ -122,12 +130,10 @@ private: int channels = 0; int offset = 0; int frameSize = 0; + int samplingRate = 0; while (m_size - offset >= 3) { - const uint8_t *p = m_packet.data + offset; - int n = m_size - offset; - // 4 bytes 0xFFExxxxx MPEG audio // 3 bytes 0x56Exxx AAC LATM audio // 5 bytes 0x0B77xxxxxx AC-3 audio @@ -135,58 +141,72 @@ private: // 7/9 bytes 0xFFFxxxxxxxxxxx ADTS audio // PCM audio can't be found - if (FastMpegCheck(p)) + const uint8_t *p = m_packet.data + offset; + int n = m_size - offset; + + switch (FastCheck(p)) { - if (MpegCheck(p, n, frameSize)) - { + case cAudioCodec::eMPG: + if (MpegCheck(p, n, frameSize, channels, samplingRate)) codec = cAudioCodec::eMPG; - channels = 2; - } break; - } - else if (FastAc3Check(p)) - { - if (Ac3Check(p, n, frameSize, channels)) + + case cAudioCodec::eAC3: + if (Ac3Check(p, n, frameSize, channels, samplingRate)) { codec = cAudioCodec::eAC3; if (n > 5 && p[5] > (10 << 3)) codec = cAudioCodec::eEAC3; } break; - } - else if (FastLatmCheck(p)) - { - if (LatmCheck(p, n, frameSize)) - { + + case cAudioCodec::eAAC: + if (LatmCheck(p, n, frameSize, channels, samplingRate)) codec = cAudioCodec::eAAC; - channels = 2; - } break; - } - else if (FastAdtsCheck(p)) - { - if (AdtsCheck(p, n, frameSize, channels)) + + case cAudioCodec::eADTS: + if (AdtsCheck(p, n, frameSize, channels, samplingRate)) codec = cAudioCodec::eADTS; break; + + default: + break; + } + + if (codec != cAudioCodec::eInvalid) + { + // if there is enough data in buffer, check if predicted next + // frame start is valid + if (n < frameSize + 3 || + FastCheck(p + frameSize) != cAudioCodec::eInvalid) + { + // if codec has been detected but buffer does not yet + // contains a complete frame, set size to zero to prevent + // frame from being decoded + if (frameSize > n) + frameSize = 0; + + break; + } } ++offset; } - if (codec != cAudioCodec::eInvalid) + if (offset) { - if (offset) - { - dsyslog("rpihddevice: audio packet shrinked by %d bytes", offset); - Shrink(offset); - } + dsyslog("rpihddevice: audio parser skipped %d of %d bytes", offset, m_size); + Shrink(offset); + } + if (codec != cAudioCodec::eInvalid) + { m_codec = codec; m_channels = channels; + m_samplingRate = samplingRate; m_packet.size = frameSize; } - else - Reset(); m_parsed = true; } @@ -194,18 +214,29 @@ private: AVPacket m_packet; cAudioCodec::eCodec m_codec; unsigned int m_channels; + unsigned int m_samplingRate; unsigned int m_size; bool m_parsed; /* ------------------------------------------------------------------------- */ - /* audio codec parser helper functions, taken from vdr-softhddevice */ + /* audio codec parser helper functions, based on vdr-softhddevice */ /* ------------------------------------------------------------------------- */ static const uint16_t BitRateTable[2][3][16]; static const uint16_t MpegSampleRateTable[4]; + static const uint32_t Mpeg4SampleRateTable[16]; static const uint16_t Ac3SampleRateTable[4]; static const uint16_t Ac3FrameSizeTable[38][3]; + static cAudioCodec::eCodec FastCheck(const uint8_t *p) + { + return FastMpegCheck(p) ? cAudioCodec::eMPG : + FastAc3Check (p) ? cAudioCodec::eAC3 : + FastLatmCheck(p) ? cAudioCodec::eAAC : + FastAdtsCheck(p) ? cAudioCodec::eADTS : + cAudioCodec::eInvalid; + } + /// /// Fast check for MPEG audio. /// @@ -242,6 +273,8 @@ private: /// o e 2x BitRate index /// o f 2x SampleRate index (41000, 48000, 32000, 0) /// o g 1x Padding bit + /// o h 1x Private bit + /// o i 2x Channel mode /// o .. Doesn't care /// /// frame length: @@ -250,48 +283,45 @@ private: /// Layer II & III: /// FrameLengthInBytes = 144 * BitRate / SampleRate + Padding /// - static bool MpegCheck(const uint8_t *data, int size, int &frame_size) + static bool MpegCheck(const uint8_t *p, int size, int &frameSize, int &channels, int &samplingRate) { - frame_size = 0; + frameSize = size; 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 cmode = (p[3] >> 6) & 0x03; + int mpeg2 = !(p[1] & 0x08) && (p[1] & 0x10); + int mpeg25 = !(p[1] & 0x08) && !(p[1] & 0x10); + int layer = 4 - ((p[1] >> 1) & 0x03); + int padding = (p[2] >> 1) & 0x01; + + // channel mode = [ stereo, joint stereo, dual channel, mono] + channels = cmode == 0x03 ? 1 : 2; - int sample_rate = MpegSampleRateTable[(data[2] >> 2) & 0x03]; - if (!sample_rate) + samplingRate = MpegSampleRateTable[(p[2] >> 2) & 0x03]; + if (!samplingRate) return false; - sample_rate >>= mpeg2; // MPEG 2 half rate - sample_rate >>= mpeg25; // MPEG 2.5 quarter rate + samplingRate >>= mpeg2; // MPEG 2 half rate + samplingRate >>= mpeg25; // MPEG 2.5 quarter rate - int bit_rate = BitRateTable[mpeg2 | mpeg25][layer - 1][(data[2] >> 4) & 0x0F]; + int bit_rate = BitRateTable[mpeg2 | mpeg25][layer - 1][(p[2] >> 4) & 0x0F]; if (!bit_rate) return false; switch (layer) { case 1: - frame_size = (12000 * bit_rate) / sample_rate; - frame_size = (frame_size + padding) * 4; + frameSize = (12000 * bit_rate) / samplingRate; + frameSize = (frameSize + padding) * 4; break; case 2: case 3: default: - frame_size = (144000 * bit_rate) / sample_rate; - frame_size = frame_size + padding; + frameSize = (144000 * bit_rate) / samplingRate; + frameSize = frameSize + padding; break; } - - if (size >= frame_size + 3 && !FastMpegCheck(data + frame_size)) - return false; - - if (frame_size > size) - frame_size = 0; - return true; } @@ -331,38 +361,43 @@ private: /// 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 d 11x 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 bool Ac3Check(const uint8_t *p, int size, int &frame_size, int &channels) + static bool Ac3Check(const uint8_t *p, int size, int &frameSize, int &channels, int &samplingRate) { - frame_size = 0; + frameSize = size; if (size < 7) return true; int acmod; bool lfe; - int sample_rate; // for future use, E-AC3 t.b.d. + int fscod = (p[4] & 0xC0) >> 6; + + samplingRate = Ac3SampleRateTable[fscod]; if (p[5] > (10 << 3)) // E-AC-3 { - if ((p[4] & 0xF0) == 0xF0) // invalid fscod fscod2 - return false; + if (fscod == 0x03) + { + int fscod2 = (p[4] & 0x30) >> 4; + if (fscod2 == 0x03) + return false; // invalid fscod & fscod2 + + samplingRate = Ac3SampleRateTable[fscod2] / 2; + } 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; + frameSize = ((p[2] & 0x07) << 8) + p[3] + 1; + frameSize *= 2; } else // AC-3 { - sample_rate = Ac3SampleRateTable[(p[4] >> 6) & 0x03]; - - int fscod = p[4] >> 6; if (fscod == 0x03) // invalid sample rate return false; @@ -382,7 +417,7 @@ private: lfe = (p[lfe_bptr / 8] & (1 << (7 - (lfe_bptr % 8)))); // invalid is checked above - frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2; + frameSize = Ac3FrameSizeTable[frmsizcod][fscod] * 2; } channels = @@ -396,13 +431,6 @@ private: acmod == 0x07 ? 5 : 0; // L, C, R, RL, RR if (lfe) channels++; - - if (size >= frame_size + 2 && !FastAc3Check(p + frame_size)) - return false; - - if (frame_size > size) - frame_size = 0; - return true; } @@ -425,22 +453,21 @@ private: /// /// 0x56Exxx already checked. /// - static bool LatmCheck(const uint8_t *p, int size, int &frame_size) + static bool LatmCheck(const uint8_t *p, int size, int &frameSize, int &channels, int &samplingRate) { - frame_size = 0; + frameSize = size; if (size < 3) return true; - // 13 bit frame size without header - frame_size = ((p[1] & 0x1F) << 8) + p[2]; - frame_size += 3; - - if (size >= frame_size + 3 && !FastLatmCheck(p + frame_size)) - return false; + // to do: determine channels + channels = 2; - if (frame_size > size) - frame_size = 0; + // to do: determine sampling rate + samplingRate = 48000; + // 13 bit frame size without header + frameSize = ((p[1] & 0x1F) << 8) + p[2]; + frameSize += 3; return true; } @@ -478,36 +505,34 @@ private: /// o ... /// o M*13 frame length /// - static bool AdtsCheck(const uint8_t *p, int size, int &frame_size, int &channels) + static bool AdtsCheck(const uint8_t *p, int size, int &frameSize, int &channels, int &samplingRate) { - frame_size = 0; + frameSize = size; if (size < 6) return true; - frame_size = (p[3] & 0x03) << 11; - frame_size |= (p[4] & 0xFF) << 3; - frame_size |= (p[5] & 0xE0) >> 5; + samplingRate = Mpeg4SampleRateTable[(p[2] >> 2) & 0x0F]; + if (!samplingRate) + return false; + + frameSize = (p[3] & 0x03) << 11; + frameSize |= (p[4] & 0xFF) << 3; + frameSize |= (p[5] & 0xE0) >> 5; - int ch_config = (p[2] & 0x01) << 7; - ch_config |= (p[3] & 0xC0) >> 6; + int cConf = (p[2] & 0x01) << 7; + cConf |= (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 + cConf == 0x00 ? 0 : // defined in AOT specific config + cConf == 0x01 ? 1 : // C + cConf == 0x02 ? 2 : // L, R + cConf == 0x03 ? 3 : // C, L, R + cConf == 0x04 ? 4 : // C, L, R, RC + cConf == 0x05 ? 5 : // C, L, R, RL, RR + cConf == 0x06 ? 6 : // C, L, R, RL, RR, LFE + cConf == 0x07 ? 8 : // C, L, R, SL, SR, RL, RR, LFE 0; - if (size >= frame_size + 3 && !FastAdtsCheck(p + frame_size)) - return false; - - if (frame_size > size) - frame_size = 0; - - return true; + return true; } }; @@ -536,6 +561,14 @@ const uint16_t cAudioParser::BitRateTable[2][3][16] = const uint16_t cAudioParser::MpegSampleRateTable[4] = { 44100, 48000, 32000, 0 }; /// +/// MPEG-4 sample rate table. +/// +const uint32_t cAudioParser::Mpeg4SampleRateTable[16] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +/// /// AC-3 sample rate table. /// const uint16_t cAudioParser::Ac3SampleRateTable[4] = { 48000, 44100, 32000, 0 }; @@ -679,8 +712,10 @@ void cAudioDecoder::Action(void) { unsigned int channels = 0; unsigned int outputChannels = 0; + unsigned int samplingRate = 0; bool bufferFull = false; + bool setupChanged = false; cAudioCodec::eCodec codec = cAudioCodec::eInvalid; OMX_BUFFERHEADERTYPE *buf = 0; @@ -697,11 +732,15 @@ void cAudioDecoder::Action(void) while (!m_reset) { + setupChanged |= cRpiSetup::HasAudioSetupChanged(); + // check for data if no decoded samples are pending if (!m_parser->Empty() && !frame->nb_samples) { - if (codec != m_parser->GetCodec() || - channels != m_parser->GetChannels()) + if (setupChanged || + codec != m_parser->GetCodec() || + channels != m_parser->GetChannels() || + samplingRate != m_parser->GetSamplingRate()) { // to change codec config, we need to empty buffer first if (buf) @@ -710,9 +749,12 @@ void cAudioDecoder::Action(void) { codec = m_parser->GetCodec(); channels = m_parser->GetChannels(); + samplingRate = m_parser->GetSamplingRate(); outputChannels = channels; - SetCodec(codec, outputChannels); + SetCodec(codec, outputChannels, samplingRate); + + setupChanged = false; } } } @@ -836,6 +878,10 @@ void cAudioDecoder::Action(void) // we have a buffer to empty if (buf && bufferFull) { + // send empty buffer if we're about to reset + if (m_reset) + buf->nFilledLen = 0; + if (m_omx->EmptyAudioBuffer(buf)) { bufferFull = false; @@ -854,7 +900,6 @@ void cAudioDecoder::Action(void) } } - dsyslog("reset"); if (buf && m_omx->EmptyAudioBuffer(buf)) buf = 0; @@ -864,27 +909,29 @@ void cAudioDecoder::Action(void) dsyslog("rpihddevice: cAudioDecoder() thread ended"); } -void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels) +void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels, unsigned int samplingRate) { if (codec != cAudioCodec::eInvalid && channels > 0) { dsyslog("rpihddevice: set audio codec to %dch %s", channels, cAudioCodec::Str(codec)); + avcodec_flush_buffers(m_codecs[codec].context); m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE; m_codecs[codec].context->request_channels = 0; + //m_codecs[codec].context->bit_rate = 0; m_passthrough = false; cAudioCodec::eCodec outputFormat = cAudioCodec::ePCM; cAudioPort::ePort outputPort = cAudioPort::eLocal; if (cRpiSetup::GetAudioPort() == cAudioPort::eHDMI && - cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, channels, 48000)) + cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, channels, samplingRate)) { outputPort = cAudioPort::eHDMI; if (cRpiSetup::IsAudioPassthrough() && - cRpiSetup::IsAudioFormatSupported(codec, channels, 48000)) + cRpiSetup::IsAudioFormatSupported(codec, channels, samplingRate)) { m_passthrough = true; outputFormat = codec; @@ -898,13 +945,14 @@ void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels) // if 2ch PCM audio on HDMI is supported if (cRpiSetup::GetAudioPort() == cAudioPort::eHDMI && - cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, 2, 48000)) + cRpiSetup::IsAudioFormatSupported(cAudioCodec::ePCM, 2, samplingRate)) outputPort = cAudioPort::eHDMI; } - m_omx->SetupAudioRender(outputFormat, channels, outputPort, 48000); - dsyslog("rpihddevice: set %s audio output format to %dch %s%s", + m_omx->SetupAudioRender(outputFormat, channels, outputPort, samplingRate); + dsyslog("rpihddevice: set %s audio output format to %dch %s, %d.%dkHz%s", cAudioPort::Str(outputPort), channels, cAudioCodec::Str(outputFormat), + samplingRate / 1000, (samplingRate % 1000) / 100, m_passthrough ? " (pass-through)" : ""); } } @@ -37,7 +37,7 @@ public: protected: virtual void Action(void); - void SetCodec(cAudioCodec::eCodec codec, unsigned int &channels); + void SetCodec(cAudioCodec::eCodec codec, unsigned int &channels, unsigned int samplingRate); struct Codec { diff --git a/ilclient/ilclient.c b/ilclient/ilclient.c index da08ad0..66d70a8 100644 --- a/ilclient/ilclient.c +++ b/ilclient/ilclient.c @@ -1080,7 +1080,7 @@ int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout vc_assert(error == OMX_ErrorNone); error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0); vc_assert(error == OMX_ErrorNone); - + if(enable_error) { //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/ @@ -5,6 +5,7 @@ */ #include "omx.h" +#include "setup.h" #include <vdr/tools.h> #include <vdr/thread.h> @@ -99,31 +100,33 @@ const char* cOmx::errStr(int err) "unknown"; } -void cOmx::HandleEndOfStream(unsigned int portId) - { - dsyslog("rpihddevice: HandleEndOfStream(%d)", portId); - - switch (portId) +void cOmx::Action(void) +{ + while (Running()) { - case 131: - break; - - case 11: - break; - - case 90: - break; + m_portEventReady->Wait(); + while (!m_portEvents->empty()) + { + HandlePortSettingsChanged(m_portEvents->front()); + m_portEvents->pop(); + } } } void cOmx::HandlePortSettingsChanged(unsigned int portId) { - //dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId); +// dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId); switch (portId) { - case 131: + case 191: + if (ilclient_setup_tunnel(&m_tun[eVideoFxToVideoScheduler], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from video fx to scheduler!"); + if (ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateExecuting) != 0) + esyslog("rpihddevice: failed to enable video scheduler!"); + break; + case 131: OMX_PARAM_PORTDEFINITIONTYPE portdef; OMX_INIT_STRUCT(portdef); portdef.nPortIndex = 131; @@ -138,15 +141,30 @@ void cOmx::HandlePortSettingsChanged(unsigned int portId) &interlace) != OMX_ErrorNone) esyslog("rpihddevice: failed to get video decoder interlace config!"); - dsyslog("rpihddevice: decoding video %dx%d%s", + bool deinterlace; + deinterlace = (cRpiSetup::IsDisplayProgressive() && + (interlace.eMode != OMX_InterlaceProgressive)); + + dsyslog("rpihddevice: decoding video %dx%d%s, %sabling deinterlacer", 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!"); + interlace.eMode == OMX_InterlaceProgressive ? "p" : "i", + deinterlace ? "en" : "dis"); + + OMX_CONFIG_IMAGEFILTERPARAMSTYPE filterparam; + OMX_INIT_STRUCT(filterparam); + filterparam.nPortIndex = 191; + filterparam.nNumParams = 1; + filterparam.nParams[0] = 3; + filterparam.eImageFilter = deinterlace ? OMX_ImageFilterDeInterlaceAdvanced : OMX_ImageFilterNone; + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoFx]), + OMX_IndexConfigCommonImageFilterParameters, &filterparam) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set deinterlacing paramaters!"); + + if (ilclient_setup_tunnel(&m_tun[eVideoDecoderToVideoFx], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from video decoder to fx!"); + if (ilclient_change_component_state(m_comp[eVideoFx], OMX_StateExecuting) != 0) + esyslog("rpihddevice: failed to enable video fx!"); break; case 11: @@ -158,30 +176,25 @@ void cOmx::HandlePortSettingsChanged(unsigned int portId) } } -void cOmx::HandleBufferEmpty(COMPONENT_T *comp) -{ - if (comp == m_comp[eVideoDecoder]) - m_freeVideoBuffers++; - else if (comp == m_comp[eAudioRender]) - m_freeAudioBuffers++; -} - void cOmx::OnBufferEmpty(void *instance, COMPONENT_T *comp) { cOmx* omx = static_cast <cOmx*> (instance); - omx->HandleBufferEmpty(comp); + if (comp == omx->m_comp[eVideoDecoder]) + omx->m_freeVideoBuffers++; + else if (comp == omx->m_comp[eAudioRender]) + omx->m_freeAudioBuffers++; } void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data) { cOmx* omx = static_cast <cOmx*> (instance); - omx->HandlePortSettingsChanged(data); + omx->m_portEvents->push(data); + omx->m_portEventReady->Signal(); } 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, OMX_U32 data) @@ -191,19 +204,24 @@ void cOmx::OnError(void *instance, COMPONENT_T *comp, OMX_U32 data) } cOmx::cOmx() : + cThread(), m_mutex(new cMutex()), m_setAudioStartTime(false), m_setVideoStartTime(false), m_setVideoDiscontinuity(false), m_freeAudioBuffers(0), m_freeVideoBuffers(0), - m_clockReference(eClockRefAudio) + m_clockReference(eClockRefAudio), + m_portEventReady(new cCondWait()), + m_portEvents(new std::queue<unsigned int>) { } cOmx::~cOmx() { delete m_mutex; + delete m_portEventReady; + delete m_portEvents; } int cOmx::Init(void) @@ -226,6 +244,11 @@ int cOmx::Init(void) (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0) esyslog("rpihddevice: failed creating video decoder!"); + // create image_fx + if (ilclient_create_component(m_client, &m_comp[eVideoFx], + "image_fx", ILCLIENT_DISABLE_ALL_PORTS) != 0) + esyslog("rpihddevice: failed creating video fx!"); + // create video_render if (ilclient_create_component(m_client, &m_comp[eVideoRender], "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) @@ -248,8 +271,11 @@ int cOmx::Init(void) 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[eVideoDecoderToVideoFx], + m_comp[eVideoDecoder], 131, m_comp[eVideoFx], 190); + + set_tunnel(&m_tun[eVideoFxToVideoScheduler], + m_comp[eVideoFx], 191, m_comp[eVideoScheduler], 10); set_tunnel(&m_tun[eVideoSchedulerToVideoRender], m_comp[eVideoScheduler], 11, m_comp[eVideoRender], 90); @@ -269,6 +295,7 @@ int cOmx::Init(void) ilclient_change_component_state(m_comp[eClock], OMX_StateExecuting); ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); + ilclient_change_component_state(m_comp[eVideoFx], OMX_StateIdle); // set up the number and size of buffers for audio render m_freeAudioBuffers = 2; //64; @@ -318,11 +345,15 @@ int cOmx::Init(void) SetClockState(cOmx::eClockStateRun); + Start(); + return 0; } int cOmx::DeInit(void) { + Cancel(); + ilclient_teardown_tunnels(m_tun); ilclient_state_transition(m_comp, OMX_StateIdle); ilclient_state_transition(m_comp, OMX_StateLoaded); @@ -402,14 +433,14 @@ bool cOmx::IsClockRunning(void) void cOmx::SetClockState(eClockState clockState) { - dsyslog("rpihddevice: SetClockState(%s)", +/* 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); @@ -463,7 +494,7 @@ void cOmx::SetClockState(eClockState clockState) if (cstate.eState == OMX_TIME_ClockStateWaitingForStartTime) // 200ms pre roll, value taken from omxplayer - cstate.nOffset = ToOmxTicks(-1000LL * 400); + cstate.nOffset = ToOmxTicks(-1000LL * 200); if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) @@ -537,15 +568,13 @@ 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 +/* else dsyslog("rpihddevice: set active clock reference to %s", - m_clockReference == eClockRefAudio ? "audio" : "video"); + m_clockReference == eClockRefAudio ? "audio" : "video"); */ } void cOmx::SetVolume(int vol) { - dsyslog("rpihddevice: SetVolumeDevice(%d)", vol); - OMX_AUDIO_CONFIG_VOLUMETYPE volume; OMX_INIT_STRUCT(volume); volume.nPortIndex = 100; @@ -557,6 +586,18 @@ void cOmx::SetVolume(int vol) esyslog("rpihddevice: failed to set volume!"); } +void cOmx::SetMute(bool mute) +{ + OMX_AUDIO_CONFIG_MUTETYPE amute; + OMX_INIT_STRUCT(amute); + amute.nPortIndex = 100; + amute.bMute = mute ? OMX_TRUE : OMX_FALSE; + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexConfigAudioMute, &amute) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set mute state!"); +} + void cOmx::SendEos(void) { #if 0 @@ -582,9 +623,14 @@ void cOmx::StopVideo(void) // put video decoder into idle ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); + // put video fx into idle + ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoFx], 1); + ilclient_disable_tunnel(&m_tun[eVideoDecoderToVideoFx]); + ilclient_change_component_state(m_comp[eVideoFx], 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[eVideoFxToVideoScheduler], 1); + ilclient_disable_tunnel(&m_tun[eVideoFxToVideoScheduler]); ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1); ilclient_disable_tunnel(&m_tun[eClockToVideoScheduler]); ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateIdle); @@ -648,7 +694,8 @@ void cOmx::FlushVideo(bool flushRender) 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); + ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoFx], 1); + ilclient_flush_tunnels(&m_tun[eVideoFxToVideoScheduler], 1); if (flushRender) ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1); @@ -658,8 +705,6 @@ void cOmx::FlushVideo(bool flushRender) 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!"); @@ -679,7 +724,7 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec, eDataUnitType dataUnit) // start with valid frames only if codec is MPEG2 SetVideoErrorConcealment(codec == cVideoCodec::eMPEG2); SetVideoDataUnitType(dataUnit); - //SetVideoDecoderExtraBuffers(3); + 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!"); @@ -730,7 +775,7 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP : outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP : outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : - outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_CodingDTS : + outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_CodingAAC : OMX_AUDIO_CodingAutoDetect; if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), @@ -768,34 +813,22 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, break; case cAudioCodec::eAAC: + case cAudioCodec::eADTS: OMX_AUDIO_PARAM_AACPROFILETYPE aac; OMX_INIT_STRUCT(aac); aac.nPortIndex = 100; aac.nChannels = channels; aac.nSampleRate = samplingRate; - aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4LATM; + aac.eAACStreamFormat = + outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_AACStreamFormatMP4LATM : + outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_AACStreamFormatMP4ADTS : + (OMX_AUDIO_AACSTREAMFORMATTYPE)0; 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::eADTS: - 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); @@ -7,6 +7,8 @@ #ifndef OMX_H #define OMX_H +#include <queue> +#include <vdr/thread.h> #include "types.h" extern "C" @@ -14,9 +16,7 @@ extern "C" #include "ilclient.h" } -class cMutex; - -class cOmx +class cOmx : public cThread { public: @@ -55,6 +55,7 @@ public: void SetClockReference(eClockReference clockReference); void SetVolume(int vol); + void SetMute(bool mute); void SendEos(void); void StopVideo(void); void StopAudio(void); @@ -86,11 +87,14 @@ public: private: + virtual void Action(void); + static const char* errStr(int err); enum eOmxComponent { eClock = 0, eVideoDecoder, + eVideoFx, eVideoScheduler, eVideoRender, eAudioRender, @@ -98,7 +102,8 @@ private: }; enum eOmxTunnel { - eVideoDecoderToVideoScheduler = 0, + eVideoDecoderToVideoFx = 0, + eVideoFxToVideoScheduler, eVideoSchedulerToVideoRender, eClockToVideoScheduler, eClockToAudioRender, @@ -120,9 +125,9 @@ private: eClockReference m_clockReference; - void HandleEndOfStream(unsigned int portId); + cCondWait *m_portEventReady; + std::queue<unsigned int> *m_portEvents; 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, OMX_U32 data); diff --git a/omxdevice.c b/omxdevice.c index 456f5b4..a87bd87 100644 --- a/omxdevice.c +++ b/omxdevice.c @@ -9,6 +9,7 @@ #include "audio.h" #include "setup.h" +#include <vdr/thread.h> #include <vdr/remux.h> #include <vdr/tools.h> #include <vdr/skins.h> @@ -120,8 +121,10 @@ void cOmxDevice::StillPicture(const uchar *Data, int 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++) + // twice for MPEG2 and 10x for H264... ? + int repeat = ParseVideoCodec(Data, Length) == cVideoCodec::eMPEG2 ? 2 : 10; + + while (repeat--) PlayVideo(Data, Length, true); } } @@ -218,6 +221,9 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) const uchar *payload = Data + PesPayloadOffset(Data); unsigned int length = PesLength(Data) - PesPayloadOffset(Data); + if (length > Length) + esyslog("rpihddevice: PES-Length > Length!"); + OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts); if (buf) { @@ -255,17 +261,10 @@ int64_t cOmxDevice::GetSTC(void) 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); + ResetAudioVideo(); + m_omx->SetStartTime(0); m_mutex->Unlock(); cDevice::Play(); @@ -279,6 +278,14 @@ void cOmxDevice::Freeze(void) cDevice::Freeze(); } +#if APIVERSNUM >= 20103 +void cOmxDevice::TrickSpeed(int Speed, bool Forward) +{ + m_mutex->Lock(); + ApplyTrickSpeed(Speed, Forward); + m_mutex->Unlock(); +} +#else void cOmxDevice::TrickSpeed(int Speed) { m_mutex->Lock(); @@ -294,8 +301,9 @@ void cOmxDevice::TrickSpeed(int Speed) m_mutex->Unlock(); } +#endif -void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool reverse) +void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool forward) { float scale = // slow forward @@ -304,9 +312,9 @@ void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool reverse) 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) : + trickSpeed == 6 ? (forward ? 2.0f : -2.0f) : + trickSpeed == 3 ? (forward ? 4.0f : -4.0f) : + trickSpeed == 1 ? (forward ? 12.0f : -12.0f) : // slow backward trickSpeed == 63 ? -0.125f : @@ -320,7 +328,7 @@ void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool reverse) m_skipAudio = true; dsyslog("rpihddevice: ApplyTrickSpeed(%.3f, %sward)", - scale, reverse ? "back" : "for"); + scale, forward ? "for" : "back"); } @@ -332,7 +340,7 @@ void cOmxDevice::PtsTracker(int64_t ptsDiff) m_playDirection += 2; if (m_playDirection < -2 || m_playDirection > 3) - ApplyTrickSpeed(m_trickRequest, m_playDirection < 0); + ApplyTrickSpeed(m_trickRequest, m_playDirection > 0); } bool cOmxDevice::Flush(int TimeoutMs) @@ -343,7 +351,6 @@ bool cOmxDevice::Flush(int TimeoutMs) void cOmxDevice::Clear(void) { - dsyslog("rpihddevice: Clear()"); m_mutex->Lock(); ResetAudioVideo(); @@ -377,7 +384,13 @@ void cOmxDevice::ResetAudioVideo(bool flushVideoRender) void cOmxDevice::SetVolumeDevice(int Volume) { - m_omx->SetVolume(Volume); + if (Volume) + { + m_omx->SetVolume(Volume); + m_omx->SetMute(false); + } + else + m_omx->SetMute(true); } bool cOmxDevice::Poll(cPoller &Poller, int TimeoutMs) diff --git a/omxdevice.h b/omxdevice.h index 797c58d..d28543e 100644 --- a/omxdevice.h +++ b/omxdevice.h @@ -8,26 +8,18 @@ #define OMX_DEVICE_H #include <vdr/device.h> -#include <vdr/thread.h> #include "types.h" class cOmx; class cAudioDecoder; +class cMutex; class cOmxDevice : cDevice { public: - enum eState { - eNone, - eStartingVideo, - eAudioOnly, - eVideoOnly, - eAudioVideo - }; - cOmxDevice(void (*onPrimaryDevice)(void)); virtual ~cOmxDevice(); @@ -47,12 +39,18 @@ public: virtual int PlayVideo(const uchar *Data, int Length) { return PlayVideo(Data, Length, false); } - virtual int PlayVideo(const uchar *Data, int Length, bool singleFrame = false); + virtual int PlayVideo(const uchar *Data, int Length, bool singleFrame); virtual int64_t GetSTC(void); virtual bool HasIBPTrickSpeed(void) { return true; } + +#if APIVERSNUM >= 20103 + virtual void TrickSpeed(int Speed, bool Forward); +#else virtual void TrickSpeed(int Speed); +#endif + virtual void Clear(void); virtual void Play(void); virtual void Freeze(void); @@ -73,7 +71,7 @@ private: void ResetAudioVideo(bool flushVideoRender = false); - void ApplyTrickSpeed(int trickSpeed, bool reverse = false); + void ApplyTrickSpeed(int trickSpeed, bool forward = true); void PtsTracker(int64_t ptsDiff); cOmx *m_omx; diff --git a/rpihddevice.c b/rpihddevice.c index 05ce3d0..03c0813 100644 --- a/rpihddevice.c +++ b/rpihddevice.c @@ -12,7 +12,7 @@ #include "setup.h" #include "types.h" -static const char *VERSION = "0.0.6a"; +static const char *VERSION = "0.0.7"; static const char *DESCRIPTION = "HD output device for Raspberry Pi"; class cDummyDevice : cDevice @@ -71,7 +71,6 @@ cPluginRpiHdDevice::cPluginRpiHdDevice(void) : cPluginRpiHdDevice::~cPluginRpiHdDevice() { - delete m_device; cRpiSetup::DropInstance(); } @@ -113,19 +113,20 @@ bool cRpiSetup::HwInit(void) bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec, int channels, int samplingRate) { - // AAC and DTS pass-through currently not supported - if (codec == cAudioCodec::eAAC || - codec == cAudioCodec::eADTS) - return false; + // AAC-LATM and AAC pass-through currently not supported +// if (codec == cAudioCodec::eAAC || +// codec == cAudioCodec::eADTS) +// return false; if (vc_tv_hdmi_audio_supported( 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::eADTS ? EDID_AudioFormat_eAAC : EDID_AudioFormat_ePCM, channels, samplingRate == 32000 ? EDID_AudioSampleRate_e32KHz : - samplingRate == 44000 ? EDID_AudioSampleRate_e44KHz : + samplingRate == 44100 ? EDID_AudioSampleRate_e44KHz : samplingRate == 88000 ? EDID_AudioSampleRate_e88KHz : samplingRate == 96000 ? EDID_AudioSampleRate_e96KHz : samplingRate == 176000 ? EDID_AudioSampleRate_e176KHz : @@ -134,6 +135,10 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec, EDID_AudioSampleSize_16bit) == 0) return true; + dsyslog("rpihddevice: %dch %s, %d.%dkHz not supported by HDMI device", + channels, cAudioCodec::Str(codec), + samplingRate / 1000, (samplingRate % 1000) / 100); + return false; } @@ -151,9 +156,31 @@ int cRpiSetup::GetDisplaySize(int &width, int &height, double &aspect) aspect = 1; return 0; } + return -1; } +bool cRpiSetup::IsDisplayProgressive(void) +{ + bool progressive = false; + + TV_DISPLAY_STATE_T tvstate; + memset(&tvstate, 0, sizeof(TV_DISPLAY_STATE_T)); + if (!vc_tv_get_display_state(&tvstate)) + { + // HDMI + if ((tvstate.state & (VC_HDMI_HDMI | VC_HDMI_DVI))) + progressive = tvstate.display.hdmi.scan_mode == 0; + // composite + else + progressive = false; + } + else + esyslog("rpihddevice: failed to get display state!"); + + return progressive; +} + bool cRpiSetup::HasAudioSetupChanged(void) { if (!GetInstance()->m_audioSetupChanged) @@ -34,6 +34,7 @@ public: } static int GetDisplaySize(int &width, int &height, double &aspect); + static bool IsDisplayProgressive(void); static cRpiSetup* GetInstance(void); static void DropInstance(void); |