[PiSink] Add support for float and 32-bit formats
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkPi.cpp
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "system.h"
22
23 #if defined(TARGET_RASPBERRY_PI)
24
25 #include <stdint.h>
26 #include <limits.h>
27
28 #include "AESinkPi.h"
29 #include "cores/AudioEngine/Utils/AEUtil.h"
30 #include "utils/log.h"
31 #include "settings/Settings.h"
32 #include "linux/RBP.h"
33
34 #define CLASSNAME "CAESinkPi"
35
36 #define NUM_OMX_BUFFERS 2
37 #define AUDIO_PLAYBUFFER (1.0/20.0)
38
39 static const unsigned int PassthroughSampleRates[] = { 8000, 11025, 16000, 22050, 24000, 32000, 41400, 48000, 88200, 96000, 176400, 192000 };
40
41 CAEDeviceInfo CAESinkPi::m_info;
42
43 CAESinkPi::CAESinkPi() :
44     m_sinkbuffer_sec_per_byte(0),
45     m_Initialized(false),
46     m_submitted(0)
47 {
48 }
49
50 CAESinkPi::~CAESinkPi()
51 {
52 }
53
54 void CAESinkPi::SetAudioDest()
55 {
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"));
61   else
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);
66 }
67
68 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
69 {
70   char response[80];
71   /* if we are raw need to let gpu know */
72   if (AE_IS_RAW(format.m_dataFormat))
73   {
74     vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
75     m_passthrough = true;
76   }
77   else
78   {
79     vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
80     m_passthrough = false;
81   }
82
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;
94
95   m_format = format;
96
97   m_format = format;
98   m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
99
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);
102
103   // This may be called before Application calls g_RBP.Initialise, so call it here too
104   g_RBP.Initialize();
105
106   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
107
108   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
109
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);
112
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;
122
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);
126
127   m_omx_render.ResetEos();
128
129   SetAudioDest();
130
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();
135
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);
139
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;
142
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);
146
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);
150
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);
154
155   m_Initialized = true;
156   return true;
157 }
158
159
160 void CAESinkPi::Deinitialize()
161 {
162   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
163   if (m_Initialized)
164   {
165     m_omx_render.FlushAll();
166     m_omx_render.Deinitialize();
167     m_Initialized = false;
168   }
169 }
170
171 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
172 {
173   bool compatible =
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);
180   return compatible;
181 }
182
183 double CAESinkPi::GetDelay()
184 {
185   OMX_PARAM_U32TYPE param;
186   OMX_INIT_STRUCTURE(param);
187
188   if (!m_Initialized)
189     return 0.0;
190
191   param.nPortIndex = m_omx_render.GetInputPort();
192
193   OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, &param);
194
195   if (omx_err != OMX_ErrorNone)
196   {
197     CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
198       CLASSNAME, __func__, omx_err);
199   }
200   double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
201   return sinkbuffer_seconds_to_empty;
202 }
203
204 double CAESinkPi::GetCacheTime()
205 {
206   return GetDelay();
207 }
208
209 double CAESinkPi::GetCacheTotal()
210 {
211   return AUDIO_PLAYBUFFER;
212 }
213
214 unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
215 {
216   unsigned int sent = 0;
217
218   if (!m_Initialized)
219     return frames;
220
221   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
222   OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
223   while (sent < frames)
224   {
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);
233     if (too_laggy)
234     {
235       if (blocking)
236       {
237         Sleep(sleeptime);
238         continue;
239       }
240       break;
241     }
242     omx_buffer = m_omx_render.GetInputBuffer(timeout);
243     if (omx_buffer == NULL)
244     {
245       if (blocking)
246         CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
247       break;
248     }
249
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);
252
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);
257
258     sent += samples;
259
260     if (sent == frames)
261       omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
262
263     if (delay <= 0.0 && m_submitted)
264       CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
265
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);
269     m_submitted++;
270   }
271
272   return sent;
273 }
274
275 void CAESinkPi::Drain()
276 {
277   int delay = (int)(GetDelay() * 1000.0);
278   if (delay)
279     Sleep(delay);
280   CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
281 }
282
283 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
284 {
285   m_info.m_channels.Reset();
286   m_info.m_dataFormats.clear();
287   m_info.m_sampleRates.clear();
288
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);
303
304   list.push_back(m_info);
305
306   m_info.m_channels.Reset();
307   m_info.m_dataFormats.clear();
308   m_info.m_sampleRates.clear();
309
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);
320
321   list.push_back(m_info);
322 }
323
324 #endif