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 (0.1) // 100ms
39 static const unsigned int PassthroughSampleRates[] = { 8000, 11025, 16000, 22050, 24000, 32000, 41400, 48000, 88200, 96000, 176400, 192000 };
41 CAEDeviceInfo CAESinkPi::m_info;
43 CAESinkPi::CAESinkPi() :
44 m_sinkbuffer_sec_per_byte(0),
50 CAESinkPi::~CAESinkPi()
54 void CAESinkPi::SetAudioDest()
56 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
57 OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
58 OMX_INIT_STRUCTURE(audioDest);
59 if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
60 strncpy((char *)audioDest.sName, "local", strlen("local"));
62 strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
63 omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
64 if (omx_err != OMX_ErrorNone)
65 CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
68 static void SetAudioProps(bool stream_channels, uint32_t channel_map)
70 char command[80], response[80];
72 sprintf(command, "hdmi_stream_channels %d", stream_channels ? 1 : 0);
73 vc_gencmd(response, sizeof response, command);
75 sprintf(command, "hdmi_channel_map 0x%08x", channel_map);
76 vc_gencmd(response, sizeof response, command);
78 CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map);
81 static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
83 unsigned int channels = format.m_channelLayout.Count();
84 uint32_t channel_map = 0;
88 static const unsigned char map_normal[] =
103 static const unsigned char map_back[] =
118 const unsigned char *map = map_normal;
119 // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
120 // but no BR BL channels, we use the wide map in order to open only the num of channels really
122 if (format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_SL))
125 for (unsigned int i = 0; i < channels; ++i)
127 AEChannel c = format.m_channelLayout[i];
128 unsigned int chan = 0;
129 if ((unsigned int)c < sizeof map_normal / sizeof *map_normal)
130 chan = map[(unsigned int)c];
132 channel_map |= (chan-1) << (3*i);
134 // These numbers are from Table 28 Audio InfoFrame Data byte 4 of CEA 861
135 // and describe the speaker layout
136 static const uint8_t cea_map[] = {
147 static const uint8_t cea_map_lfe[] = {
158 uint8_t cea = format.m_channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
160 CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, format.m_channelLayout.HasChannel(AE_CH_LFE), channels);
162 channel_map |= cea << 24;
167 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
169 // This may be called before Application calls g_RBP.Initialise, so call it here too
172 /* if we are raw need to let gpu know */
173 m_passthrough = AE_IS_RAW(format.m_dataFormat);
175 m_initDevice = device;
176 m_initFormat = format;
178 // analogue only supports stereo
179 if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
180 format.m_channelLayout = AE_CH_LAYOUT_2_0;
182 // setup for a 50ms sink feed from SoftAE
183 if (format.m_dataFormat != AE_FMT_FLOAT && format.m_dataFormat != AE_FMT_S32LE)
184 format.m_dataFormat = AE_FMT_S16LE;
185 unsigned int channels = format.m_channelLayout.Count();
186 unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
187 format.m_frameSize = sample_size * channels;
188 format.m_sampleRate = std::max(8000U, std::min(192000U, format.m_sampleRate));
189 format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER;
190 format.m_frameSamples = format.m_frames * channels;
192 SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough));
195 m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
197 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
198 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);
200 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
202 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
204 if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
205 CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
207 OMX_INIT_STRUCTURE(m_pcm_input);
208 m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
209 m_pcm_input.eNumData = OMX_NumericalDataSigned;
210 m_pcm_input.eEndian = OMX_EndianLittle;
211 m_pcm_input.bInterleaved = OMX_TRUE;
212 m_pcm_input.nBitPerSample = sample_size * 8;
213 m_pcm_input.ePCMMode = m_format.m_dataFormat == AE_FMT_FLOAT ? (OMX_AUDIO_PCMMODETYPE)0x8000 : OMX_AUDIO_PCMModeLinear;
214 m_pcm_input.nChannels = channels;
215 m_pcm_input.nSamplingRate = m_format.m_sampleRate;
217 omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
218 if (omx_err != OMX_ErrorNone)
219 CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
221 m_omx_render.ResetEos();
225 // set up the number/size of buffers for decoder input
226 OMX_PARAM_PORTDEFINITIONTYPE port_param;
227 OMX_INIT_STRUCTURE(port_param);
228 port_param.nPortIndex = m_omx_render.GetInputPort();
230 omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
231 if (omx_err != OMX_ErrorNone)
232 CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
234 port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
235 port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames / port_param.nBufferCountActual;
237 omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
238 if (omx_err != OMX_ErrorNone)
239 CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
241 omx_err = m_omx_render.AllocInputBuffers();
242 if (omx_err != OMX_ErrorNone)
243 CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
245 omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
246 if (omx_err != OMX_ErrorNone)
247 CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
249 m_Initialized = true;
254 void CAESinkPi::Deinitialize()
256 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
257 SetAudioProps(false, 0);
260 m_omx_render.FlushAll();
261 m_omx_render.Deinitialize();
262 m_Initialized = false;
266 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
269 /* compare against the requested format and the real format */
270 (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
271 (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
272 (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
273 (m_initDevice == device);
274 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);
278 double CAESinkPi::GetDelay()
280 OMX_PARAM_U32TYPE param;
281 OMX_INIT_STRUCTURE(param);
286 param.nPortIndex = m_omx_render.GetInputPort();
288 OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, ¶m);
290 if (omx_err != OMX_ErrorNone)
292 CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
293 CLASSNAME, __func__, omx_err);
295 double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
296 return sinkbuffer_seconds_to_empty;
299 double CAESinkPi::GetCacheTime()
304 double CAESinkPi::GetCacheTotal()
306 return AUDIO_PLAYBUFFER;
309 unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
311 unsigned int sent = 0;
312 uint8_t *buffer = data[0]+offset*m_format.m_frameSize;
317 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
318 OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
319 while (sent < frames)
321 double delay = GetDelay();
322 double ideal_submission_time = AUDIO_PLAYBUFFER - delay;
323 // ideal amount of audio we'd like submit (to make delay match AUDIO_PLAYBUFFER)
325 int ideal_submission_samples = ideal_submission_time / (m_sinkbuffer_sec_per_byte * m_format.m_frameSize);
326 // if we are almost full then sleep (to avoid repeatedly sending a few samples)
327 bool too_laggy = ideal_submission_time < 0.25 * AUDIO_PLAYBUFFER;
328 int sleeptime = (int)(AUDIO_PLAYBUFFER * 0.25 * 1000.0);
334 omx_buffer = m_omx_render.GetInputBuffer(timeout);
335 if (omx_buffer == NULL)
337 CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
341 unsigned int space = omx_buffer->nAllocLen / m_format.m_frameSize;
342 unsigned int samples = std::min(std::min(space, (unsigned int)ideal_submission_samples), frames - sent);
344 omx_buffer->nFilledLen = samples * m_format.m_frameSize;
345 omx_buffer->nTimeStamp = ToOMXTime(0);
346 omx_buffer->nFlags = 0;
347 memcpy(omx_buffer->pBuffer, (uint8_t *)buffer + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
352 omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
354 if (delay <= 0.0 && m_submitted)
355 CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
357 omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
358 if (omx_err != OMX_ErrorNone)
359 CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
366 void CAESinkPi::Drain()
368 int delay = (int)(GetDelay() * 1000.0);
371 CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
374 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
376 m_info.m_channels.Reset();
377 m_info.m_dataFormats.clear();
378 m_info.m_sampleRates.clear();
380 m_info.m_deviceType = AE_DEVTYPE_HDMI;
381 m_info.m_deviceName = "HDMI";
382 m_info.m_displayName = "HDMI";
383 m_info.m_displayNameExtra = "";
384 m_info.m_channels += AE_CH_FL;
385 m_info.m_channels += AE_CH_FR;
386 for (unsigned int i=0; i<sizeof PassthroughSampleRates/sizeof *PassthroughSampleRates; i++)
387 m_info.m_sampleRates.push_back(PassthroughSampleRates[i]);
388 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
389 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
390 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
391 m_info.m_dataFormats.push_back(AE_FMT_AC3);
392 m_info.m_dataFormats.push_back(AE_FMT_DTS);
393 m_info.m_dataFormats.push_back(AE_FMT_EAC3);
395 list.push_back(m_info);
397 m_info.m_channels.Reset();
398 m_info.m_dataFormats.clear();
399 m_info.m_sampleRates.clear();
401 m_info.m_deviceType = AE_DEVTYPE_PCM;
402 m_info.m_deviceName = "Analogue";
403 m_info.m_displayName = "Analogue";
404 m_info.m_displayNameExtra = "";
405 m_info.m_channels += AE_CH_FL;
406 m_info.m_channels += AE_CH_FR;
407 m_info.m_sampleRates.push_back(48000);
408 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
409 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
410 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
412 list.push_back(m_info);