Merge pull request #4356 from popcornmix/invalidpts
[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_size(0),
45     m_sinkbuffer_sec_per_byte(0),
46     m_Initialized(false),
47     m_submitted(0)
48 {
49 }
50
51 CAESinkPi::~CAESinkPi()
52 {
53 }
54
55 void CAESinkPi::SetAudioDest()
56 {
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"));
62   else
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);
67 }
68
69 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
70 {
71   char response[80];
72   /* if we are raw need to let gpu know */
73   if (AE_IS_RAW(format.m_dataFormat))
74   {
75     vc_gencmd(response, sizeof response, "hdmi_stream_channels 1");
76     m_passthrough = true;
77   }
78   else
79   {
80     vc_gencmd(response, sizeof response, "hdmi_stream_channels 0");
81     m_passthrough = false;
82   }
83
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));
92
93   m_format = format;
94
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);
97
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);
100
101   // This may be called before Application calls g_RBP.Initialise, so call it here too
102   g_RBP.Initialize();
103
104   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
105
106   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
107
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);
110
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;
123
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);
127
128   m_omx_render.ResetEos();
129
130   SetAudioDest();
131
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();
136
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);
140
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;
143
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);
147
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);
151
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);
155
156   m_Initialized = true;
157   return true;
158 }
159
160
161 void CAESinkPi::Deinitialize()
162 {
163   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
164   if (m_Initialized)
165   {
166     m_omx_render.FlushAll();
167     m_omx_render.Deinitialize();
168     m_Initialized = false;
169   }
170 }
171
172 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
173 {
174   bool compatible =
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);
181   return compatible;
182 }
183
184 double CAESinkPi::GetDelay()
185 {
186   OMX_PARAM_U32TYPE param;
187   OMX_INIT_STRUCTURE(param);
188
189   if (!m_Initialized)
190     return 0.0;
191
192   param.nPortIndex = m_omx_render.GetInputPort();
193
194   OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, &param);
195
196   if (omx_err != OMX_ErrorNone)
197   {
198     CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
199       CLASSNAME, __func__, omx_err);
200   }
201   double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
202   return sinkbuffer_seconds_to_empty;
203 }
204
205 double CAESinkPi::GetCacheTime()
206 {
207   return GetDelay();
208 }
209
210 double CAESinkPi::GetCacheTotal()
211 {
212   double audioplus_buffer = AUDIO_PLAYBUFFER;
213   return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size + audioplus_buffer;
214 }
215
216 unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
217 {
218   unsigned int sent = 0;
219
220   if (!m_Initialized)
221     return frames;
222
223   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
224   OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
225   while (sent < frames)
226   {
227     int timeout = blocking ? 1000 : 0;
228
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);
233
234     if (omx_buffer == NULL)
235     {
236       if (too_laggy)
237       {
238         Sleep((int)((delay - AUDIO_PLAYBUFFER) * 1000.0));
239         continue;
240       }
241       if (blocking)
242         CLog::Log(LOGERROR, "COMXAudio::Decode timeout");
243       break;
244     }
245
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;
251
252     if (sent == frames)
253       omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
254
255     if (delay <= 0.0 && m_submitted)
256       CLog::Log(LOGERROR, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
257
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;
262   }
263
264   return sent;
265 }
266
267 void CAESinkPi::Drain()
268 {
269   int delay = (int)(GetDelay() * 1000.0);
270   if (delay)
271     Sleep(delay);
272   CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(GetDelay() * 1000.0));
273 }
274
275 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
276 {
277   m_info.m_channels.Reset();
278   m_info.m_dataFormats.clear();
279   m_info.m_sampleRates.clear();
280
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);
293
294   list.push_back(m_info);
295
296   m_info.m_channels.Reset();
297   m_info.m_dataFormats.clear();
298   m_info.m_sampleRates.clear();
299
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);
308
309   list.push_back(m_info);
310 }
311
312 #endif