Merge pull request #1129 from jmarshallnz/remove_smb_auth_details_in_add_source
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkOSS.cpp
1 /*
2  *      Copyright (C) 2010-2012 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "AESinkOSS.h"
23 #include <stdint.h>
24 #include <limits.h>
25
26 #include "Utils/AEUtil.h"
27 #include "utils/StdString.h"
28 #include "utils/log.h"
29 #include "threads/SingleLock.h"
30 #include <sstream>
31
32 #include <sys/ioctl.h>
33
34 #if defined(OSS4) || defined(__FreeBSD__)
35   #include <sys/soundcard.h>
36 #else
37   #include <linux/soundcard.h>
38 #endif
39
40 #define OSS_FRAMES 256
41
42 static enum AEChannel OSSChannelMap[9] =
43   {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_FC, AE_CH_LFE, AE_CH_SL, AE_CH_SR, AE_CH_NULL};
44
45 #if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
46 static int OSSSampleRateList[] =
47 {
48   5512,
49   8000,
50   11025,
51   16000,
52   22050,
53   32000,
54   44100,
55   48000,
56   64000,
57   88200,
58   96000,
59   176400,
60   192000,
61   384000,
62   0
63 };
64 #endif
65
66 CAESinkOSS::CAESinkOSS()
67 {
68 }
69
70 CAESinkOSS::~CAESinkOSS()
71 {
72   Deinitialize();
73 }
74
75 std::string CAESinkOSS::GetDeviceUse(const AEAudioFormat format, const std::string device)
76 {
77 #ifdef OSS4
78   if (AE_IS_RAW(format.m_dataFormat))
79   {
80     if (device.find_first_of('/') != 0)
81       return "/dev/dsp_ac3";
82     return device;
83   }
84
85   if (device.find_first_of('/') != 0)
86     return "/dev/dsp_multich";
87 #else
88   if (device.find_first_of('/') != 0)
89     return "/dev/dsp";
90 #endif
91
92   return device;
93 }
94
95 bool CAESinkOSS::Initialize(AEAudioFormat &format, std::string &device)
96 {
97   m_initFormat = format;
98   format.m_channelLayout = GetChannelLayout(format);
99   device = GetDeviceUse(format, device);
100
101 #ifdef __linux__
102   /* try to open in exclusive mode first (no software mixing) */
103   m_fd = open(device.c_str(), O_WRONLY | O_EXCL, 0);
104   if (!m_fd)
105 #endif
106     m_fd = open(device.c_str(), O_WRONLY, 0);
107   if (!m_fd)
108   {
109     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to open the audio device: %s", device.c_str());
110     return false;
111   }
112
113   int format_mask;
114   if (ioctl(m_fd, SNDCTL_DSP_GETFMTS, &format_mask) == -1)
115   {
116     close(m_fd);
117     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get supported formats, assuming AFMT_S16_NE");
118     return false;
119   }
120
121 #ifdef OSS4
122   bool useCooked = true;
123 #endif
124
125   int oss_fmt = 0;
126 #ifdef AFMT_FLOAT
127   if ((format.m_dataFormat == AE_FMT_FLOAT) && (format_mask & AFMT_FLOAT ))
128     oss_fmt = AFMT_FLOAT;
129   else
130 #endif
131 #ifdef AFMT_S32_NE
132   if ((format.m_dataFormat == AE_FMT_S32NE) && (format_mask & AFMT_S32_NE))
133     oss_fmt = AFMT_S32_NE;
134   else if ((format.m_dataFormat == AE_FMT_S32BE) && (format_mask & AFMT_S32_BE))
135     oss_fmt = AFMT_S32_BE;
136   else if ((format.m_dataFormat == AE_FMT_S32LE) && (format_mask & AFMT_S32_LE))
137     oss_fmt = AFMT_S32_LE;
138   else
139 #endif
140   if ((format.m_dataFormat == AE_FMT_S16NE) && (format_mask & AFMT_S16_NE))
141     oss_fmt = AFMT_S16_NE;
142   else if ((format.m_dataFormat == AE_FMT_S16BE) && (format_mask & AFMT_S16_BE))
143     oss_fmt = AFMT_S16_BE;
144   else if ((format.m_dataFormat == AE_FMT_S16LE) && (format_mask & AFMT_S16_LE))
145     oss_fmt = AFMT_S16_LE;
146   else if ((format.m_dataFormat == AE_FMT_S8   ) && (format_mask & AFMT_S8    ))
147     oss_fmt = AFMT_S8;
148   else if ((format.m_dataFormat == AE_FMT_U8   ) && (format_mask & AFMT_U8    ))
149     oss_fmt = AFMT_U8;
150   else if ((AE_IS_RAW(format.m_dataFormat)     ) && (format_mask & AFMT_AC3   ))
151     oss_fmt = AFMT_AC3;
152   else if (AE_IS_RAW(format.m_dataFormat))
153   {
154     close(m_fd);
155     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable RAW output format");
156     return false;
157   }
158   else
159   {
160     CLog::Log(LOGINFO, "CAESinkOSS::Initialize - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));
161
162     /* fallback to the best supported format */
163 #ifdef AFMT_FLOAT
164     if (format_mask & AFMT_FLOAT )
165     {
166       oss_fmt = AFMT_FLOAT;
167       format.m_dataFormat = AE_FMT_FLOAT;
168     }
169     else
170 #endif
171 #ifdef AFMT_S32_NE
172     if (format_mask & AFMT_S32_NE)
173     {
174       oss_fmt = AFMT_S32_NE;
175       format.m_dataFormat = AE_FMT_S32NE;
176     }
177     else if (format_mask & AFMT_S32_BE)
178     {
179       oss_fmt = AFMT_S32_BE;
180       format.m_dataFormat = AE_FMT_S32BE;
181     }
182     else if (format_mask & AFMT_S32_LE)
183     {
184       oss_fmt = AFMT_S32_LE;
185       format.m_dataFormat = AE_FMT_S32LE;
186     }
187     else
188 #endif
189     if (format_mask & AFMT_S16_NE)
190     {
191       oss_fmt = AFMT_S16_NE;
192       format.m_dataFormat = AE_FMT_S16NE;
193     }
194     else if (format_mask & AFMT_S16_BE)
195     {
196       oss_fmt = AFMT_S16_BE;
197       format.m_dataFormat = AE_FMT_S16BE;
198     }
199     else if (format_mask & AFMT_S16_LE)
200     {
201       oss_fmt = AFMT_S16_LE;
202       format.m_dataFormat = AE_FMT_S16LE;
203     }
204     else if (format_mask & AFMT_S8    )
205     {
206       oss_fmt = AFMT_S8;
207       format.m_dataFormat = AE_FMT_S8;
208     }
209     else if (format_mask & AFMT_U8    )
210     {
211       oss_fmt = AFMT_U8;
212       format.m_dataFormat = AE_FMT_U8;
213     }
214     else
215     {
216       CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable native output format, will try to use AE_FMT_S16NE anyway");
217       oss_fmt             = AFMT_S16_NE;
218       format.m_dataFormat = AE_FMT_S16NE;
219 #ifdef OSS4
220       /* dont use cooked if we did not find a native format, OSS might be able to convert */
221       useCooked           = false;
222 #endif
223     }
224   }
225
226 #ifdef OSS4
227   if (useCooked)
228   {
229     int oss_cooked = 1;
230     if (ioctl(m_fd, SNDCTL_DSP_COOKEDMODE, &oss_cooked) == -1)
231       CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set cooked mode");
232   }
233 #endif
234
235   if (ioctl(m_fd, SNDCTL_DSP_SETFMT, &oss_fmt) == -1)
236   {
237     close(m_fd);
238     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the data format (%s)", CAEUtil::DataFormatToStr(format.m_dataFormat));
239     return false;
240   }
241
242 #ifndef OSS4
243 #ifndef __FreeBSD__
244   int mask = 0;
245   for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
246     switch (format.m_channelLayout[i])
247     {
248       case AE_CH_FL:
249       case AE_CH_FR:
250         mask |= DSP_BIND_FRONT;
251         break;
252
253       case AE_CH_BL:
254       case AE_CH_BR:
255         mask |= DSP_BIND_SURR;
256         break;
257
258       case AE_CH_FC:
259       case AE_CH_LFE:
260         mask |= DSP_BIND_CENTER_LFE;
261         break;
262
263       default:
264         break;
265     }
266
267   /* try to set the channel mask, not all cards support this */
268   if (ioctl(m_fd, SNDCTL_DSP_BIND_CHANNEL, &mask) == -1)
269   {
270     CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the channel mask");
271     /* get the configured channel mask */
272     if (ioctl(m_fd, SNDCTL_DSP_GETCHANNELMASK, &mask) == -1)
273     {
274       /* as not all cards support this so we just assume stereo if it fails */
275       CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel mask, assuming stereo");
276       mask = DSP_BIND_FRONT;
277     }
278   }
279 #endif
280 #else /* OSS4 */
281   unsigned long long order = 0;
282   for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
283     switch (format.m_channelLayout[i])
284     {
285       case AE_CH_FL : order = (order << 4) | CHID_L  ; break;
286       case AE_CH_FR : order = (order << 4) | CHID_R  ; break;
287       case AE_CH_FC : order = (order << 4) | CHID_C  ; break;
288       case AE_CH_LFE: order = (order << 4) | CHID_LFE; break;
289       case AE_CH_SL : order = (order << 4) | CHID_LS ; break;
290       case AE_CH_SR : order = (order << 4) | CHID_RS ; break;
291       case AE_CH_BL : order = (order << 4) | CHID_LR ; break;
292       case AE_CH_BR : order = (order << 4) | CHID_RR ; break;
293
294       default:
295         continue;
296     }
297
298   if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1)
299   {
300     if (ioctl(m_fd, SNDCTL_DSP_GET_CHNORDER, &order) == -1)
301     {
302       CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel order, assuming CHNORDER_NORMAL");
303       order = CHNORDER_NORMAL;
304     }
305   }
306
307 #endif
308
309   /* find the number we need to open to access the channels we need */
310   bool found = false;
311   int oss_ch = 0;
312   for (int ch = format.m_channelLayout.Count(); ch < 9; ++ch)
313   {
314     oss_ch = ch;
315     if (ioctl(m_fd, SNDCTL_DSP_CHANNELS, &oss_ch) != -1 && oss_ch >= (int)format.m_channelLayout.Count())
316     {
317       found = true;
318       break;
319     }
320   }
321
322   if (!found)
323     CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to access the number of channels required, falling back");
324
325   int tmp = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count() * OSS_FRAMES;
326   int pos = 0;
327   while ((tmp & 0x1) == 0x0)
328   {
329     tmp = tmp >> 1;
330     ++pos;
331   }
332
333   int oss_frag = (4 << 16) | pos;
334   if (ioctl(m_fd, SNDCTL_DSP_SETFRAGMENT, &oss_frag) == -1)
335     CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the fragment size");
336
337   int oss_sr = format.m_sampleRate;
338   if (ioctl(m_fd, SNDCTL_DSP_SPEED, &oss_sr) == -1)
339   {
340     close(m_fd);
341     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the sample rate");
342     return false;
343   }
344
345   audio_buf_info bi;
346   if (ioctl(m_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
347   {
348     close(m_fd);
349     CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get the output buffer size");
350     return false;
351   }
352
353   format.m_sampleRate    = oss_sr;
354   format.m_frameSize     = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count();
355   format.m_frames        = bi.fragsize / format.m_frameSize;
356   format.m_frameSamples  = format.m_frames * format.m_channelLayout.Count();
357
358   m_device = device;
359   m_format = format;
360   return true;
361 }
362
363 void CAESinkOSS::Deinitialize()
364 {
365   Stop();
366
367   if (m_fd)
368     close(m_fd);
369 }
370
371 inline CAEChannelInfo CAESinkOSS::GetChannelLayout(AEAudioFormat format)
372 {
373   unsigned int count = 0;
374
375        if (format.m_dataFormat == AE_FMT_AC3 ||
376            format.m_dataFormat == AE_FMT_DTS ||
377            format.m_dataFormat == AE_FMT_EAC3)
378            count = 2;
379   else if (format.m_dataFormat == AE_FMT_TRUEHD ||
380            format.m_dataFormat == AE_FMT_DTSHD)
381            count = 8;
382   else
383   {
384     for (unsigned int c = 0; c < 8; ++c)
385       for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
386         if (format.m_channelLayout[i] == OSSChannelMap[c])
387         {
388           count = c + 1;
389           break;
390         }
391   }
392
393   CAEChannelInfo info;
394   for (unsigned int i = 0; i < count; ++i)
395     info += OSSChannelMap[i];
396
397   return info;
398 }
399
400 bool CAESinkOSS::IsCompatible(const AEAudioFormat format, const std::string device)
401 {
402   AEAudioFormat tmp  = format;
403   tmp.m_channelLayout = GetChannelLayout(format);
404
405   return (
406     tmp.m_sampleRate    == m_initFormat.m_sampleRate    &&
407     tmp.m_dataFormat    == m_initFormat.m_dataFormat    &&
408     tmp.m_channelLayout == m_initFormat.m_channelLayout &&
409     GetDeviceUse(tmp, device) == m_device
410   );
411 }
412
413 void CAESinkOSS::Stop()
414 {
415 #ifdef SNDCTL_DSP_RESET
416   if (m_fd)
417     ioctl(m_fd, SNDCTL_DSP_RESET, NULL);
418 #endif
419 }
420
421 double CAESinkOSS::GetDelay()
422 {
423   int delay;
424   if (ioctl(m_fd, SNDCTL_DSP_GETODELAY, &delay) == -1)
425     return 0.0f;
426
427   return (double)delay / (m_format.m_frameSize * m_format.m_sampleRate);
428 }
429
430 unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
431 {
432   int size = frames * m_format.m_frameSize;
433   int wrote = write(m_fd, data, size);
434   if (wrote < 0)
435   {
436     CLog::Log(LOGERROR, "CAESinkOSS::AddPackets - Failed to write");
437     return frames;
438   }
439
440   return wrote / m_format.m_frameSize;
441 }
442
443 void CAESinkOSS::Drain()
444 {
445   if (!m_fd)
446     return;
447
448   // ???
449 }
450
451 void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list)
452 {
453   int mixerfd;
454   const char * mixerdev = "/dev/mixer";
455
456   if ((mixerfd = open(mixerdev, O_RDWR, 0)) == -1)
457   {
458     CLog::Log(LOGERROR,
459           "CAESinkOSS::EnumerateDevicesEx - Failed to open mixer: %s", mixerdev);
460     return;
461   }     
462
463 #if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
464   oss_sysinfo sysinfo;
465   if (ioctl(mixerfd, SNDCTL_SYSINFO, &sysinfo) == -1)
466   {
467     // hardware not supported
468         // OSSv4 required ?
469     close(mixerfd);
470     return;
471   }
472
473   for (int i = 0; i < sysinfo.numcards; ++i)
474   {
475     std::stringstream devicepath;
476     std::stringstream devicename;
477     CAEDeviceInfo info;
478     oss_card_info cardinfo;
479
480     devicepath << "/dev/dsp" << i;
481     info.m_deviceName = devicepath.str();
482
483     cardinfo.card = i;
484     if (ioctl(mixerfd, SNDCTL_CARDINFO, &cardinfo) == -1)
485       break;
486
487     devicename << cardinfo.shortname << " " << cardinfo.longname;
488     info.m_displayName = devicename.str();
489
490     if (info.m_displayName.find("HDMI") != std::string::npos)
491       info.m_deviceType = AE_DEVTYPE_HDMI;
492     else if (info.m_displayName.find("Digital") != std::string::npos)
493       info.m_deviceType = AE_DEVTYPE_IEC958;
494     else
495       info.m_deviceType = AE_DEVTYPE_PCM;
496  
497     oss_audioinfo ainfo;
498     memset(&ainfo, 0, sizeof(ainfo));
499     ainfo.dev = i;
500     if (ioctl(mixerfd, SNDCTL_AUDIOINFO, &ainfo) != -1) {
501 #if 0
502       if (ainfo.oformats & AFMT_S32_LE)
503         info.m_dataFormats.push_back(AE_FMT_S32LE);
504       if (ainfo.oformats & AFMT_S16_LE)
505         info.m_dataFormats.push_back(AE_FMT_S16LE);
506 #endif
507       for (int j = 0;
508         j < ainfo.max_channels && AE_CH_NULL != OSSChannelMap[j];
509         ++j)
510           info.m_channels += OSSChannelMap[j];
511
512       for (int *rate = OSSSampleRateList; *rate != 0; ++rate)
513         if (*rate >= ainfo.min_rate && *rate <= ainfo.max_rate)
514           info.m_sampleRates.push_back(*rate);
515     }
516     list.push_back(info);
517   }
518
519   close(mixerfd);
520 #endif
521 }
522