unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset) { if (!m_Initialized || !m_omx_output || !frames) { Sleep(10); return frames; } OMX_ERRORTYPE omx_err = OMX_ErrorNone; OMX_BUFFERHEADERTYPE *omx_buffer = NULL; unsigned int channels = m_format.m_channelLayout.Count(); unsigned int sample_size = CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3; const int planes = AE_IS_PLANAR(m_format.m_dataFormat) ? channels : 1; const int chans = AE_IS_PLANAR(m_format.m_dataFormat) ? 1 : channels; const int pitch = chans * sample_size; AEDelayStatus status; GetDelay(status); double delay = status.GetDelay(); if (delay <= 0.0 && m_submitted) CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames); omx_buffer = m_omx_output->GetInputBuffer(1000); if (omx_buffer == NULL) { CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout"); return 0; } omx_buffer->nFilledLen = frames * m_format.m_frameSize; // must be true assert(omx_buffer->nFilledLen <= omx_buffer->nAllocLen); omx_buffer->nTimeStamp = ToOMXTime(0); omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; if (omx_buffer->nFilledLen) { int planesize = omx_buffer->nFilledLen / planes; for (int i=0; i < planes; i++) memcpy((uint8_t *)omx_buffer->pBuffer + i * planesize, data[i] + offset * pitch, planesize); } omx_err = m_omx_output->EmptyThisBuffer(omx_buffer); if (omx_err != OMX_ErrorNone) { CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err); m_omx_output->DecoderEmptyBufferDone(m_omx_output->GetComponent(), omx_buffer); } m_submitted++; GetDelay(status); delay = status.GetDelay(); if (delay > m_latency) Sleep((int)(1000.0f * (delay - m_latency))); return frames; }
AVSampleFormat CAEUtil::GetAVSampleFormat(AEDataFormat format) { switch (format) { case AEDataFormat::AE_FMT_U8: return AV_SAMPLE_FMT_U8; case AEDataFormat::AE_FMT_S16NE: return AV_SAMPLE_FMT_S16; case AEDataFormat::AE_FMT_S32NE: return AV_SAMPLE_FMT_S32; case AEDataFormat::AE_FMT_S24NE4: return AV_SAMPLE_FMT_S32; case AEDataFormat::AE_FMT_S24NE4MSB: return AV_SAMPLE_FMT_S32; case AEDataFormat::AE_FMT_S24NE3: return AV_SAMPLE_FMT_S32; case AEDataFormat::AE_FMT_FLOAT: return AV_SAMPLE_FMT_FLT; case AEDataFormat::AE_FMT_DOUBLE: return AV_SAMPLE_FMT_DBL; case AEDataFormat::AE_FMT_U8P: return AV_SAMPLE_FMT_U8P; case AEDataFormat::AE_FMT_S16NEP: return AV_SAMPLE_FMT_S16P; case AEDataFormat::AE_FMT_S32NEP: return AV_SAMPLE_FMT_S32P; case AEDataFormat::AE_FMT_S24NE4P: return AV_SAMPLE_FMT_S32P; case AEDataFormat::AE_FMT_S24NE4MSBP: return AV_SAMPLE_FMT_S32P; case AEDataFormat::AE_FMT_S24NE3P: return AV_SAMPLE_FMT_S32P; case AEDataFormat::AE_FMT_FLOATP: return AV_SAMPLE_FMT_FLTP; case AEDataFormat::AE_FMT_DOUBLEP: return AV_SAMPLE_FMT_DBLP; case AEDataFormat::AE_FMT_RAW: return AV_SAMPLE_FMT_U8; default: { if (AE_IS_PLANAR(format)) return AV_SAMPLE_FMT_FLTP; else return AV_SAMPLE_FMT_FLT; } } }
bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) { // This may be called before Application calls g_RBP.Initialise, so call it here too g_RBP.Initialize(); /* if we are raw need to let gpu know */ m_passthrough = format.m_dataFormat == AE_FMT_RAW; m_initDevice = device; m_initFormat = format; m_latency = CServiceBroker::GetSettings().GetInt("audiooutput.latency") * 1e-3; m_latency = std::max(m_latency, 50e-3); if (m_passthrough || CServiceBroker::GetSettings().GetString(CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE) == "PI:HDMI") m_output = AESINKPI_HDMI; else if (CServiceBroker::GetSettings().GetString(CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE) == "PI:Analogue") m_output = AESINKPI_ANALOGUE; else if (CServiceBroker::GetSettings().GetString(CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE) == "PI:Both") m_output = AESINKPI_BOTH; else assert(0); // analogue only supports stereo if (m_output == AESINKPI_ANALOGUE || m_output == AESINKPI_BOTH) format.m_channelLayout = AE_CH_LAYOUT_2_0; // setup for a 50ms sink feed from SoftAE if (format.m_dataFormat != AE_FMT_FLOATP && format.m_dataFormat != AE_FMT_FLOAT && format.m_dataFormat != AE_FMT_S32NE && format.m_dataFormat != AE_FMT_S32NEP && format.m_dataFormat != AE_FMT_S32LE && format.m_dataFormat != AE_FMT_S16NE && format.m_dataFormat != AE_FMT_S16NEP && format.m_dataFormat != AE_FMT_S16LE) format.m_dataFormat = AE_FMT_S16LE; unsigned int channels = format.m_channelLayout.Count(); unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3; format.m_frameSize = sample_size * channels; format.m_sampleRate = std::max(8000U, std::min(192000U, format.m_sampleRate)); format.m_frames = format.m_sampleRate * m_latency / NUM_OMX_BUFFERS; m_format = format; m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate); CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f dest=%s", CLASSNAME, __func__, m_format.m_dataFormat, channels, m_format.m_sampleRate, m_format.m_frameSize, m_format.m_frameSize * m_format.m_frames, 1.0/m_sinkbuffer_sec_per_byte, CServiceBroker::GetSettings().GetString(CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE).c_str()); // magic value used when omxplayer is playing - want sink to be disabled if (m_passthrough && m_format.m_streamInfo.m_sampleRate == 16000) return true; SetAudioProps(m_passthrough, GetChannelMap(m_format.m_channelLayout, m_passthrough)); OMX_ERRORTYPE omx_err = OMX_ErrorNone; if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit)) CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err); if (m_output == AESINKPI_BOTH) { if (!m_omx_splitter.Initialize("OMX.broadcom.audio_splitter", OMX_IndexParamAudioInit)) CLog::Log(LOGERROR, "%s::%s - m_omx_splitter.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err); if (!m_omx_render_slave.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit)) CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err); m_omx_output = &m_omx_splitter; } else m_omx_output = &m_omx_render; SetAudioDest(); OMX_INIT_STRUCTURE(m_pcm_input); m_pcm_input.eNumData = OMX_NumericalDataSigned; m_pcm_input.eEndian = OMX_EndianLittle; m_pcm_input.bInterleaved = OMX_TRUE; m_pcm_input.nBitPerSample = sample_size * 8; // 0x8000 = float, 0x10000 = planar uint32_t flags = 0; if (m_format.m_dataFormat == AE_FMT_FLOAT || m_format.m_dataFormat == AE_FMT_FLOATP) flags |= 0x8000; if (AE_IS_PLANAR(m_format.m_dataFormat)) flags |= 0x10000; m_pcm_input.ePCMMode = flags == 0 ? OMX_AUDIO_PCMModeLinear : (OMX_AUDIO_PCMMODETYPE)flags; m_pcm_input.nChannels = channels; m_pcm_input.nSamplingRate = m_format.m_sampleRate; if ( m_omx_splitter.IsInitialized() ) { m_pcm_input.nPortIndex = m_omx_splitter.GetInputPort(); omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err); m_pcm_input.nPortIndex = m_omx_splitter.GetOutputPort(); omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if(omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err); m_pcm_input.nPortIndex = m_omx_splitter.GetOutputPort() + 1; omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if(omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } if ( m_omx_render_slave.IsInitialized() ) { m_pcm_input.nPortIndex = m_omx_render_slave.GetInputPort(); omx_err = m_omx_render_slave.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_render_slave SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } if ( m_omx_render.IsInitialized() ) { m_pcm_input.nPortIndex = m_omx_render.GetInputPort(); omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } if ( m_omx_output->IsInitialized() ) { // set up the number/size of buffers for decoder input OMX_PARAM_PORTDEFINITIONTYPE port_param; OMX_INIT_STRUCTURE(port_param); port_param.nPortIndex = m_omx_output->GetInputPort(); omx_err = m_omx_output->GetParameter(OMX_IndexParamPortDefinition, &port_param); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err); port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS); port_param.nBufferSize = ALIGN_UP(m_format.m_frameSize * m_format.m_frames, port_param.nBufferAlignment); omx_err = m_omx_output->SetParameter(OMX_IndexParamPortDefinition, &port_param); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err); omx_err = m_omx_output->AllocInputBuffers(); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err); } if ( m_omx_splitter.IsInitialized() ) { m_omx_tunnel_splitter.Initialize(&m_omx_splitter, m_omx_splitter.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort()); omx_err = m_omx_tunnel_splitter.Establish(); if (omx_err != OMX_ErrorNone) { CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_splitter.Establish 0x%08x", omx_err); return false; } m_omx_tunnel_splitter_slave.Initialize(&m_omx_splitter, m_omx_splitter.GetOutputPort() + 1, &m_omx_render_slave, m_omx_render_slave.GetInputPort()); omx_err = m_omx_tunnel_splitter_slave.Establish(); if (omx_err != OMX_ErrorNone) { CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_splitter_slave.Establish 0x%08x", omx_err); return false; } } if ( m_omx_splitter.IsInitialized() ) { omx_err = m_omx_splitter.SetStateForComponent(OMX_StateExecuting); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - m_omx_splitter OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } if ( m_omx_render.IsInitialized() ) { omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } if ( m_omx_render_slave.IsInitialized() ) { omx_err = m_omx_render_slave.SetStateForComponent(OMX_StateExecuting); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - m_omx_render_slave OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err); } m_Initialized = true; return true; }