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() :
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 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
71 /* if we are raw need to let gpu know */
72 if (AE_IS_RAW(format.m_dataFormat))
74 vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
79 vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
80 m_passthrough = false;
83 m_initDevice = device;
84 m_initFormat = format;
85 // setup for a 50ms sink feed from SoftAE
86 if (format.m_dataFormat != AE_FMT_FLOAT && format.m_dataFormat != AE_FMT_S32LE)
87 format.m_dataFormat = AE_FMT_S16LE;
88 unsigned int channels = format.m_channelLayout.Count();
89 unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
90 format.m_frameSize = sample_size * channels;
91 format.m_sampleRate = std::max(8000U, std::min(192000U, format.m_sampleRate));
92 format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER;
93 format.m_frameSamples = format.m_frames * channels;
98 m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
100 CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
101 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);
103 // This may be called before Application calls g_RBP.Initialise, so call it here too
106 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
108 OMX_ERRORTYPE omx_err = OMX_ErrorNone;
110 if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
111 CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
113 OMX_INIT_STRUCTURE(m_pcm_input);
114 m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
115 m_pcm_input.eNumData = OMX_NumericalDataSigned;
116 m_pcm_input.eEndian = OMX_EndianLittle;
117 m_pcm_input.bInterleaved = OMX_TRUE;
118 m_pcm_input.nBitPerSample = sample_size * 8;
119 m_pcm_input.ePCMMode = m_format.m_dataFormat == AE_FMT_FLOAT ? (OMX_AUDIO_PCMMODETYPE)0x8000 : OMX_AUDIO_PCMModeLinear;
120 m_pcm_input.nChannels = channels;
121 m_pcm_input.nSamplingRate = m_format.m_sampleRate;
123 omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
124 if (omx_err != OMX_ErrorNone)
125 CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
127 m_omx_render.ResetEos();
131 // set up the number/size of buffers for decoder input
132 OMX_PARAM_PORTDEFINITIONTYPE port_param;
133 OMX_INIT_STRUCTURE(port_param);
134 port_param.nPortIndex = m_omx_render.GetInputPort();
136 omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
137 if (omx_err != OMX_ErrorNone)
138 CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
140 port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
141 port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames / port_param.nBufferCountActual;
143 omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
144 if (omx_err != OMX_ErrorNone)
145 CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
147 omx_err = m_omx_render.AllocInputBuffers();
148 if (omx_err != OMX_ErrorNone)
149 CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
151 omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
152 if (omx_err != OMX_ErrorNone)
153 CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
155 m_Initialized = true;
160 void CAESinkPi::Deinitialize()
162 CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
165 m_omx_render.FlushAll();
166 m_omx_render.Deinitialize();
167 m_Initialized = false;
171 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
174 /* compare against the requested format and the real format */
175 (m_initFormat.m_sampleRate == format.m_sampleRate || m_format.m_sampleRate == format.m_sampleRate ) &&
176 (m_initFormat.m_dataFormat == format.m_dataFormat || m_format.m_dataFormat == format.m_dataFormat ) &&
177 (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
178 (m_initDevice == device);
179 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);
183 double CAESinkPi::GetDelay()
185 OMX_PARAM_U32TYPE param;
186 OMX_INIT_STRUCTURE(param);
191 param.nPortIndex = m_omx_render.GetInputPort();
193 OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, ¶m);
195 if (omx_err != OMX_ErrorNone)
197 CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
198 CLASSNAME, __func__, omx_err);
200 double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
201 return sinkbuffer_seconds_to_empty;
204 double CAESinkPi::GetCacheTime()
209 double CAESinkPi::GetCacheTotal()
211 return AUDIO_PLAYBUFFER;
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 double delay = GetDelay();
226 double ideal_submission_time = AUDIO_PLAYBUFFER - delay;
227 // ideal amount of audio we'd like submit (to make delay match AUDIO_PLAYBUFFER)
228 int timeout = blocking ? 1000 : 0;
229 int ideal_submission_samples = ideal_submission_time / (m_sinkbuffer_sec_per_byte * m_format.m_frameSize);
230 // if we are almost full then sleep (to avoid repeatedly sending a few samples)
231 bool too_laggy = ideal_submission_time < 0.25 * AUDIO_PLAYBUFFER;
232 int sleeptime = (int)(AUDIO_PLAYBUFFER * 0.25 * 1000.0);
242 omx_buffer = m_omx_render.GetInputBuffer(timeout);
243 if (omx_buffer == NULL)
246 CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
250 unsigned int space = omx_buffer->nAllocLen / m_format.m_frameSize;
251 unsigned int samples = std::min(std::min(space, (unsigned int)ideal_submission_samples), frames - sent);
253 omx_buffer->nFilledLen = samples * m_format.m_frameSize;
254 omx_buffer->nTimeStamp = ToOMXTime(0);
255 omx_buffer->nFlags = 0;
256 memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
261 omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
263 if (delay <= 0.0 && m_submitted)
264 CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
266 omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
267 if (omx_err != OMX_ErrorNone)
268 CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
275 void CAESinkPi::Drain()
277 int delay = (int)(GetDelay() * 1000.0);
280 CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
283 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
285 m_info.m_channels.Reset();
286 m_info.m_dataFormats.clear();
287 m_info.m_sampleRates.clear();
289 m_info.m_deviceType = AE_DEVTYPE_HDMI;
290 m_info.m_deviceName = "HDMI";
291 m_info.m_displayName = "HDMI";
292 m_info.m_displayNameExtra = "";
293 m_info.m_channels += AE_CH_FL;
294 m_info.m_channels += AE_CH_FR;
295 for (unsigned int i=0; i<sizeof PassthroughSampleRates/sizeof *PassthroughSampleRates; i++)
296 m_info.m_sampleRates.push_back(PassthroughSampleRates[i]);
297 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
298 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
299 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
300 m_info.m_dataFormats.push_back(AE_FMT_AC3);
301 m_info.m_dataFormats.push_back(AE_FMT_DTS);
302 m_info.m_dataFormats.push_back(AE_FMT_EAC3);
304 list.push_back(m_info);
306 m_info.m_channels.Reset();
307 m_info.m_dataFormats.clear();
308 m_info.m_sampleRates.clear();
310 m_info.m_deviceType = AE_DEVTYPE_PCM;
311 m_info.m_deviceName = "Analogue";
312 m_info.m_displayName = "Analogue";
313 m_info.m_displayNameExtra = "";
314 m_info.m_channels += AE_CH_FL;
315 m_info.m_channels += AE_CH_FR;
316 m_info.m_sampleRates.push_back(48000);
317 m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
318 m_info.m_dataFormats.push_back(AE_FMT_S32LE);
319 m_info.m_dataFormats.push_back(AE_FMT_S16LE);
321 list.push_back(m_info);