[PiSink] Support planar 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 (0.1) // 100ms
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 static void SetAudioProps(bool stream_channels, uint32_t channel_map)
69 {
70   char command[80], response[80];
71
72   sprintf(command, "hdmi_stream_channels %d", stream_channels ? 1 : 0);
73   vc_gencmd(response, sizeof response, command);
74
75   sprintf(command, "hdmi_channel_map 0x%08x", channel_map);
76   vc_gencmd(response, sizeof response, command);
77
78   CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map);
79 }
80
81 static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
82 {
83   unsigned int channels = format.m_channelLayout.Count();
84   uint32_t channel_map = 0;
85   if (passthrough)
86     return 0;
87
88   static const unsigned char map_normal[] =
89   {
90     0, //AE_CH_RAW ,
91     1, //AE_CH_FL
92     2, //AE_CH_FR
93     4, //AE_CH_FC
94     3, //AE_CH_LFE
95     7, //AE_CH_BL
96     8, //AE_CH_BR
97     1, //AE_CH_FLOC,
98     2, //AE_CH_FROC,
99     4, //AE_CH_BC,
100     5, //AE_CH_SL
101     6, //AE_CH_SR
102   };
103   static const unsigned char map_back[] =
104   {
105     0, //AE_CH_RAW ,
106     1, //AE_CH_FL
107     2, //AE_CH_FR
108     4, //AE_CH_FC
109     3, //AE_CH_LFE
110     5, //AE_CH_BL
111     6, //AE_CH_BR
112     1, //AE_CH_FLOC,
113     2, //AE_CH_FROC,
114     4, //AE_CH_BC,
115     5, //AE_CH_SL
116     6, //AE_CH_SR
117   };
118   const unsigned char *map = map_normal;
119   // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
120   // but no BR BL channels, we use the wide map in order to open only the num of channels really
121   // needed.
122   if (format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_SL))
123     map = map_back;
124
125   for (unsigned int i = 0; i < channels; ++i)
126   {
127     AEChannel c = format.m_channelLayout[i];
128     unsigned int chan = 0;
129     if ((unsigned int)c < sizeof map_normal / sizeof *map_normal)
130       chan = map[(unsigned int)c];
131     if (chan > 0)
132       channel_map |= (chan-1) << (3*i);
133   }
134   // These numbers are from Table 28 Audio InfoFrame Data byte 4 of CEA 861
135   // and describe the speaker layout
136   static const uint8_t cea_map[] = {
137     0xff, // 0
138     0xff, // 1
139     0x00, // 2.0
140     0x02, // 3.0
141     0x08, // 4.0
142     0x0a, // 5.0
143     0xff, // 6
144     0x12, // 7.0
145     0xff, // 8
146   };
147   static const uint8_t cea_map_lfe[] = {
148     0xff, // 0
149     0xff, // 1
150     0xff, // 2
151     0x01, // 2.1
152     0x03, // 3.1
153     0x09, // 4.1
154     0x0b, // 5.1
155     0xff, // 7
156     0x13, // 7.1
157   };
158   uint8_t cea = format.m_channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
159   if (cea == 0xff)
160     CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, format.m_channelLayout.HasChannel(AE_CH_LFE), channels);
161
162   channel_map |= cea << 24;
163
164   return channel_map;
165 }
166
167 bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
168 {
169   // This may be called before Application calls g_RBP.Initialise, so call it here too
170   g_RBP.Initialize();
171
172   /* if we are raw need to let gpu know */
173   m_passthrough = AE_IS_RAW(format.m_dataFormat);
174
175   m_initDevice = device;
176   m_initFormat = format;
177
178   // analogue only supports stereo
179   if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
180     format.m_channelLayout = AE_CH_LAYOUT_2_0;
181
182   // setup for a 50ms sink feed from SoftAE
183   if (format.m_dataFormat != AE_FMT_FLOATP && format.m_dataFormat != AE_FMT_FLOAT &&
184       format.m_dataFormat != AE_FMT_S32NE && format.m_dataFormat != AE_FMT_S32NEP && format.m_dataFormat != AE_FMT_S32LE &&
185       format.m_dataFormat != AE_FMT_S16NE && format.m_dataFormat != AE_FMT_S16NEP && format.m_dataFormat != AE_FMT_S16LE)
186     format.m_dataFormat = AE_FMT_S16LE;
187   unsigned int channels    = format.m_channelLayout.Count();
188   unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
189   format.m_frameSize     = sample_size * channels;
190   format.m_sampleRate    = std::max(8000U, std::min(192000U, format.m_sampleRate));
191   format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER / NUM_OMX_BUFFERS;
192   format.m_frameSamples  = format.m_frames * channels;
193
194   SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough));
195
196   m_format = format;
197   m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
198
199   CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
200                 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);
201
202   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
203
204   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
205
206   if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
207     CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
208
209   OMX_INIT_STRUCTURE(m_pcm_input);
210   m_pcm_input.nPortIndex            = m_omx_render.GetInputPort();
211   m_pcm_input.eNumData              = OMX_NumericalDataSigned;
212   m_pcm_input.eEndian               = OMX_EndianLittle;
213   m_pcm_input.bInterleaved          = OMX_TRUE;
214   m_pcm_input.nBitPerSample         = sample_size * 8;
215   // 0x8000 = float, 0x10000 = planar
216   uint32_t flags = 0;
217   if (m_format.m_dataFormat == AE_FMT_FLOAT || m_format.m_dataFormat == AE_FMT_FLOATP)
218    flags |= 0x8000;
219   if (AE_IS_PLANAR(m_format.m_dataFormat))
220    flags |= 0x10000;
221   m_pcm_input.ePCMMode              = flags == 0 ? OMX_AUDIO_PCMModeLinear : (OMX_AUDIO_PCMMODETYPE)flags;
222   m_pcm_input.nChannels             = channels;
223   m_pcm_input.nSamplingRate         = m_format.m_sampleRate;
224
225   omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
226   if (omx_err != OMX_ErrorNone)
227     CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
228
229   m_omx_render.ResetEos();
230
231   SetAudioDest();
232
233   // set up the number/size of buffers for decoder input
234   OMX_PARAM_PORTDEFINITIONTYPE port_param;
235   OMX_INIT_STRUCTURE(port_param);
236   port_param.nPortIndex = m_omx_render.GetInputPort();
237
238   omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
239   if (omx_err != OMX_ErrorNone)
240     CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
241
242   port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
243   port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames;
244
245   omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
246   if (omx_err != OMX_ErrorNone)
247     CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
248
249   omx_err = m_omx_render.AllocInputBuffers();
250   if (omx_err != OMX_ErrorNone)
251     CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
252
253   omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
254   if (omx_err != OMX_ErrorNone)
255     CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
256
257   m_Initialized = true;
258   return true;
259 }
260
261
262 void CAESinkPi::Deinitialize()
263 {
264   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
265   SetAudioProps(false, 0);
266   if (m_Initialized)
267   {
268     m_omx_render.FlushAll();
269     m_omx_render.Deinitialize();
270     m_Initialized = false;
271   }
272 }
273
274 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
275 {
276   bool compatible =
277       /* compare against the requested format and the real format */
278       (m_initFormat.m_sampleRate    == format.m_sampleRate    || m_format.m_sampleRate    == format.m_sampleRate   ) &&
279       (m_initFormat.m_dataFormat    == format.m_dataFormat    || m_format.m_dataFormat    == format.m_dataFormat   ) &&
280       (m_initFormat.m_channelLayout == format.m_channelLayout || m_format.m_channelLayout == format.m_channelLayout) &&
281       (m_initDevice == device);
282   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);
283   return compatible;
284 }
285
286 void CAESinkPi::GetDelay(AEDelayStatus& status)
287 {
288   OMX_PARAM_U32TYPE param;
289   OMX_INIT_STRUCTURE(param);
290
291   if (!m_Initialized)
292   {
293     status.SetDelay(0);
294     return;
295   }
296
297   param.nPortIndex = m_omx_render.GetInputPort();
298
299   OMX_ERRORTYPE omx_err = m_omx_render.GetConfig(OMX_IndexConfigAudioRenderingLatency, &param);
300
301   if (omx_err != OMX_ErrorNone)
302   {
303     CLog::Log(LOGERROR, "%s::%s - error getting OMX_IndexConfigAudioRenderingLatency error 0x%08x",
304       CLASSNAME, __func__, omx_err);
305   }
306   double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * param.nU32 * m_format.m_frameSize;
307   status.SetDelay(sinkbuffer_seconds_to_empty);
308 }
309
310 double CAESinkPi::GetCacheTotal()
311 {
312   return AUDIO_PLAYBUFFER;
313 }
314
315 unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
316 {
317   if (!m_Initialized || !frames)
318     return frames;
319
320   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
321   OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
322
323   unsigned int channels    = m_format.m_channelLayout.Count();
324   unsigned int sample_size = CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3;
325   const int planes = AE_IS_PLANAR(m_format.m_dataFormat) ? channels : 1;
326   const int chans  = AE_IS_PLANAR(m_format.m_dataFormat) ? 1 : channels;
327   const int pitch  = chans * sample_size;
328
329   AEDelayStatus status;
330   GetDelay(status);
331   double delay = status.GetDelay();
332   if (delay <= 0.0 && m_submitted)
333     CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
334
335   omx_buffer = m_omx_render.GetInputBuffer(1000);
336   if (omx_buffer == NULL)
337   {
338     CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout");
339     return 0;
340   }
341
342   omx_buffer->nFilledLen = frames * m_format.m_frameSize;
343   // must be true
344   assert(omx_buffer->nFilledLen <= omx_buffer->nAllocLen);
345   omx_buffer->nTimeStamp = ToOMXTime(0);
346   omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
347
348   if (omx_buffer->nFilledLen)
349   {
350     int planesize = omx_buffer->nFilledLen / planes;
351     for (int i=0; i < planes; i++)
352       memcpy((uint8_t *)omx_buffer->pBuffer + i * planesize, data[i] + offset * pitch, planesize);
353   }
354   omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
355   if (omx_err != OMX_ErrorNone)
356     CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
357   m_submitted++;
358   GetDelay(status);
359   delay = status.GetDelay();
360   if (delay > AUDIO_PLAYBUFFER)
361     Sleep((int)(1000.0f * (delay - AUDIO_PLAYBUFFER)));
362   return frames;
363 }
364
365 void CAESinkPi::Drain()
366 {
367   AEDelayStatus status;
368   GetDelay(status);
369   int delay = (int)(status.GetDelay() * 1000.0);
370   if (delay)
371     Sleep(delay);
372   CLog::Log(LOGDEBUG, "%s:%s delay:%dms now:%dms", CLASSNAME, __func__, delay, (int)(status.GetDelay() * 1000.0));
373 }
374
375 void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
376 {
377   m_info.m_channels.Reset();
378   m_info.m_dataFormats.clear();
379   m_info.m_sampleRates.clear();
380
381   m_info.m_deviceType = AE_DEVTYPE_HDMI;
382   m_info.m_deviceName = "HDMI";
383   m_info.m_displayName = "HDMI";
384   m_info.m_displayNameExtra = "";
385   m_info.m_channels += AE_CH_FL;
386   m_info.m_channels += AE_CH_FR;
387   for (unsigned int i=0; i<sizeof PassthroughSampleRates/sizeof *PassthroughSampleRates; i++)
388     m_info.m_sampleRates.push_back(PassthroughSampleRates[i]);
389   m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
390   m_info.m_dataFormats.push_back(AE_FMT_S32NE);
391   m_info.m_dataFormats.push_back(AE_FMT_S16NE);
392   m_info.m_dataFormats.push_back(AE_FMT_S32LE);
393   m_info.m_dataFormats.push_back(AE_FMT_S16LE);
394   m_info.m_dataFormats.push_back(AE_FMT_FLOATP);
395   m_info.m_dataFormats.push_back(AE_FMT_S32NEP);
396   m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
397   m_info.m_dataFormats.push_back(AE_FMT_AC3);
398   m_info.m_dataFormats.push_back(AE_FMT_DTS);
399   m_info.m_dataFormats.push_back(AE_FMT_EAC3);
400
401   list.push_back(m_info);
402
403   m_info.m_channels.Reset();
404   m_info.m_dataFormats.clear();
405   m_info.m_sampleRates.clear();
406
407   m_info.m_deviceType = AE_DEVTYPE_PCM;
408   m_info.m_deviceName = "Analogue";
409   m_info.m_displayName = "Analogue";
410   m_info.m_displayNameExtra = "";
411   m_info.m_channels += AE_CH_FL;
412   m_info.m_channels += AE_CH_FR;
413   m_info.m_sampleRates.push_back(48000);
414   m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
415   m_info.m_dataFormats.push_back(AE_FMT_S32LE);
416   m_info.m_dataFormats.push_back(AE_FMT_S16LE);
417   m_info.m_dataFormats.push_back(AE_FMT_FLOATP);
418   m_info.m_dataFormats.push_back(AE_FMT_S32NEP);
419   m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
420
421   list.push_back(m_info);
422 }
423
424 #endif