Merge pull request #3640 from wsnipex/joystick-fix
[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 CAEDeviceInfo CAESinkPi::m_info;
40
41 CAESinkPi::CAESinkPi() :
42     m_sinkbuffer_size(0),
43     m_sinkbuffer_sec_per_byte(0),
44     m_Initialized(false),
45     m_submitted(0)
46 {
47 }
48
49 CAESinkPi::~CAESinkPi()
50 {
51 }
52
53 void CAESinkPi::SetAudioDest()
54 {
55   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
56   OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
57   OMX_INIT_STRUCTURE(audioDest);
58   if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
59     strncpy((char *)audioDest.sName, "local", strlen("local"));
60   else
61     strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
62   omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
63   if (omx_err != OMX_ErrorNone)
64     CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
65 }
66
67 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
68 {
69   char response[80];
70   /* if we are raw need to let gpu know */
71   if (AE_IS_RAW(format.m_dataFormat))
72   {
73     vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
74     m_passthrough = true;
75   }
76   else
77   {
78     vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
79     m_passthrough = false;
80   }
81
82   m_initDevice = device;
83   m_initFormat = format;
84   // setup for a 50ms sink feed from SoftAE
85   format.m_dataFormat    = AE_FMT_S16NE;
86   format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER;
87   format.m_frameSamples  = format.m_channelLayout.Count();
88   format.m_frameSize     = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
89   format.m_sampleRate    = std::max(8000U, std::min(96000U, format.m_sampleRate));
90
91   m_format = format;
92
93   m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS;
94   m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate);
95
96   CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
97                 format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte);
98
99   // This may be called before Application calls g_RBP.Initialise, so call it here too
100   g_RBP.Initialize();
101
102   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
103
104   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
105
106   if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
107     CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
108
109   OMX_INIT_STRUCTURE(m_pcm_input);
110   m_pcm_input.nPortIndex            = m_omx_render.GetInputPort();
111   m_pcm_input.eNumData              = OMX_NumericalDataSigned;
112   m_pcm_input.eEndian               = OMX_EndianLittle;
113   m_pcm_input.bInterleaved          = OMX_TRUE;
114   m_pcm_input.nBitPerSample         = 16;
115   m_pcm_input.ePCMMode              = OMX_AUDIO_PCMModeLinear;
116   m_pcm_input.nChannels             = m_format.m_frameSamples;
117   m_pcm_input.nSamplingRate         = m_format.m_sampleRate;
118   m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
119   m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
120   m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax;
121
122   omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
123   if (omx_err != OMX_ErrorNone)
124     CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
125
126   m_omx_render.ResetEos();
127
128   SetAudioDest();
129
130   // set up the number/size of buffers for decoder input
131   OMX_PARAM_PORTDEFINITIONTYPE port_param;
132   OMX_INIT_STRUCTURE(port_param);
133   port_param.nPortIndex = m_omx_render.GetInputPort();
134
135   omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
136   if (omx_err != OMX_ErrorNone)
137     CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
138
139   port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
140   port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual;
141
142   omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
143   if (omx_err != OMX_ErrorNone)
144     CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
145
146   omx_err = m_omx_render.AllocInputBuffers();
147   if (omx_err != OMX_ErrorNone)
148     CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
149
150   omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
151   if (omx_err != OMX_ErrorNone)
152     CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
153
154   m_Initialized = true;
155   return true;
156 }
157
158
159 void CAESinkPi::Deinitialize()
160 {
161   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
162   if (m_Initialized)
163   {
164     m_omx_render.FlushAll();
165     m_omx_render.Deinitialize();
166     m_Initialized = false;
167   }
168 }
169
170 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
171 {
172   bool compatible =
173       /* compare against the requested format and the real format */
174       (m_initFormat.m_sampleRate    == format.m_sampleRate    || m_format.m_sampleRate    == format.m_sampleRate   ) &&
175       (m_initFormat.m_dataFormat    == format.m_dataFormat    || m_format.m_dataFormat    == format.m_dataFormat   ) &&
176       (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
177       (m_initDevice == device);
178   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);
179   return compatible;
180 }
181
182 double CAESinkPi::GetDelay()
183 {
184   OMX_PARAM_U32TYPE param;
185   OMX_INIT_STRUCTURE(param);
186
187   if (!m_Initialized)
188     return 0.0;
189
190   param.nPortIndex = m_omx_render.GetInputPort();
191
192   OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, &param);
193
194   if (omx_err != OMX_ErrorNone)
195   {
196     CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
197       CLASSNAME, __func__, omx_err);
198   }
199   double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
200   return sinkbuffer_seconds_to_empty;
201 }
202
203 double CAESinkPi::GetCacheTime()
204 {
205   return GetDelay();
206 }
207
208 double CAESinkPi::GetCacheTotal()
209 {
210   double audioplus_buffer = AUDIO_PLAYBUFFER;
211   return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size + audioplus_buffer;
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     int timeout = blocking ? 1000 : 0;
226
227     // delay compared to maximum we'd like (to keep lag low)
228     double delay = GetDelay();
229     bool too_laggy = delay - AUDIO_PLAYBUFFER > 0.0;
230     omx_buffer = too_laggy ? NULL : m_omx_render.GetInputBuffer(timeout);
231
232     if (omx_buffer == NULL)
233     {
234       if (too_laggy)
235       {
236         Sleep((int)((delay - AUDIO_PLAYBUFFER) * 1000.0));
237         continue;
238       }
239       if (blocking)
240         CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
241       break;
242     }
243
244     omx_buffer->nFilledLen = std::min(omx_buffer->nAllocLen, (frames - sent) * m_format.m_frameSize);
245     omx_buffer->nTimeStamp = ToOMXTime(0);
246     omx_buffer->nFlags = 0;
247     memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen);
248     sent += omx_buffer->nFilledLen / m_format.m_frameSize;
249
250     if (sent == frames)
251       omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
252
253     if (delay <= 0.0 && m_submitted)
254       CLog::Log(LOGERROR, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
255
256     omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
257     if (omx_err != OMX_ErrorNone)
258       CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
259     m_submitted += omx_buffer->nFilledLen;
260   }
261
262   return sent;
263 }
264
265 void CAESinkPi::Drain()
266 {
267   int delay = (int)(GetDelay() * 1000.0);
268   if (delay)
269     Sleep(delay);
270   CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
271 }
272
273 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
274 {
275   m_info.m_channels.Reset();
276   m_info.m_dataFormats.clear();
277   m_info.m_sampleRates.clear();
278
279   m_info.m_deviceType = AE_DEVTYPE_HDMI;
280   m_info.m_deviceName = "HDMI";
281   m_info.m_displayName = "HDMI";
282   m_info.m_displayNameExtra = "";
283   m_info.m_channels += AE_CH_FL;
284   m_info.m_channels += AE_CH_FR;
285   m_info.m_sampleRates.push_back(48000);
286   m_info.m_dataFormats.push_back(AE_FMT_S16LE);
287   m_info.m_dataFormats.push_back(AE_FMT_AC3);
288   m_info.m_dataFormats.push_back(AE_FMT_DTS);
289   m_info.m_dataFormats.push_back(AE_FMT_EAC3);
290
291   list.push_back(m_info);
292
293   m_info.m_channels.Reset();
294   m_info.m_dataFormats.clear();
295   m_info.m_sampleRates.clear();
296
297   m_info.m_deviceType = AE_DEVTYPE_PCM;
298   m_info.m_deviceName = "Analogue";
299   m_info.m_displayName = "Analogue";
300   m_info.m_displayNameExtra = "";
301   m_info.m_channels += AE_CH_FL;
302   m_info.m_channels += AE_CH_FR;
303   m_info.m_sampleRates.push_back(48000);
304   m_info.m_dataFormats.push_back(AE_FMT_S16LE);
305
306   list.push_back(m_info);
307 }
308
309 #endif