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_FLOATP && format.m_dataFormat != AE_FMT_FLOAT &&
184 format.m_dataFormat != AE_FMT_S32NE && format.m_dataFormat != AE_FMT_S32NEP && format.m_dataFormat != AE_FMT_S32LE &&
185 format.m_dataFormat != AE_FMT_S16NE && format.m_dataFormat != AE_FMT_S16NEP && format.m_dataFormat != AE_FMT_S16LE)
186 format.m_dataFormat = AE_FMT_S16LE;
187 unsigned int channels = format.m_channelLayout.Count();
188 unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
189 format.m_frameSize = sample_size * channels;
190 format.m_sampleRate = std::max(8000U, std::min(192000U, format.m_sampleRate));
191 format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER / NUM_OMX_BUFFERS;
192 format.m_frameSamples = format.m_frames * channels;
194 SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough));
197 m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
199 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
200 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);
202 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
204 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
206 if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
207 CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
209 OMX_INIT_STRUCTURE(m_pcm_input);
210 m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
211 m_pcm_input.eNumData = OMX_NumericalDataSigned;
212 m_pcm_input.eEndian = OMX_EndianLittle;
213 m_pcm_input.bInterleaved = OMX_TRUE;
214 m_pcm_input.nBitPerSample = sample_size * 8;
215 // 0x8000 = float, 0x10000 = planar
217 if (m_format.m_dataFormat == AE_FMT_FLOAT || m_format.m_dataFormat == AE_FMT_FLOATP)
219 if (AE_IS_PLANAR(m_format.m_dataFormat))
221 m_pcm_input.ePCMMode = flags == 0 ? OMX_AUDIO_PCMModeLinear : (OMX_AUDIO_PCMMODETYPE)flags;
222 m_pcm_input.nChannels = channels;
223 m_pcm_input.nSamplingRate = m_format.m_sampleRate;
225 omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
226 if (omx_err != OMX_ErrorNone)
227 CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
229 m_omx_render.ResetEos();
233 // set up the number/size of buffers for decoder input
234 OMX_PARAM_PORTDEFINITIONTYPE port_param;
235 OMX_INIT_STRUCTURE(port_param);
236 port_param.nPortIndex = m_omx_render.GetInputPort();
238 omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
239 if (omx_err != OMX_ErrorNone)
240 CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
242 port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
243 port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames;
245 omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
246 if (omx_err != OMX_ErrorNone)
247 CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
249 omx_err = m_omx_render.AllocInputBuffers();
250 if (omx_err != OMX_ErrorNone)
251 CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
253 omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
254 if (omx_err != OMX_ErrorNone)
255 CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
257 m_Initialized = true;
262 void CAESinkPi::Deinitialize()
264 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
265 SetAudioProps(false, 0);
268 m_omx_render.FlushAll();
269 m_omx_render.Deinitialize();
270 m_Initialized = false;
274 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
277 /* compare against the requested format and the real format */
278 (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
279 (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
280 (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
281 (m_initDevice == device);
282 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);
286 void CAESinkPi::GetDelay(AEDelayStatus& status)
288 OMX_PARAM_U32TYPE param;
289 OMX_INIT_STRUCTURE(param);
297 param.nPortIndex = m_omx_render.GetInputPort();
299 OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, ¶m);
301 if (omx_err != OMX_ErrorNone)
303 CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
304 CLASSNAME, __func__, omx_err);
306 double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
307 status.SetDelay(sinkbuffer_seconds_to_empty);
310 double CAESinkPi::GetCacheTotal()
312 return AUDIO_PLAYBUFFER;
315 unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
317 if (!m_Initialized || !frames)
320 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
321 OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
323 unsigned int channels = m_format.m_channelLayout.Count();
324 unsigned int sample_size = CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3;
325 const int planes = AE_IS_PLANAR(m_format.m_dataFormat) ? channels : 1;
326 const int chans = AE_IS_PLANAR(m_format.m_dataFormat) ? 1 : channels;
327 const int pitch = chans * sample_size;
329 AEDelayStatus status;
331 double delay = status.GetDelay();
332 if (delay <= 0.0 && m_submitted)
333 CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
335 omx_buffer = m_omx_render.GetInputBuffer(1000);
336 if (omx_buffer == NULL)
338 CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout");
342 omx_buffer->nFilledLen = frames * m_format.m_frameSize;
344 assert(omx_buffer->nFilledLen <= omx_buffer->nAllocLen);
345 omx_buffer->nTimeStamp = ToOMXTime(0);
346 omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
348 if (omx_buffer->nFilledLen)
350 int planesize = omx_buffer->nFilledLen / planes;
351 for (int i=0; i < planes; i++)
352 memcpy((uint8_t *)omx_buffer->pBuffer + i * planesize, data[i] + offset * pitch, planesize);
354 omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
355 if (omx_err != OMX_ErrorNone)
356 CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
359 delay = status.GetDelay();
360 if (delay > AUDIO_PLAYBUFFER)
361 Sleep((int)(1000.0f * (delay - AUDIO_PLAYBUFFER)));
365 void CAESinkPi::Drain()
367 AEDelayStatus status;
369 int delay = (int)(status.GetDelay() * 1000.0);
372 CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(status.GetDelay() * 1000.0));
375 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
377 m_info.m_channels.Reset();
378 m_info.m_dataFormats.clear();
379 m_info.m_sampleRates.clear();
381 m_info.m_deviceType = AE_DEVTYPE_HDMI;
382 m_info.m_deviceName = "HDMI";
383 m_info.m_displayName = "HDMI";
384 m_info.m_displayNameExtra = "";
385 m_info.m_channels += AE_CH_FL;
386 m_info.m_channels += AE_CH_FR;
387 for (unsigned int i=0; i<sizeof PassthroughSampleRates/sizeof *PassthroughSampleRates; i++)
388 m_info.m_sampleRates.push_back(PassthroughSampleRates[i]);
389 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
390 m_info.m_dataFormats.push_back(AE_FMT_S32NE);
391 m_info.m_dataFormats.push_back(AE_FMT_S16NE);
392 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
393 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
394 m_info.m_dataFormats.push_back(AE_FMT_FLOATP);
395 m_info.m_dataFormats.push_back(AE_FMT_S32NEP);
396 m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
397 m_info.m_dataFormats.push_back(AE_FMT_AC3);
398 m_info.m_dataFormats.push_back(AE_FMT_DTS);
399 m_info.m_dataFormats.push_back(AE_FMT_EAC3);
401 list.push_back(m_info);
403 m_info.m_channels.Reset();
404 m_info.m_dataFormats.clear();
405 m_info.m_sampleRates.clear();
407 m_info.m_deviceType = AE_DEVTYPE_PCM;
408 m_info.m_deviceName = "Analogue";
409 m_info.m_displayName = "Analogue";
410 m_info.m_displayNameExtra = "";
411 m_info.m_channels += AE_CH_FL;
412 m_info.m_channels += AE_CH_FR;
413 m_info.m_sampleRates.push_back(48000);
414 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
415 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
416 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
417 m_info.m_dataFormats.push_back(AE_FMT_FLOATP);
418 m_info.m_dataFormats.push_back(AE_FMT_S32NEP);
419 m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
421 list.push_back(m_info);