2 * Copyright (C) 2010-2012 Team XBMC
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)
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.
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
22 #include "AESinkOSS.h"
26 #include "Utils/AEUtil.h"
27 #include "utils/StdString.h"
28 #include "utils/log.h"
29 #include "threads/SingleLock.h"
32 #include <sys/ioctl.h>
34 #if defined(OSS4) || defined(__FreeBSD__)
35 #include <sys/soundcard.h>
37 #include <linux/soundcard.h>
40 #define OSS_FRAMES 256
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};
45 #if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
46 static int OSSSampleRateList[] =
66 CAESinkOSS::CAESinkOSS()
70 CAESinkOSS::~CAESinkOSS()
75 std::string CAESinkOSS::GetDeviceUse(const AEAudioFormat format, const std::string device)
78 if (AE_IS_RAW(format.m_dataFormat))
80 if (device.find_first_of('/') != 0)
81 return "/dev/dsp_ac3";
85 if (device.find_first_of('/') != 0)
86 return "/dev/dsp_multich";
88 if (device.find_first_of('/') != 0)
95 bool CAESinkOSS::Initialize(AEAudioFormat &format, std::string &device)
97 m_initFormat = format;
98 format.m_channelLayout = GetChannelLayout(format);
99 device = GetDeviceUse(format, device);
102 /* try to open in exclusive mode first (no software mixing) */
103 m_fd = open(device.c_str(), O_WRONLY | O_EXCL, 0);
106 m_fd = open(device.c_str(), O_WRONLY, 0);
109 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to open the audio device: %s", device.c_str());
114 if (ioctl(m_fd, SNDCTL_DSP_GETFMTS, &format_mask) == -1)
117 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get supported formats, assuming AFMT_S16_NE");
122 bool useCooked = true;
127 if ((format.m_dataFormat == AE_FMT_FLOAT) && (format_mask & AFMT_FLOAT ))
128 oss_fmt = AFMT_FLOAT;
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;
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 ))
148 else if ((format.m_dataFormat == AE_FMT_U8 ) && (format_mask & AFMT_U8 ))
150 else if ((AE_IS_RAW(format.m_dataFormat) ) && (format_mask & AFMT_AC3 ))
152 else if (AE_IS_RAW(format.m_dataFormat))
155 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable RAW output format");
160 CLog::Log(LOGINFO, "CAESinkOSS::Initialize - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat));
162 /* fallback to the best supported format */
164 if (format_mask & AFMT_FLOAT )
166 oss_fmt = AFMT_FLOAT;
167 format.m_dataFormat = AE_FMT_FLOAT;
172 if (format_mask & AFMT_S32_NE)
174 oss_fmt = AFMT_S32_NE;
175 format.m_dataFormat = AE_FMT_S32NE;
177 else if (format_mask & AFMT_S32_BE)
179 oss_fmt = AFMT_S32_BE;
180 format.m_dataFormat = AE_FMT_S32BE;
182 else if (format_mask & AFMT_S32_LE)
184 oss_fmt = AFMT_S32_LE;
185 format.m_dataFormat = AE_FMT_S32LE;
189 if (format_mask & AFMT_S16_NE)
191 oss_fmt = AFMT_S16_NE;
192 format.m_dataFormat = AE_FMT_S16NE;
194 else if (format_mask & AFMT_S16_BE)
196 oss_fmt = AFMT_S16_BE;
197 format.m_dataFormat = AE_FMT_S16BE;
199 else if (format_mask & AFMT_S16_LE)
201 oss_fmt = AFMT_S16_LE;
202 format.m_dataFormat = AE_FMT_S16LE;
204 else if (format_mask & AFMT_S8 )
207 format.m_dataFormat = AE_FMT_S8;
209 else if (format_mask & AFMT_U8 )
212 format.m_dataFormat = AE_FMT_U8;
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;
220 /* dont use cooked if we did not find a native format, OSS might be able to convert */
230 if (ioctl(m_fd, SNDCTL_DSP_COOKEDMODE, &oss_cooked) == -1)
231 CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set cooked mode");
235 if (ioctl(m_fd, SNDCTL_DSP_SETFMT, &oss_fmt) == -1)
238 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the data format (%s)", CAEUtil::DataFormatToStr(format.m_dataFormat));
245 for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
246 switch (format.m_channelLayout[i])
250 mask |= DSP_BIND_FRONT;
255 mask |= DSP_BIND_SURR;
260 mask |= DSP_BIND_CENTER_LFE;
267 /* try to set the channel mask, not all cards support this */
268 if (ioctl(m_fd, SNDCTL_DSP_BIND_CHANNEL, &mask) == -1)
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)
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;
281 unsigned long long order = 0;
282 for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
283 switch (format.m_channelLayout[i])
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;
298 if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1)
300 if (ioctl(m_fd, SNDCTL_DSP_GET_CHNORDER, &order) == -1)
302 CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel order, assuming CHNORDER_NORMAL");
303 order = CHNORDER_NORMAL;
309 /* find the number we need to open to access the channels we need */
312 for (int ch = format.m_channelLayout.Count(); ch < 9; ++ch)
315 if (ioctl(m_fd, SNDCTL_DSP_CHANNELS, &oss_ch) != -1 && oss_ch >= (int)format.m_channelLayout.Count())
323 CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to access the number of channels required, falling back");
325 int tmp = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count() * OSS_FRAMES;
327 while ((tmp & 0x1) == 0x0)
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");
337 int oss_sr = format.m_sampleRate;
338 if (ioctl(m_fd, SNDCTL_DSP_SPEED, &oss_sr) == -1)
341 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the sample rate");
346 if (ioctl(m_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1)
349 CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get the output buffer size");
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();
363 void CAESinkOSS::Deinitialize()
371 inline CAEChannelInfo CAESinkOSS::GetChannelLayout(AEAudioFormat format)
373 unsigned int count = 0;
375 if (format.m_dataFormat == AE_FMT_AC3 ||
376 format.m_dataFormat == AE_FMT_DTS ||
377 format.m_dataFormat == AE_FMT_EAC3)
379 else if (format.m_dataFormat == AE_FMT_TRUEHD ||
380 format.m_dataFormat == AE_FMT_DTSHD)
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])
394 for (unsigned int i = 0; i < count; ++i)
395 info += OSSChannelMap[i];
400 bool CAESinkOSS::IsCompatible(const AEAudioFormat format, const std::string device)
402 AEAudioFormat tmp = format;
403 tmp.m_channelLayout = GetChannelLayout(format);
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
413 void CAESinkOSS::Stop()
415 #ifdef SNDCTL_DSP_RESET
417 ioctl(m_fd, SNDCTL_DSP_RESET, NULL);
421 double CAESinkOSS::GetDelay()
424 if (ioctl(m_fd, SNDCTL_DSP_GETODELAY, &delay) == -1)
427 return (double)delay / (m_format.m_frameSize * m_format.m_sampleRate);
430 unsigned int CAESinkOSS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
432 int size = frames * m_format.m_frameSize;
433 int wrote = write(m_fd, data, size);
436 CLog::Log(LOGERROR, "CAESinkOSS::AddPackets - Failed to write");
440 return wrote / m_format.m_frameSize;
443 void CAESinkOSS::Drain()
451 void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list)
454 const char * mixerdev = "/dev/mixer";
456 if ((mixerfd = open(mixerdev, O_RDWR, 0)) == -1)
459 "CAESinkOSS::EnumerateDevicesEx - Failed to open mixer: %s", mixerdev);
463 #if defined(SNDCTL_SYSINFO) && defined(SNDCTL_CARDINFO)
465 if (ioctl(mixerfd, SNDCTL_SYSINFO, &sysinfo) == -1)
467 // hardware not supported
473 for (int i = 0; i < sysinfo.numcards; ++i)
475 std::stringstream devicepath;
476 std::stringstream devicename;
478 oss_card_info cardinfo;
480 devicepath << "/dev/dsp" << i;
481 info.m_deviceName = devicepath.str();
484 if (ioctl(mixerfd, SNDCTL_CARDINFO, &cardinfo) == -1)
487 devicename << cardinfo.shortname << " " << cardinfo.longname;
488 info.m_displayName = devicename.str();
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;
495 info.m_deviceType = AE_DEVTYPE_PCM;
498 memset(&ainfo, 0, sizeof(ainfo));
500 if (ioctl(mixerfd, SNDCTL_AUDIOINFO, &ainfo) != -1) {
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);
508 j < ainfo.max_channels && AE_CH_NULL != OSSChannelMap[j];
510 info.m_channels += OSSChannelMap[j];
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);
516 list.push_back(info);