diff options
author | Thomas Reufer <thomas@reufer.ch> | 2016-04-20 12:11:35 +0200 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2016-04-20 12:13:35 +0200 |
commit | 2ae1d9d53be5cf781c449ee0f026c77b9215167d (patch) | |
tree | 582b66504f90337fffe22de50130d90ba686e1b2 | |
parent | f2e71c5214d6bd20c76b0f0114e269ed74777e61 (diff) | |
download | vdr-plugin-rpihddevice-2ae1d9d53be5cf781c449ee0f026c77b9215167d.tar.gz vdr-plugin-rpihddevice-2ae1d9d53be5cf781c449ee0f026c77b9215167d.tar.bz2 |
move all audio related OMX stuff to cRpiOmxAudioRender
-rw-r--r-- | audio.c | 451 | ||||
-rw-r--r-- | audio.h | 21 | ||||
-rw-r--r-- | omx.c | 374 | ||||
-rw-r--r-- | omx.h | 35 | ||||
-rw-r--r-- | omxdevice.c | 66 |
5 files changed, 462 insertions, 485 deletions
@@ -888,13 +888,43 @@ const uint32_t cRpiAudioDecoder::cParser::DtsSampleRateTable[16] = /* ------------------------------------------------------------------------- */ -class cRpiAudioRender +// default: 16x 4096 bytes, now 128x 16k (2M) +#define OMX_AUDIO_BUFFERS 128 +#define OMX_AUDIO_BUFFERSIZE KILOBYTE(16); + +#define OMX_AUDIO_CHANNEL_MAPPING(s, c) \ +switch (c) { \ +case 4: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ + (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ + (s).eChannelMapping[2] = OMX_AUDIO_ChannelLR; \ + (s).eChannelMapping[3] = OMX_AUDIO_ChannelRR; \ + break; \ +case 1: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelCF; \ + break; \ +case 8: \ + (s).eChannelMapping[6] = OMX_AUDIO_ChannelLS; \ + (s).eChannelMapping[7] = OMX_AUDIO_ChannelRS; \ +case 6: \ + (s).eChannelMapping[2] = OMX_AUDIO_ChannelCF; \ + (s).eChannelMapping[3] = OMX_AUDIO_ChannelLFE; \ + (s).eChannelMapping[4] = OMX_AUDIO_ChannelLR; \ + (s).eChannelMapping[5] = OMX_AUDIO_ChannelRR; \ +case 2: \ +default: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ + (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ + break; } + +class cRpiOmxAudioRender : protected cOmxEventHandler { public: - cRpiAudioRender(cOmx *omx) : - m_mutex(new cMutex()), + cRpiOmxAudioRender(cOmx *omx) : + cOmxEventHandler(), + m_mutex(), m_omx(omx), m_port(cRpiAudioPort::eLocal), m_codec(cAudioCodec::eInvalid), @@ -909,17 +939,36 @@ public: m_resamplerConfigured(false), #endif m_pcmSampleFormat(AV_SAMPLE_FMT_NONE), - m_pts(0) + m_pts(0), + m_setStartTime(true), + m_spareBuffers(0) { + // create audio_render + if (!m_omx->CreateComponent(cOmx::eAudioRender, true)) + ELOG("failed creating audio render!"); + + m_omx->SetTunnel(cOmx::eClockToAudioRender, + cOmx::eClock, 81, cOmx::eAudioRender, 101); + + if (!m_omx->SetupTunnel(cOmx::eClockToAudioRender)) + ELOG("failed to setup up tunnel from clock to audio render!"); + + m_omx->ChangeComponentState(cOmx::eAudioRender, OMX_StateIdle); + m_omx->AddEventHandler(this); } - ~cRpiAudioRender() + ~cRpiOmxAudioRender() { Flush(); #ifdef DO_RESAMPLE swr_free(&m_resample); #endif - delete m_mutex; + + m_omx->DisableTunnel(cOmx::eClockToAudioRender); + m_omx->ChangeComponentState(cOmx::eAudioRender, OMX_StateIdle); + m_omx->CleanupComponent(cOmx::eAudioRender); + + m_omx->RemoveEventHandler(this); } int WriteSamples(uint8_t** data, int samples, int64_t pts, @@ -928,7 +977,7 @@ public: if (!Ready()) return 0; - m_mutex->Lock(); + cMutexLock MutexLock(&m_mutex); int copied = 0; if (sampleFormat == AV_SAMPLE_FMT_NONE) @@ -936,10 +985,19 @@ public: // pass through while (samples > copied) { - OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(pts); + OMX_BUFFERHEADERTYPE *buf = GetBuffer(); if (!buf) break; + if (pts == OMX_INVALID_PTS) + buf->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN; + else if (m_setStartTime) + { + buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + m_setStartTime = false; + } + cOmx::PtsToTicks(pts, buf->nTimeStamp); + unsigned int len = samples - copied; if (len > buf->nAllocLen) len = buf->nAllocLen; @@ -947,16 +1005,17 @@ public: memcpy(buf->pBuffer, *data + copied, len); buf->nFilledLen = len; - if (!m_omx->EmptyAudioBuffer(buf)) + if (!EmptyBuffer(buf)) break; copied += len; - pts = 0; + pts = OMX_INVALID_PTS; } } else { #ifdef DO_RESAMPLE + // local decode, do resampling if (!m_resamplerConfigured || m_pcmSampleFormat != sampleFormat) { @@ -965,10 +1024,18 @@ public: } if (m_resample) { - m_pts = pts ? pts : m_pts; - OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(m_pts); - if (buf) + m_pts = pts != OMX_INVALID_PTS ? pts : m_pts; + if (OMX_BUFFERHEADERTYPE *buf = GetBuffer()) { + if (m_pts == OMX_INVALID_PTS) + buf->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN; + else if (m_setStartTime) + { + buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + m_setStartTime = false; + } + cOmx::PtsToTicks(m_pts, buf->nTimeStamp); + if (buf->nAllocLen >= (samples * m_outChannels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))) { @@ -981,15 +1048,23 @@ public: m_pts += copiedSamples * 90000 / m_samplingRate; } - copied = m_omx->EmptyAudioBuffer(buf) ? samples : 0; + copied = EmptyBuffer(buf) ? samples : 0; } } #else // local decode, no resampling - m_pts = pts ? pts : m_pts; - OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(m_pts); - if (buf) + m_pts = pts != OMX_INVALID_PTS ? pts : m_pts; + if (OMX_BUFFERHEADERTYPE *buf = GetBuffer()) { + if (m_pts == OMX_INVALID_PTS) + buf->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN; + else if (m_setStartTime) + { + buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; + m_setStartTime = false; + } + cOmx::PtsToTicks(m_pts, buf->nTimeStamp); + unsigned int size = samples * m_outChannels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); if (buf->nAllocLen >= size) @@ -998,29 +1073,28 @@ public: buf->nFilledLen = size; m_pts += samples * 90000 / m_samplingRate; } - copied = m_omx->EmptyAudioBuffer(buf) ? samples : 0; + copied = EmptyBuffer(buf) ? samples : 0; } #endif } - m_mutex->Unlock(); return copied; } void Flush(void) { - m_mutex->Lock(); + cMutexLock MutexLock(&m_mutex); if (m_running) - m_omx->StopAudio(); + Stop(); m_configured = false; m_running = false; m_pts = 0; - m_mutex->Unlock(); + m_setStartTime = true; } void SetCodec(cAudioCodec::eCodec codec, unsigned int channels, unsigned int samplingRate, unsigned int frameSize) { - m_mutex->Lock(); + cMutexLock MutexLock(&m_mutex); if (codec != cAudioCodec::eInvalid && channels > 0) { m_inChannels = channels; @@ -1063,7 +1137,6 @@ public: m_resamplerConfigured = false; #endif } - m_mutex->Unlock(); } bool IsPassthrough(void) @@ -1081,7 +1154,7 @@ public: if (!m_configured) { // wait until render is ready before applying new settings - if (m_running && m_omx->GetAudioLatency()) + if (m_running && GetLatency()) return false; ApplyRenderSettings(); @@ -1089,15 +1162,51 @@ public: return true; } + void SetVolume(int vol) + { + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCT(volume); + volume.nPortIndex = 100; + volume.bLinear = OMX_TRUE; + volume.sVolume.nValue = vol * 100 / 255; + + if (!m_omx->SetConfig(cOmx::eAudioRender, + OMX_IndexConfigAudioVolume, &volume)) + ELOG("failed to set volume!"); + } + + void SetMute(bool mute) + { + OMX_AUDIO_CONFIG_MUTETYPE amute; + OMX_INIT_STRUCT(amute); + amute.nPortIndex = 100; + amute.bMute = mute ? OMX_TRUE : OMX_FALSE; + + if (!m_omx->SetConfig(cOmx::eAudioRender, + OMX_IndexConfigAudioMute, &amute)) + ELOG("failed to set mute state!"); + } + + int GetBufferUsage(void) + { + int usage = 0; + for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++) + usage += m_usedBuffers[i]; + + return usage * 100 / BUFFERSTAT_FILTER_SIZE / OMX_AUDIO_BUFFERS; + } + private: - cRpiAudioRender(const cRpiAudioRender&); - cRpiAudioRender& operator= (const cRpiAudioRender&); + cRpiOmxAudioRender(const cRpiOmxAudioRender&); + cRpiOmxAudioRender& operator= (const cRpiOmxAudioRender&); void ApplyRenderSettings(void) { + cMutexLock MutexLock(&m_mutex); + if (m_running) - m_omx->StopAudio(); + Stop(); if (m_codec != cAudioCodec::eInvalid) { @@ -1105,8 +1214,7 @@ private: cRpiSetup::SetHDMIChannelMapping(m_codec != cAudioCodec::ePCM, m_outChannels); - m_omx->SetupAudioRender(m_codec, m_outChannels, m_port, - m_samplingRate, m_frameSize); + Setup(m_codec, m_outChannels, m_port, m_samplingRate, m_frameSize); DLOG("set %s audio output format to %dch %s, %d.%dkHz%s", cRpiAudioPort::Str(m_port), m_outChannels, @@ -1118,6 +1226,176 @@ private: m_configured = true; } + void Setup(cAudioCodec::eCodec outputFormat, int channels, + cRpiAudioPort::ePort audioPort, int samplingRate, int frameSize) + { + OMX_AUDIO_PARAM_PORTFORMATTYPE format; + OMX_INIT_STRUCT(format); + format.nPortIndex = 100; + if (!m_omx->GetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioPortFormat, &format)) + ELOG("failed to get audio port format parameters!"); + + format.eEncoding = + outputFormat == cAudioCodec::ePCM ? OMX_AUDIO_CodingPCM : + outputFormat == cAudioCodec::eMPG ? OMX_AUDIO_CodingMP3 : + outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : + outputFormat == cAudioCodec::eDTS ? OMX_AUDIO_CodingDTS : + OMX_AUDIO_CodingAutoDetect; + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioPortFormat, &format)) + ELOG("failed to set audio port format parameters!"); + + switch (outputFormat) + { + case cAudioCodec::eMPG: + OMX_AUDIO_PARAM_MP3TYPE mp3; + OMX_INIT_STRUCT(mp3); + mp3.nPortIndex = 100; + mp3.nChannels = channels; + mp3.nSampleRate = samplingRate; + mp3.eChannelMode = OMX_AUDIO_ChannelModeStereo; // ? + mp3.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; // should be MPEG-1 layer 2 + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioMp3, &mp3)) + ELOG("failed to set audio render mp3 parameters!"); + break; + + case cAudioCodec::eAC3: + case cAudioCodec::eEAC3: + OMX_AUDIO_PARAM_DDPTYPE ddp; + OMX_INIT_STRUCT(ddp); + ddp.nPortIndex = 100; + ddp.nChannels = channels; + ddp.nSampleRate = samplingRate; + OMX_AUDIO_CHANNEL_MAPPING(ddp, channels); + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioDdp, &ddp)) + ELOG("failed to set audio render ddp parameters!"); + break; + + case cAudioCodec::eAAC: + OMX_AUDIO_PARAM_AACPROFILETYPE aac; + OMX_INIT_STRUCT(aac); + aac.nPortIndex = 100; + aac.nChannels = channels; + aac.nSampleRate = samplingRate; + aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioAac, &aac)) + ELOG("failed to set audio render aac parameters!"); + break; + + case cAudioCodec::eDTS: + OMX_AUDIO_PARAM_DTSTYPE dts; + OMX_INIT_STRUCT(dts); + dts.nPortIndex = 100; + dts.nChannels = channels; + dts.nSampleRate = samplingRate; + dts.nDtsType = 1; + dts.nFormat = 3; /* 16bit, LE */ + dts.nDtsFrameSizeBytes = frameSize; + OMX_AUDIO_CHANNEL_MAPPING(dts, channels); + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioDts, &dts)) + ELOG("failed to set audio render dts parameters!"); + break; + + case cAudioCodec::ePCM: + OMX_AUDIO_PARAM_PCMMODETYPE pcm; + OMX_INIT_STRUCT(pcm); + pcm.nPortIndex = 100; + pcm.nChannels = channels; + pcm.eNumData = OMX_NumericalDataSigned; + pcm.eEndian = OMX_EndianLittle; + pcm.bInterleaved = OMX_TRUE; + pcm.nBitPerSample = 16; + pcm.nSamplingRate = samplingRate; + pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + OMX_AUDIO_CHANNEL_MAPPING(pcm, channels); + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamAudioPcm, &pcm)) + ELOG("failed to set audio render pcm parameters!"); + break; + + default: + ELOG("output codec not supported: %s!", + cAudioCodec::Str(outputFormat)); + break; + } + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; + OMX_INIT_STRUCT(audioDest); + strcpy((char *)audioDest.sName, + audioPort == cRpiAudioPort::eLocal ? "local" : "hdmi"); + + if (!m_omx->SetConfig(cOmx::eAudioRender, + OMX_IndexConfigBrcmAudioDestination, &audioDest)) + ELOG("failed to set audio destination!"); + + // set up the number and size of buffers for audio render + OMX_PARAM_PORTDEFINITIONTYPE param; + OMX_INIT_STRUCT(param); + param.nPortIndex = 100; + if (!m_omx->GetParameter(cOmx::eAudioRender, + OMX_IndexParamPortDefinition, ¶m)) + ELOG("failed to get audio render port parameters!"); + + param.nBufferSize = OMX_AUDIO_BUFFERSIZE; + param.nBufferCountActual = OMX_AUDIO_BUFFERS; + for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++) + m_usedBuffers[i] = 0; + + if (!m_omx->SetParameter(cOmx::eAudioRender, + OMX_IndexParamPortDefinition, ¶m)) + ELOG("failed to set audio render port parameters!"); + + if (!m_omx->EnablePortBuffers(cOmx::eAudioRender, 100)) + ELOG("failed to enable port buffer on audio render!"); + + if (!m_omx->ChangeComponentState(cOmx::eAudioRender, OMX_StateExecuting)) + ELOG("failed to set audio render to executing state!"); + + if (!m_omx->SetupTunnel(cOmx::eClockToAudioRender)) + ELOG("failed to setup up tunnel from clock to audio render!"); + } + + void Stop(void) + { + // put audio render onto idle + m_omx->FlushTunnel(cOmx::eClockToAudioRender); + m_omx->DisableTunnel(cOmx::eClockToAudioRender); + m_omx->ChangeComponentState(cOmx::eAudioRender, OMX_StateIdle); + + m_omx->DisablePortBuffers(cOmx::eAudioRender, 100, m_spareBuffers); + m_spareBuffers = 0; + } + + unsigned int GetLatency(void) + { + unsigned int ret = 0; + + OMX_PARAM_U32TYPE u32; + OMX_INIT_STRUCT(u32); + u32.nPortIndex = 100; + + if (!m_omx->GetConfig(cOmx::eAudioRender, + OMX_IndexConfigAudioRenderingLatency, &u32)) + ELOG("failed get audio render latency!"); + else + ret = u32.nU32; + + return ret; + } + #ifdef DO_RESAMPLE void ApplyResamplerSettings(void) { @@ -1145,25 +1423,93 @@ private: } #endif - cMutex *m_mutex; - cOmx *m_omx; + OMX_BUFFERHEADERTYPE* GetBuffer(void) + { + OMX_BUFFERHEADERTYPE* buf = 0; + cMutexLock MutexLock(&m_mutex); + + if (m_spareBuffers) + { + buf = m_spareBuffers; + m_spareBuffers = + static_cast <OMX_BUFFERHEADERTYPE*>(buf->pAppPrivate); + buf->pAppPrivate = 0; + } + else + { + buf = m_omx->GetBuffer(cOmx::eAudioRender, 100); + if (buf) + m_usedBuffers[0]++; + } - cRpiAudioPort::ePort m_port; - cAudioCodec::eCodec m_codec; - unsigned int m_inChannels; - unsigned int m_outChannels; - unsigned int m_samplingRate; - unsigned int m_frameSize; - bool m_configured; - bool m_running; + if (buf) + { + buf->nFilledLen = 0; + buf->nOffset = 0; + buf->nFlags = 0; + } + return buf; + } + + bool EmptyBuffer(OMX_BUFFERHEADERTYPE *buf) + { + bool ret = true; + + #ifdef DEBUG_BUFFERS + cOmx::DumpBuffer(buf, "A"); + #endif + + if (!m_omx->EmptyBuffer(cOmx::eAudioRender, buf)) + { + ELOG("failed to empty OMX audio buffer"); + cMutexLock MutexLock(&m_mutex); + + buf->nFilledLen = 0; + buf->pAppPrivate = m_spareBuffers; + m_spareBuffers = buf; + ret = false; + } + return ret; + } + + void BufferEmptied(cOmx::eOmxComponent comp) + { + if (comp == cOmx::eAudioRender) + { + cMutexLock MutexLock(&m_mutex); + m_usedBuffers[0]--; + } + } + + void Tick(void) + { + for (int i = BUFFERSTAT_FILTER_SIZE - 1; i > 0; i--) + m_usedBuffers[i] = m_usedBuffers[i - 1]; + } + + cMutex m_mutex; + cOmx *m_omx; + + cRpiAudioPort::ePort m_port; + cAudioCodec::eCodec m_codec; + unsigned int m_inChannels; + unsigned int m_outChannels; + unsigned int m_samplingRate; + unsigned int m_frameSize; + bool m_configured; + bool m_running; #ifdef DO_RESAMPLE - SwrContext *m_resample; - bool m_resamplerConfigured; + SwrContext *m_resample; + bool m_resamplerConfigured; #endif - AVSampleFormat m_pcmSampleFormat; - int64_t m_pts; + AVSampleFormat m_pcmSampleFormat; + int64_t m_pts; + + int m_usedBuffers[BUFFERSTAT_FILTER_SIZE]; + bool m_setStartTime; + OMX_BUFFERHEADERTYPE *m_spareBuffers; }; /* ------------------------------------------------------------------------- */ @@ -1175,7 +1521,7 @@ cRpiAudioDecoder::cRpiAudioDecoder(cOmx *omx) : m_setupChanged(true), m_wait(new cCondWait()), m_parser(new cParser()), - m_render(new cRpiAudioRender(omx)) + m_render(new cRpiOmxAudioRender(omx)) { memset(m_codecs, 0, sizeof(m_codecs)); } @@ -1445,3 +1791,18 @@ void cRpiAudioDecoder::Log(void* ptr, int level, const char* fmt, va_list vl) else if (level <= AV_LOG_VERBOSE) DLOG("[libav] %s", line); } + +int cRpiAudioDecoder::GetBufferUsage(void) +{ + return m_render->GetBufferUsage(); +} + +void cRpiAudioDecoder::SetVolume(int vol) +{ + m_render->SetVolume(vol); +} + +void cRpiAudioDecoder::SetMute(bool mute) +{ + m_render->SetMute(mute); +} @@ -25,7 +25,7 @@ #include "tools.h" #include "omx.h" -class cRpiAudioRender; +class cRpiOmxAudioRender; class cRpiAudioDecoder : public cThread { @@ -44,6 +44,11 @@ public: virtual bool Poll(void); virtual void Reset(void); + int GetBufferUsage(void); + + void SetVolume(int vol); + void SetMute(bool mute); + protected: virtual void Action(void); @@ -65,14 +70,14 @@ private: class cParser; - Codec m_codecs[cAudioCodec::eNumCodecs]; - bool m_passthrough; - bool m_reset; - bool m_setupChanged; + Codec m_codecs[cAudioCodec::eNumCodecs]; + bool m_passthrough; + bool m_reset; + bool m_setupChanged; - cCondWait *m_wait; - cParser *m_parser; - cRpiAudioRender *m_render; + cCondWait *m_wait; + cParser *m_parser; + cRpiOmxAudioRender *m_render; }; #endif @@ -33,35 +33,6 @@ extern "C" { #define OMX_PRE_ROLL 0 -// default: 16x 4096 bytes, now 128x 16k (2M) -#define OMX_AUDIO_BUFFERS 128 -#define OMX_AUDIO_BUFFERSIZE KILOBYTE(16); - -#define OMX_AUDIO_CHANNEL_MAPPING(s, c) \ -switch (c) { \ -case 4: \ - (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ - (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ - (s).eChannelMapping[2] = OMX_AUDIO_ChannelLR; \ - (s).eChannelMapping[3] = OMX_AUDIO_ChannelRR; \ - break; \ -case 1: \ - (s).eChannelMapping[0] = OMX_AUDIO_ChannelCF; \ - break; \ -case 8: \ - (s).eChannelMapping[6] = OMX_AUDIO_ChannelLS; \ - (s).eChannelMapping[7] = OMX_AUDIO_ChannelRS; \ -case 6: \ - (s).eChannelMapping[2] = OMX_AUDIO_ChannelCF; \ - (s).eChannelMapping[3] = OMX_AUDIO_ChannelLFE; \ - (s).eChannelMapping[4] = OMX_AUDIO_ChannelLR; \ - (s).eChannelMapping[5] = OMX_AUDIO_ChannelRR; \ -case 2: \ -default: \ - (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ - (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ - break; } - class cOmxEvents { @@ -212,8 +183,6 @@ void cOmx::Action(void) break; case cOmxEvents::eBufferEmptied: - HandlePortBufferEmptied((eOmxComponent)event->data); - for (cOmxEventHandler *handler = m_eventHandlers->First(); handler; handler = m_eventHandlers->Next(handler)) handler->BufferEmptied((eOmxComponent)event->data); @@ -230,52 +199,13 @@ void cOmx::Action(void) if (timer.TimedOut()) { timer.Set(100); - Lock(); - for (int i = BUFFERSTAT_FILTER_SIZE - 1; i > 0; i--) - { - m_usedAudioBuffers[i] = m_usedAudioBuffers[i - 1]; - m_usedVideoBuffers[i] = m_usedVideoBuffers[i - 1]; - } - for (cOmxEventHandler *handler = m_eventHandlers->First(); handler; handler = m_eventHandlers->Next(handler)) handler->Tick(); - Unlock(); } } } -void cOmx::GetBufferUsage(int &audio, int &video) -{ - audio = 0; - for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++) - { - audio += m_usedAudioBuffers[i]; - } - audio = audio * 100 / BUFFERSTAT_FILTER_SIZE / OMX_AUDIO_BUFFERS; -} - -void cOmx::HandlePortBufferEmptied(eOmxComponent component) -{ - Lock(); - - switch (component) - { - case eVideoDecoder: - m_usedVideoBuffers[0]--; - break; - - case eAudioRender: - m_usedAudioBuffers[0]--; - break; - - default: - ELOG("HandlePortBufferEmptied: invalid component!"); - break; - } - Unlock(); -} - void cOmx::HandlePortSettingsChanged(unsigned int portId) { Lock(); @@ -341,8 +271,6 @@ void cOmx::OnError(void *instance, COMPONENT_T *comp, OMX_U32 data) cOmx::cOmx() : cThread(), m_client(NULL), - m_setAudioStartTime(false), - m_spareAudioBuffers(0), m_clockReference(eClockRefNone), m_clockScale(0), m_portEvents(new cOmxEvents()), @@ -381,10 +309,6 @@ int cOmx::Init(int display, int layer) if (!CreateComponent(eClock)) ELOG("failed creating clock!"); - // create audio_render - if (!CreateComponent(eAudioRender, true)) - ELOG("failed creating audio render!"); - //create video_scheduler if (!CreateComponent(eVideoScheduler)) ELOG("failed creating video scheduler!"); @@ -392,26 +316,18 @@ int cOmx::Init(int display, int layer) // set tunnels SetTunnel(eVideoSchedulerToVideoRender, eVideoScheduler, 11, eVideoRender, 90); SetTunnel(eClockToVideoScheduler, eClock, 80, eVideoScheduler, 12); - SetTunnel(eClockToAudioRender, eClock, 81, eAudioRender, 101); // setup clock tunnels first if (!SetupTunnel(eClockToVideoScheduler)) ELOG("failed to setup up tunnel from clock to video scheduler!"); - if (!SetupTunnel(eClockToAudioRender)) - ELOG("failed to setup up tunnel from clock to audio render!"); - ChangeComponentState(eClock, OMX_StateExecuting); - ChangeComponentState(eAudioRender, OMX_StateIdle); SetDisplay(display, layer); SetClockLatencyTarget(); SetClockReference(cOmx::eClockRefVideo); - FlushAudio(); - Start(); - return 0; } @@ -420,13 +336,9 @@ int cOmx::DeInit(void) Cancel(-1); m_portEvents->Add(0); - DisableTunnel(eClockToAudioRender); - ChangeComponentState(eClock, OMX_StateIdle); - ChangeComponentState(eAudioRender, OMX_StateIdle); CleanupComponent(eClock); - CleanupComponent(eAudioRender); CleanupComponent(eVideoScheduler); CleanupComponent(eVideoRender); @@ -504,7 +416,7 @@ bool cOmx::EmptyBuffer(eOmxComponent comp, OMX_BUFFERHEADERTYPE *buf) { return buf && comp >= 0 && comp < eNumComponents && (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[comp]), buf) - == OMX_ErrorNone); + == OMX_ErrorNone); } bool cOmx::GetParameter(eOmxComponent comp, OMX_INDEXTYPE index, OMX_PTR param) @@ -667,7 +579,6 @@ void cOmx::StartClock(bool waitForVideo, bool waitForAudio) if (waitForAudio) { cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; - m_setAudioStartTime = true; cstate.nWaitMask |= OMX_CLOCKPORT1; } @@ -729,23 +640,6 @@ void cOmx::ResetClock(void) } } -unsigned int cOmx::GetAudioLatency(void) -{ - unsigned int ret = 0; - - OMX_PARAM_U32TYPE u32; - OMX_INIT_STRUCT(u32); - u32.nPortIndex = 100; - - if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexConfigAudioRenderingLatency, &u32) != OMX_ErrorNone) - ELOG("failed get audio render latency!"); - else - ret = u32.nU32; - - return ret; -} - void cOmx::SetClockReference(eClockReference clockReference) { if (m_clockReference != clockReference) @@ -817,207 +711,6 @@ bool cOmx::IsBufferStall(void) return stallConf.bStalled == OMX_TRUE; } -void cOmx::SetVolume(int vol) -{ - OMX_AUDIO_CONFIG_VOLUMETYPE volume; - OMX_INIT_STRUCT(volume); - volume.nPortIndex = 100; - volume.bLinear = OMX_TRUE; - volume.sVolume.nValue = vol * 100 / 255; - - if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexConfigAudioVolume, &volume) != OMX_ErrorNone) - ELOG("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) - ELOG("failed to set mute state!"); -} - -void cOmx::StopAudio(void) -{ - Lock(); - - // put audio render onto idle - ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1); - ilclient_disable_tunnel(&m_tun[eClockToAudioRender]); - ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle); - ilclient_disable_port_buffers(m_comp[eAudioRender], 100, - m_spareAudioBuffers, NULL, NULL); - - m_spareAudioBuffers = 0; - Unlock(); -} - -void cOmx::FlushAudio(void) -{ - Lock(); - - if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_CommandFlush, 100, NULL) != OMX_ErrorNone) - ELOG("failed to flush audio render!"); - - ilclient_wait_for_event(m_comp[eAudioRender], OMX_EventCmdComplete, - OMX_CommandFlush, 0, 100, 0, ILCLIENT_PORT_FLUSH, - VCOS_EVENT_FLAGS_SUSPEND); - - ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1); - Unlock(); -} - -int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, - cRpiAudioPort::ePort audioPort, int samplingRate, int frameSize) -{ - Lock(); - - OMX_AUDIO_PARAM_PORTFORMATTYPE format; - OMX_INIT_STRUCT(format); - format.nPortIndex = 100; - if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone) - ELOG("failed to get audio port format parameters!"); - - format.eEncoding = - outputFormat == cAudioCodec::ePCM ? OMX_AUDIO_CodingPCM : - outputFormat == cAudioCodec::eMPG ? OMX_AUDIO_CodingMP3 : - outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP : - outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP : - outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : - outputFormat == cAudioCodec::eDTS ? OMX_AUDIO_CodingDTS : - OMX_AUDIO_CodingAutoDetect; - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone) - ELOG("failed to set audio port format parameters!"); - - switch (outputFormat) - { - case cAudioCodec::eMPG: - OMX_AUDIO_PARAM_MP3TYPE mp3; - OMX_INIT_STRUCT(mp3); - mp3.nPortIndex = 100; - mp3.nChannels = channels; - mp3.nSampleRate = samplingRate; - mp3.eChannelMode = OMX_AUDIO_ChannelModeStereo; // ? - mp3.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; // should be MPEG-1 layer 2 - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioMp3, &mp3) != OMX_ErrorNone) - ELOG("failed to set audio render mp3 parameters!"); - break; - - case cAudioCodec::eAC3: - case cAudioCodec::eEAC3: - OMX_AUDIO_PARAM_DDPTYPE ddp; - OMX_INIT_STRUCT(ddp); - ddp.nPortIndex = 100; - ddp.nChannels = channels; - ddp.nSampleRate = samplingRate; - OMX_AUDIO_CHANNEL_MAPPING(ddp, channels); - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioDdp, &ddp) != OMX_ErrorNone) - ELOG("failed to set audio render ddp parameters!"); - break; - - case cAudioCodec::eAAC: - OMX_AUDIO_PARAM_AACPROFILETYPE aac; - OMX_INIT_STRUCT(aac); - aac.nPortIndex = 100; - aac.nChannels = channels; - aac.nSampleRate = samplingRate; - aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioAac, &aac) != OMX_ErrorNone) - ELOG("failed to set audio render aac parameters!"); - break; - - case cAudioCodec::eDTS: - OMX_AUDIO_PARAM_DTSTYPE dts; - OMX_INIT_STRUCT(dts); - dts.nPortIndex = 100; - dts.nChannels = channels; - dts.nSampleRate = samplingRate; - dts.nDtsType = 1; - dts.nFormat = 3; /* 16bit, LE */ - dts.nDtsFrameSizeBytes = frameSize; - OMX_AUDIO_CHANNEL_MAPPING(dts, channels); - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioDts, &dts) != OMX_ErrorNone) - ELOG("failed to set audio render dts parameters!"); - break; - - case cAudioCodec::ePCM: - OMX_AUDIO_PARAM_PCMMODETYPE pcm; - OMX_INIT_STRUCT(pcm); - pcm.nPortIndex = 100; - pcm.nChannels = channels; - pcm.eNumData = OMX_NumericalDataSigned; - pcm.eEndian = OMX_EndianLittle; - pcm.bInterleaved = OMX_TRUE; - pcm.nBitPerSample = 16; - pcm.nSamplingRate = samplingRate; - pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; - OMX_AUDIO_CHANNEL_MAPPING(pcm, channels); - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioPcm, &pcm) != OMX_ErrorNone) - ELOG("failed to set audio render pcm parameters!"); - break; - - default: - ELOG("output codec not supported: %s!", - cAudioCodec::Str(outputFormat)); - break; - } - - OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; - OMX_INIT_STRUCT(audioDest); - strcpy((char *)audioDest.sName, - audioPort == cRpiAudioPort::eLocal ? "local" : "hdmi"); - - if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone) - ELOG("failed to set audio destination!"); - - // set up the number and size of buffers for audio render - OMX_PARAM_PORTDEFINITIONTYPE param; - OMX_INIT_STRUCT(param); - param.nPortIndex = 100; - if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) - ELOG("failed to get audio render port parameters!"); - - param.nBufferSize = OMX_AUDIO_BUFFERSIZE; - param.nBufferCountActual = OMX_AUDIO_BUFFERS; - for (int i = 0; i < BUFFERSTAT_FILTER_SIZE; i++) - m_usedAudioBuffers[i] = 0; - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) - ELOG("failed to set audio render port parameters!"); - - if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0) - ELOG("failed to enable port buffer on audio render!"); - - ilclient_change_component_state(m_comp[eAudioRender], OMX_StateExecuting); - - if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0) - ELOG("failed to setup up tunnel from clock to audio render!"); - - Unlock(); - return 0; -} - void cOmx::SetDisplayMode(bool fill, bool noaspect) { OMX_CONFIG_DISPLAYREGIONTYPE region; @@ -1083,43 +776,6 @@ void cOmx::SetDisplay(int display, int layer) ELOG("failed to set display number and layer!"); } -OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(int64_t pts) -{ - Lock(); - OMX_BUFFERHEADERTYPE* buf = 0; - if (m_spareAudioBuffers) - { - buf = m_spareAudioBuffers; - m_spareAudioBuffers = - static_cast <OMX_BUFFERHEADERTYPE*>(buf->pAppPrivate); - buf->pAppPrivate = 0; - } - else - { - buf = ilclient_get_input_buffer(m_comp[eAudioRender], 100, 0); - if (buf) - m_usedAudioBuffers[0]++; - } - - if (buf) - { - buf->nFilledLen = 0; - buf->nOffset = 0; - buf->nFlags = 0; - - if (pts == OMX_INVALID_PTS) - buf->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN; - else if (m_setAudioStartTime) - { - buf->nFlags |= OMX_BUFFERFLAG_STARTTIME; - m_setAudioStartTime = false; - } - cOmx::PtsToTicks(pts, buf->nTimeStamp); - } - Unlock(); - return buf; -} - #ifdef DEBUG_BUFFERS void cOmx::DumpBuffer(OMX_BUFFERHEADERTYPE *buf, const char *prefix) { @@ -1146,31 +802,3 @@ void cOmx::DumpBuffer(OMX_BUFFERHEADERTYPE *buf, const char *prefix) ); } #endif - -bool cOmx::EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf) -{ - if (!buf) - return false; - - Lock(); - bool ret = true; -#ifdef DEBUG_BUFFERS - DumpBuffer(buf, "A"); -#endif - - if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf) - != OMX_ErrorNone) - { - ELOG("failed to empty OMX audio buffer"); - - if (buf->nFlags & OMX_BUFFERFLAG_STARTTIME) - m_setAudioStartTime = true; - - buf->nFilledLen = 0; - buf->pAppPrivate = m_spareAudioBuffers; - m_spareAudioBuffers = buf; - ret = false; - } - Unlock(); - return ret; -} @@ -38,6 +38,8 @@ extern "C" { (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ (a).nVersion.s.nStep = OMX_VERSION_STEP +#define BUFFERSTAT_FILTER_SIZE 64 + class cOmxEvents; class cOmxEventHandler; @@ -124,7 +126,6 @@ public: void SetClockScale(OMX_S32 scale); bool IsClockFreezed(void) { return m_clockScale == 0; } - unsigned int GetAudioLatency(void); enum eClockReference { eClockRefAudio, @@ -134,26 +135,15 @@ public: void SetClockReference(eClockReference clockReference); void SetClockLatencyTarget(void); - void SetVolume(int vol); - void SetMute(bool mute); - void StopAudio(void); - - void FlushAudio(void); - - int SetupAudioRender(cAudioCodec::eCodec outputFormat, - int channels, cRpiAudioPort::ePort audioPort, - int samplingRate = 0, int frameSize = 0); void SetDisplayMode(bool letterbox, bool noaspect); void SetPixelAspectRatio(int width, int height); void SetDisplayRegion(int x, int y, int width, int height); void SetDisplay(int display, int layer); - OMX_BUFFERHEADERTYPE* GetAudioBuffer(int64_t pts = OMX_INVALID_PTS); - - bool EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf); - - void GetBufferUsage(int &audio, int &video); +#ifdef DEBUG_BUFFERS + static void DumpBuffer(OMX_BUFFERHEADERTYPE *buf, const char *prefix = ""); +#endif private: @@ -161,23 +151,10 @@ private: static const char* errStr(int err); -#ifdef DEBUG_BUFFERS - static void DumpBuffer(OMX_BUFFERHEADERTYPE *buf, const char *prefix = ""); -#endif - ILCLIENT_T *m_client; COMPONENT_T *m_comp[cOmx::eNumComponents + 1]; TUNNEL_T m_tun[cOmx::eNumTunnels + 1]; - bool m_setAudioStartTime; - -#define BUFFERSTAT_FILTER_SIZE 64 - - int m_usedAudioBuffers[BUFFERSTAT_FILTER_SIZE]; - int m_usedVideoBuffers[BUFFERSTAT_FILTER_SIZE]; - - OMX_BUFFERHEADERTYPE* m_spareAudioBuffers; - eClockReference m_clockReference; OMX_S32 m_clockScale; @@ -185,7 +162,6 @@ private: cList<cOmxEventHandler> *m_eventHandlers; - void HandlePortBufferEmptied(eOmxComponent component); void HandlePortSettingsChanged(unsigned int portId); bool IsBufferStall(void); @@ -194,7 +170,6 @@ private: static void OnEndOfStream(void *instance, COMPONENT_T *comp, OMX_U32 data); static void OnError(void *instance, COMPONENT_T *comp, OMX_U32 data); static void OnConfigChanged(void *instance, COMPONENT_T *comp, OMX_U32 data); - }; /* ------------------------------------------------------------------------- */ diff --git a/omxdevice.c b/omxdevice.c index 71b444e..debd0a8 100644 --- a/omxdevice.c +++ b/omxdevice.c @@ -55,7 +55,7 @@ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void), int display, int layer) : cDevice(), m_onPrimaryDevice(onPrimaryDevice), m_omx(new cOmx()), - m_audio(new cRpiAudioDecoder(m_omx)), + m_audio(0), m_video(0), m_mutex(new cMutex()), m_timer(new cTimeMs()), @@ -81,7 +81,6 @@ cOmxDevice::~cOmxDevice() DeInit(); delete m_omx; - delete m_audio; delete m_mutex; delete m_timer; } @@ -93,6 +92,8 @@ int cOmxDevice::Init(void) ELOG("failed to initialize OMX!"); return -1; } + + m_audio = new cRpiAudioDecoder(m_omx); if (m_audio->Init() < 0) { ELOG("failed to initialize audio!"); @@ -110,6 +111,8 @@ int cOmxDevice::DeInit(void) ELOG("failed to deinitialize audio!"); return -1; } + delete m_audio; + if (m_omx->DeInit() < 0) { ELOG("failed to deinitialize OMX!"); @@ -587,30 +590,32 @@ void cOmxDevice::AdjustLiveSpeed(void) { if (m_timer->TimedOut()) { - int usedBuffers, usedAudioBuffers, usedVideoBuffers; - m_omx->GetBufferUsage(usedAudioBuffers, usedVideoBuffers); - usedBuffers = m_hasAudio ? usedAudioBuffers : usedVideoBuffers; - - if (usedBuffers < 5) - m_liveSpeed = eNegCorrection; - - else if (usedBuffers > 15) - m_liveSpeed = ePosCorrection; - - else if ((usedBuffers > 10 && m_liveSpeed == eNegCorrection) || - (usedBuffers < 10 && m_liveSpeed == ePosCorrection)) - m_liveSpeed = eNoCorrection; - -#ifdef DEBUG_BUFFERSTAT - DLOG("buffer usage: A=%3d%%, V=%3d%%, Corr=%d", - usedAudioBuffers, usedVideoBuffers, - m_liveSpeed == eNegMaxCorrection ? -2 : - m_liveSpeed == eNegCorrection ? -1 : - m_liveSpeed == eNoCorrection ? 0 : - m_liveSpeed == ePosCorrection ? 1 : - m_liveSpeed == ePosMaxCorrection ? 2 : 0); -#endif - m_omx->SetClockScale(s_liveSpeeds[m_liveSpeed]); + int usedBuffers = m_hasAudio ? m_audio->GetBufferUsage() : + (m_video ? m_video->GetBufferUsage() : -1); + + if (usedBuffers >= 0) + { + if (usedBuffers < 5) + m_liveSpeed = eNegCorrection; + + else if (usedBuffers > 15) + m_liveSpeed = ePosCorrection; + + else if ((usedBuffers > 10 && m_liveSpeed == eNegCorrection) || + (usedBuffers < 10 && m_liveSpeed == ePosCorrection)) + m_liveSpeed = eNoCorrection; + + #ifdef DEBUG_BUFFERSTAT + DLOG("buffer usage: A=%3d%%, V=%3d%%, Corr=%d", + usedAudioBuffers, usedVideoBuffers, + m_liveSpeed == eNegMaxCorrection ? -2 : + m_liveSpeed == eNegCorrection ? -1 : + m_liveSpeed == eNoCorrection ? 0 : + m_liveSpeed == ePosCorrection ? 1 : + m_liveSpeed == ePosMaxCorrection ? 2 : 0); + #endif + m_omx->SetClockScale(s_liveSpeeds[m_liveSpeed]); + } m_timer->Set(1000); } } @@ -625,6 +630,9 @@ void cOmxDevice::HandleEndOfStream(void) if (m_hasVideo && m_video) m_video->Clear(); + if (m_hasAudio) + m_audio->Reset(); + m_omx->ResetClock(); m_omx->SetClockScale(s_playbackSpeeds[m_direction][m_playbackSpeed]); @@ -674,11 +682,11 @@ void cOmxDevice::SetVolumeDevice(int Volume) DBG("SetVolume(%d)", Volume); if (Volume) { - m_omx->SetVolume(Volume); - m_omx->SetMute(false); + m_audio->SetVolume(Volume); + m_audio->SetMute(false); } else - m_omx->SetMute(true); + m_audio->SetMute(true); } bool cOmxDevice::Poll(cPoller &Poller, int TimeoutMs) |