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 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() :
45 m_sinkbuffer_sec_per_byte(0),
51 CAESinkPi::~CAESinkPi()
55 void CAESinkPi::SetAudioDest()
57 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
58 OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
59 OMX_INIT_STRUCTURE(audioDest);
60 if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
61 strncpy((char *)audioDest.sName, "local", strlen("local"));
63 strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
64 omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
65 if (omx_err != OMX_ErrorNone)
66 CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
69 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
72 /* if we are raw need to let gpu know */
73 if (AE_IS_RAW(format.m_dataFormat))
75 vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
80 vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
81 m_passthrough = false;
84 m_initDevice = device;
85 m_initFormat = format;
86 // setup for a 50ms sink feed from SoftAE
87 format.m_dataFormat = AE_FMT_S16NE;
88 format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER;
89 format.m_frameSamples = format.m_channelLayout.Count();
90 format.m_frameSize = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
91 format.m_sampleRate = std::max(8000U, std::min(96000U, format.m_sampleRate));
95 m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS;
96 m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate);
98 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
99 format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte);
101 // This may be called before Application calls g_RBP.Initialise, so call it here too
104 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
106 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
108 if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
109 CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
111 OMX_INIT_STRUCTURE(m_pcm_input);
112 m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
113 m_pcm_input.eNumData = OMX_NumericalDataSigned;
114 m_pcm_input.eEndian = OMX_EndianLittle;
115 m_pcm_input.bInterleaved = OMX_TRUE;
116 m_pcm_input.nBitPerSample = 16;
117 m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear;
118 m_pcm_input.nChannels = m_format.m_frameSamples;
119 m_pcm_input.nSamplingRate = m_format.m_sampleRate;
120 m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
121 m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
122 m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax;
124 omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
125 if (omx_err != OMX_ErrorNone)
126 CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
128 m_omx_render.ResetEos();
132 // set up the number/size of buffers for decoder input
133 OMX_PARAM_PORTDEFINITIONTYPE port_param;
134 OMX_INIT_STRUCTURE(port_param);
135 port_param.nPortIndex = m_omx_render.GetInputPort();
137 omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
138 if (omx_err != OMX_ErrorNone)
139 CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
141 port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
142 port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual;
144 omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
145 if (omx_err != OMX_ErrorNone)
146 CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
148 omx_err = m_omx_render.AllocInputBuffers();
149 if (omx_err != OMX_ErrorNone)
150 CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
152 omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
153 if (omx_err != OMX_ErrorNone)
154 CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
156 m_Initialized = true;
161 void CAESinkPi::Deinitialize()
163 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
166 m_omx_render.FlushAll();
167 m_omx_render.Deinitialize();
168 m_Initialized = false;
172 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
175 /* compare against the requested format and the real format */
176 (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
177 (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
178 (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
179 (m_initDevice == device);
180 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);
184 double CAESinkPi::GetDelay()
186 OMX_PARAM_U32TYPE param;
187 OMX_INIT_STRUCTURE(param);
192 param.nPortIndex = m_omx_render.GetInputPort();
194 OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, ¶m);
196 if (omx_err != OMX_ErrorNone)
198 CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
199 CLASSNAME, __func__, omx_err);
201 double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
202 return sinkbuffer_seconds_to_empty;
205 double CAESinkPi::GetCacheTime()
210 double CAESinkPi::GetCacheTotal()
212 double audioplus_buffer = AUDIO_PLAYBUFFER;
213 return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size + audioplus_buffer;
216 unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
218 unsigned int sent = 0;
223 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
224 OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
225 while (sent < frames)
227 int timeout = blocking ? 1000 : 0;
229 // delay compared to maximum we'd like (to keep lag low)
230 double delay = GetDelay();
231 bool too_laggy = delay - AUDIO_PLAYBUFFER > 0.0;
232 omx_buffer = too_laggy ? NULL : m_omx_render.GetInputBuffer(timeout);
234 if (omx_buffer == NULL)
238 Sleep((int)((delay - AUDIO_PLAYBUFFER) * 1000.0));
242 CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
246 omx_buffer->nFilledLen = std::min(omx_buffer->nAllocLen, (frames - sent) * m_format.m_frameSize);
247 omx_buffer->nTimeStamp = ToOMXTime(0);
248 omx_buffer->nFlags = 0;
249 memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
250 sent += omx_buffer->nFilledLen / m_format.m_frameSize;
253 omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
255 if (delay <= 0.0 && m_submitted)
256 CLog::Log(LOGERROR, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
258 omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
259 if (omx_err != OMX_ErrorNone)
260 CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
261 m_submitted += omx_buffer->nFilledLen;
267 void CAESinkPi::Drain()
269 int delay = (int)(GetDelay() * 1000.0);
272 CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
275 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
277 m_info.m_channels.Reset();
278 m_info.m_dataFormats.clear();
279 m_info.m_sampleRates.clear();
281 m_info.m_deviceType = AE_DEVTYPE_HDMI;
282 m_info.m_deviceName = "HDMI";
283 m_info.m_displayName = "HDMI";
284 m_info.m_displayNameExtra = "";
285 m_info.m_channels += AE_CH_FL;
286 m_info.m_channels += AE_CH_FR;
287 for (unsigned int i=0; i<sizeof PassthroughSampleRates/sizeof *PassthroughSampleRates; i++)
288 m_info.m_sampleRates.push_back(PassthroughSampleRates[i]);
289 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
290 m_info.m_dataFormats.push_back(AE_FMT_AC3);
291 m_info.m_dataFormats.push_back(AE_FMT_DTS);
292 m_info.m_dataFormats.push_back(AE_FMT_EAC3);
294 list.push_back(m_info);
296 m_info.m_channels.Reset();
297 m_info.m_dataFormats.clear();
298 m_info.m_sampleRates.clear();
300 m_info.m_deviceType = AE_DEVTYPE_PCM;
301 m_info.m_deviceName = "Analogue";
302 m_info.m_displayName = "Analogue";
303 m_info.m_displayNameExtra = "";
304 m_info.m_channels += AE_CH_FL;
305 m_info.m_channels += AE_CH_FR;
306 m_info.m_sampleRates.push_back(48000);
307 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
309 list.push_back(m_info);