2 * Copyright (C) 2010-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 #if defined(TARGET_RASPBERRY_PI)
29 #include "cores/AudioEngine/Utils/AEUtil.h"
30 #include "utils/log.h"
31 #include "settings/Settings.h"
32 #include "linux/RBP.h"
34 #define CLASSNAME "CAESinkPi"
36 #define NUM_OMX_BUFFERS 2
37 #define AUDIO_PLAYBUFFER (1.0/20.0)
39 CAEDeviceInfo CAESinkPi::m_info;
41 CAESinkPi::CAESinkPi() :
43 m_sinkbuffer_sec_per_byte(0),
49 CAESinkPi::~CAESinkPi()
53 void CAESinkPi::SetAudioDest()
55 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
56 OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
57 OMX_INIT_STRUCTURE(audioDest);
58 if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
59 strncpy((char *)audioDest.sName, "local", strlen("local"));
61 strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
62 omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
63 if (omx_err != OMX_ErrorNone)
64 CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
67 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
70 /* if we are raw need to let gpu know */
71 if (AE_IS_RAW(format.m_dataFormat))
73 vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
78 vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
79 m_passthrough = false;
82 m_initDevice = device;
83 m_initFormat = format;
84 // setup for a 50ms sink feed from SoftAE
85 format.m_dataFormat = AE_FMT_S16NE;
86 format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER;
87 format.m_frameSamples = format.m_channelLayout.Count();
88 format.m_frameSize = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
89 format.m_sampleRate = std::max(8000U, std::min(96000U, format.m_sampleRate));
93 m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS;
94 m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate);
96 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
97 format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte);
99 // This may be called before Application calls g_RBP.Initialise, so call it here too
102 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
104 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
106 if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
107 CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
109 OMX_INIT_STRUCTURE(m_pcm_input);
110 m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
111 m_pcm_input.eNumData = OMX_NumericalDataSigned;
112 m_pcm_input.eEndian = OMX_EndianLittle;
113 m_pcm_input.bInterleaved = OMX_TRUE;
114 m_pcm_input.nBitPerSample = 16;
115 m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear;
116 m_pcm_input.nChannels = m_format.m_frameSamples;
117 m_pcm_input.nSamplingRate = m_format.m_sampleRate;
118 m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
119 m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
120 m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax;
122 omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
123 if (omx_err != OMX_ErrorNone)
124 CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
126 m_omx_render.ResetEos();
130 // set up the number/size of buffers for decoder input
131 OMX_PARAM_PORTDEFINITIONTYPE port_param;
132 OMX_INIT_STRUCTURE(port_param);
133 port_param.nPortIndex = m_omx_render.GetInputPort();
135 omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
136 if (omx_err != OMX_ErrorNone)
137 CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
139 port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
140 port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual;
142 omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
143 if (omx_err != OMX_ErrorNone)
144 CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
146 omx_err = m_omx_render.AllocInputBuffers();
147 if (omx_err != OMX_ErrorNone)
148 CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
150 omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
151 if (omx_err != OMX_ErrorNone)
152 CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
154 m_Initialized = true;
159 void CAESinkPi::Deinitialize()
161 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
164 m_omx_render.FlushAll();
165 m_omx_render.Deinitialize();
166 m_Initialized = false;
170 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
173 /* compare against the requested format and the real format */
174 (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
175 (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
176 (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
177 (m_initDevice == device);
178 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d = %d", CLASSNAME, __func__, format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, compatible);
182 double CAESinkPi::GetDelay()
184 OMX_PARAM_U32TYPE param;
185 OMX_INIT_STRUCTURE(param);
190 param.nPortIndex = m_omx_render.GetInputPort();
192 OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, ¶m);
194 if (omx_err != OMX_ErrorNone)
196 CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
197 CLASSNAME, __func__, omx_err);
199 double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
200 return sinkbuffer_seconds_to_empty;
203 double CAESinkPi::GetCacheTime()
208 double CAESinkPi::GetCacheTotal()
210 double audioplus_buffer = AUDIO_PLAYBUFFER;
211 return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size + audioplus_buffer;
214 unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
216 unsigned int sent = 0;
221 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
222 OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
223 while (sent < frames)
225 int timeout = blocking ? 1000 : 0;
227 // delay compared to maximum we'd like (to keep lag low)
228 double delay = GetDelay();
229 bool too_laggy = delay - AUDIO_PLAYBUFFER > 0.0;
230 omx_buffer = too_laggy ? NULL : m_omx_render.GetInputBuffer(timeout);
232 if (omx_buffer == NULL)
236 Sleep((int)((delay - AUDIO_PLAYBUFFER) * 1000.0));
240 CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
244 omx_buffer->nFilledLen = std::min(omx_buffer->nAllocLen, (frames - sent) * m_format.m_frameSize);
245 omx_buffer->nTimeStamp = ToOMXTime(0);
246 omx_buffer->nFlags = 0;
247 memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
248 sent += omx_buffer->nFilledLen / m_format.m_frameSize;
251 omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
253 if (delay <= 0.0 && m_submitted)
254 CLog::Log(LOGERROR, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
256 omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
257 if (omx_err != OMX_ErrorNone)
258 CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
259 m_submitted += omx_buffer->nFilledLen;
265 void CAESinkPi::Drain()
267 int delay = (int)(GetDelay() * 1000.0);
270 CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
273 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
275 m_info.m_channels.Reset();
276 m_info.m_dataFormats.clear();
277 m_info.m_sampleRates.clear();
279 m_info.m_deviceType = AE_DEVTYPE_HDMI;
280 m_info.m_deviceName = "HDMI";
281 m_info.m_displayName = "HDMI";
282 m_info.m_displayNameExtra = "";
283 m_info.m_channels += AE_CH_FL;
284 m_info.m_channels += AE_CH_FR;
285 m_info.m_sampleRates.push_back(48000);
286 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
287 m_info.m_dataFormats.push_back(AE_FMT_AC3);
288 m_info.m_dataFormats.push_back(AE_FMT_DTS);
289 m_info.m_dataFormats.push_back(AE_FMT_EAC3);
291 list.push_back(m_info);
293 m_info.m_channels.Reset();
294 m_info.m_dataFormats.clear();
295 m_info.m_sampleRates.clear();
297 m_info.m_deviceType = AE_DEVTYPE_PCM;
298 m_info.m_deviceName = "Analogue";
299 m_info.m_displayName = "Analogue";
300 m_info.m_displayNameExtra = "";
301 m_info.m_channels += AE_CH_FL;
302 m_info.m_channels += AE_CH_FR;
303 m_info.m_sampleRates.push_back(48000);
304 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
306 list.push_back(m_info);