2 * Copyright (C) 2005-2014 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, see
17 * <http://www.gnu.org/licenses/>.
21 #include "cores/AudioEngine/AEFactory.h"
22 #include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h"
23 #include "cores/AudioEngine/Utils/AEUtil.h"
24 #include "cores/AudioEngine/Utils/AERingBuffer.h"
25 #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
26 #include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h"
27 #include "cores/AudioEngine/Sinks/osx/CoreAudioChannelLayout.h"
28 #include "osx/DarwinUtils.h"
29 #include "utils/log.h"
30 #include "utils/StringUtils.h"
31 #include "threads/Condition.h"
32 #include "threads/CriticalSection.h"
36 #define CA_MAX_CHANNELS 16
37 // default channel map - in case it can't be fetched from the device
38 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
39 AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
51 // map coraudio channel labels to activeae channel labels
52 static enum AEChannel CAChannelToAEChannel(AudioChannelLabel CAChannelLabel)
54 enum AEChannel ret = AE_CH_NULL;
55 static unsigned int unknownChannel = AE_CH_UNKNOWN1;
56 switch(CAChannelLabel)
58 case kAudioChannelLabel_Left:
61 case kAudioChannelLabel_Right:
64 case kAudioChannelLabel_Center:
67 case kAudioChannelLabel_LFEScreen:
70 case kAudioChannelLabel_LeftSurroundDirect:
73 case kAudioChannelLabel_RightSurroundDirect:
76 case kAudioChannelLabel_LeftCenter:
79 case kAudioChannelLabel_RightCenter:
82 case kAudioChannelLabel_CenterSurround:
85 case kAudioChannelLabel_LeftSurround:
88 case kAudioChannelLabel_RightSurround:
91 case kAudioChannelLabel_VerticalHeightLeft:
94 case kAudioChannelLabel_VerticalHeightRight:
97 case kAudioChannelLabel_VerticalHeightCenter:
100 case kAudioChannelLabel_TopCenterSurround:
103 case kAudioChannelLabel_TopBackLeft:
106 case kAudioChannelLabel_TopBackRight:
109 case kAudioChannelLabel_TopBackCenter:
112 case kAudioChannelLabel_RearSurroundLeft:
115 case kAudioChannelLabel_RearSurroundRight:
118 case kAudioChannelLabel_LeftWide:
121 case kAudioChannelLabel_RightWide:
124 case kAudioChannelLabel_LFE2:
127 case kAudioChannelLabel_LeftTotal:
130 case kAudioChannelLabel_RightTotal:
133 case kAudioChannelLabel_HearingImpaired:
136 case kAudioChannelLabel_Narration:
139 case kAudioChannelLabel_Mono:
142 case kAudioChannelLabel_DialogCentricMix:
145 case kAudioChannelLabel_CenterSurroundDirect:
148 case kAudioChannelLabel_Haptic:
152 ret = (enum AEChannel)unknownChannel++;
154 if (unknownChannel > AE_CH_UNKNOWN8)
155 unknownChannel = AE_CH_UNKNOWN1;
160 //Note: in multichannel mode CA will either pull 2 channels of data (stereo) or 6/8 channels of data
161 //(every speaker setup with more then 2 speakers). The difference between the number of real speakers
162 //and 6/8 channels needs to be padded with unknown channels so that the sample size fits 6/8 channels
164 //device [in] - the device whose channel layout should be used
165 //channelMap [in/out] - if filled it will it indicates that we are called from initialize and we log the requested map, out returns the channelMap for device
166 //channelsPerFrame [in] - the number of channels this device is configured to (e.x. 2 or 6/8)
167 static void GetAEChannelMap(CCoreAudioDevice &device, CAEChannelInfo &channelMap, unsigned int channelsPerFrame)
169 CCoreAudioChannelLayout calayout;
170 bool logMapping = channelMap.Count() > 0; // only log if the engine requests a layout during init
171 bool mapAvailable = false;
172 unsigned int numberChannelsInDeviceLayout = CA_MAX_CHANNELS; // default 8 channels from CAChannelMap
173 AudioChannelLayout *layout = NULL;
175 // try to fetch either the multichannel or the stereo channel layout from the device
176 if (channelsPerFrame == 2 || channelMap.Count() == 2)
177 mapAvailable = device.GetPreferredChannelLayoutForStereo(calayout);
179 mapAvailable = device.GetPreferredChannelLayout(calayout);
181 // if a map was fetched - check if it is usable
185 if (layout == NULL || layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
186 mapAvailable = false;// wrong map format
188 numberChannelsInDeviceLayout = layout->mNumberChannelDescriptions;
191 // start the mapping action
192 // the number of channels to be added to the outgoing channelmap
193 // this is CA_MAX_CHANNELS at max and might be lower for some output devices (channelsPerFrame)
194 unsigned int numChannelsToMap = std::min((unsigned int)CA_MAX_CHANNELS, (unsigned int)channelsPerFrame);
196 // if there was a map fetched we force the number of
197 // channels to map to channelsPerFrame (this allows mapping
198 // of more then CA_MAX_CHANNELS if needed)
200 numChannelsToMap = channelsPerFrame;
202 std::string layoutStr;
206 CLog::Log(LOGDEBUG, "%s Engine requests layout %s", __FUNCTION__, ((std::string)channelMap).c_str());
209 CLog::Log(LOGDEBUG, "%s trying to map to %s layout: %s", __FUNCTION__, channelsPerFrame == 2 ? "stereo" : "multichannel", calayout.ChannelLayoutToString(*layout, layoutStr));
211 CLog::Log(LOGDEBUG, "%s no map available - using static multichannel map layout", __FUNCTION__);
214 channelMap.Reset();// start with an empty map
216 for (unsigned int channel = 0; channel < numChannelsToMap; channel++)
218 // we only try to map channels which are defined in the device layout
219 enum AEChannel currentChannel;
220 if (channel < numberChannelsInDeviceLayout)
222 // get the channel from the fetched map
224 currentChannel = CAChannelToAEChannel(layout->mChannelDescriptions[channel].mChannelLabel);
225 else// get the channel from the default map
226 currentChannel = CAChannelMap[channel];
229 else// fill with unknown channels
230 currentChannel = CAChannelToAEChannel(kAudioChannelLabel_Unknown);
232 if(!channelMap.HasChannel(currentChannel))// only add if not already added
233 channelMap += currentChannel;
237 CLog::Log(LOGDEBUG, "%s mapped channels to layout %s", __FUNCTION__, ((std::string)channelMap).c_str());
240 static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
242 for (size_t i = 0; i < list.size(); ++i)
244 if (list[i] == samplerate)
250 static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
252 for (size_t i = 0; i < list.size(); ++i)
254 if (list[i] == format)
260 typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
262 static void EnumerateDevices(CADeviceList &list)
264 CAEDeviceInfo device;
266 std::string defaultDeviceName;
267 CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
269 CoreAudioDeviceList deviceIDList;
270 CCoreAudioHardware::GetOutputDevices(&deviceIDList);
271 while (!deviceIDList.empty())
273 AudioDeviceID deviceID = deviceIDList.front();
274 CCoreAudioDevice caDevice(deviceID);
276 device.m_channels.Reset();
277 device.m_dataFormats.clear();
278 device.m_sampleRates.clear();
280 device.m_deviceType = AE_DEVTYPE_PCM;
281 device.m_deviceName = caDevice.GetName();
282 device.m_displayName = device.m_deviceName;
283 device.m_displayNameExtra = "";
285 // flag indicating that passthroughformats where added throughout the stream enumeration
286 bool hasPassthroughFormats = false;
287 // the maximum number of channels found in the streams
288 UInt32 numMaxChannels = 0;
289 // the terminal type as reported by ca
290 UInt32 caTerminalType = 0;
292 bool isDigital = caDevice.IsDigital(caTerminalType);
295 CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
296 AudioStreamIdList streams;
297 if (caDevice.GetStreams(&streams))
299 for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
301 StreamFormatList streamFormats;
302 if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streamFormats))
304 for (StreamFormatList::iterator i = streamFormats.begin(); i != streamFormats.end(); ++i)
306 AudioStreamBasicDescription desc = i->mFormat;
307 std::string formatString;
308 CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
309 StreamDescriptionToString(desc, formatString));
311 // add stream format info
312 switch (desc.mFormatID)
314 case kAudioFormatAC3:
315 case kAudioFormat60958AC3:
316 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
317 device.m_dataFormats.push_back(AE_FMT_AC3);
318 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
319 device.m_dataFormats.push_back(AE_FMT_DTS);
320 hasPassthroughFormats = true;
321 isDigital = true;// sanity - those are always digital devices!
324 AEDataFormat format = AE_FMT_INVALID;
325 switch(desc.mBitsPerChannel)
328 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
329 format = AE_FMT_S16BE;
332 // if it is no digital stream per definition
333 // check if the device name suggests that it is digital
334 // (some hackintonshs are not so smart in announcing correct
338 std::string devNameLower = device.m_deviceName;
339 StringUtils::ToLower(devNameLower);
340 isDigital = devNameLower.find("digital") != std::string::npos;
343 /* Passthrough is possible with a 2ch digital output */
344 if (desc.mChannelsPerFrame == 2 && isDigital)
346 if (desc.mSampleRate == 48000)
348 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
349 device.m_dataFormats.push_back(AE_FMT_AC3);
350 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
351 device.m_dataFormats.push_back(AE_FMT_DTS);
352 hasPassthroughFormats = true;
354 else if (desc.mSampleRate == 192000)
356 if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
357 device.m_dataFormats.push_back(AE_FMT_EAC3);
358 hasPassthroughFormats = true;
361 format = AE_FMT_S16LE;
365 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
366 format = AE_FMT_S24BE3;
368 format = AE_FMT_S24LE3;
371 if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
372 format = AE_FMT_FLOAT;
375 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
376 format = AE_FMT_S32BE;
378 format = AE_FMT_S32LE;
383 if (numMaxChannels < desc.mChannelsPerFrame)
384 numMaxChannels = desc.mChannelsPerFrame;
386 if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
387 device.m_dataFormats.push_back(format);
391 // add sample rate info
392 // for devices which return kAudioStreamAnyRatee
393 // we add 44.1khz and 48khz - user can use
394 // the "fixed" audio config to force one of them
395 if (desc.mSampleRate == kAudioStreamAnyRate)
397 CLog::Log(LOGINFO, "%s reported samplerate is kAudioStreamAnyRate adding 44.1khz and 48khz", __FUNCTION__);
398 desc.mSampleRate = 44100;
399 if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
400 device.m_sampleRates.push_back(desc.mSampleRate);
401 desc.mSampleRate = 48000;
404 if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
405 device.m_sampleRates.push_back(desc.mSampleRate);
412 // flag indicating that the device name "sounds" like HDMI
413 bool hasHdmiName = device.m_deviceName.find("HDMI") != std::string::npos;
414 // flag indicating that the device name "sounds" like DisplayPort
415 bool hasDisplayPortName = device.m_deviceName.find("DisplayPort") != std::string::npos;
417 // decide the type of the device based on the discovered information
419 // device defaults to PCM (see start of the while loop)
420 // it can be HDMI, DisplayPort or Optical
421 // for all of those types it needs to support
422 // passthroughformats and needs to be a digital port
423 if (hasPassthroughFormats && isDigital)
425 // if the max number of channels was more then 2
426 // this can be HDMI or DisplayPort or Thunderbolt
427 if (numMaxChannels > 2)
429 // either the devicename suggests its HDMI
430 // or CA reported the terminalType as HDMI
431 if (hasHdmiName || caTerminalType == kIOAudioDeviceTransportTypeHdmi)
432 device.m_deviceType = AE_DEVTYPE_HDMI;
434 // either the devicename suggests its DisplayPort
435 // or CA reported the terminalType as DisplayPort or Thunderbolt
436 if (hasDisplayPortName || caTerminalType == kIOAudioDeviceTransportTypeDisplayPort || caTerminalType == kIOAudioDeviceTransportTypeThunderbolt)
437 device.m_deviceType = AE_DEVTYPE_DP;
439 else// treat all other digital passthrough devices as optical
440 device.m_deviceType = AE_DEVTYPE_IEC958;
442 //treat all other digital devices as HDMI to let options open to the user
443 if (device.m_deviceType == AE_DEVTYPE_PCM)
444 device.m_deviceType = AE_DEVTYPE_HDMI;
447 // devicename based overwrites from former code - maybe FIXME at some point when we
448 // are sure that the upper detection does its job in all[tm] use cases
450 device.m_deviceType = AE_DEVTYPE_HDMI;
451 if (hasDisplayPortName)
452 device.m_deviceType = AE_DEVTYPE_DP;
454 //get channel map to match the devices channel layout as set in audio-midi-setup
455 GetAEChannelMap(caDevice, device.m_channels, caDevice.GetTotalOutputChannels());
457 list.push_back(std::make_pair(deviceID, device));
458 //in the first place of the list add the default device
459 //with name "default" - if this is selected
460 //we will output to whatever osx claims to be default
461 //(allows transition from headphones to speaker and stuff
463 if(defaultDeviceName == device.m_deviceName)
465 device.m_deviceName = "default";
466 device.m_displayName = "Default";
467 list.insert(list.begin(), std::make_pair(deviceID, device));
470 deviceIDList.pop_front();
474 /* static, threadsafe access to the device list */
475 static CADeviceList s_devices;
476 static CCriticalSection s_devicesLock;
478 static void EnumerateDevices()
480 CADeviceList devices;
481 EnumerateDevices(devices);
483 CSingleLock lock(s_devicesLock);
488 static CADeviceList GetDevices()
492 CSingleLock lock(s_devicesLock);
498 OSStatus deviceChangedCB(AudioObjectID inObjectID,
499 UInt32 inNumberAddresses,
500 const AudioObjectPropertyAddress inAddresses[],
503 bool deviceChanged = false;
504 static AudioDeviceID oldDefaultDevice = 0;
505 AudioDeviceID currentDefaultOutputDevice = 0;
507 for (unsigned int i = 0; i < inNumberAddresses; i++)
509 switch (inAddresses[i].mSelector)
511 case kAudioHardwarePropertyDefaultOutputDevice:
512 currentDefaultOutputDevice = CCoreAudioHardware::GetDefaultOutputDevice();
513 // This listener is called on every change of the hardware
514 // device. So check if the default device has really changed.
515 if (oldDefaultDevice != currentDefaultOutputDevice)
517 deviceChanged = true;
518 oldDefaultDevice = currentDefaultOutputDevice;
522 deviceChanged = true;
531 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
532 CAEFactory::DeviceChange();
533 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
538 void RegisterDeviceChangedCB(bool bRegister, void *ref)
540 OSStatus ret = noErr;
541 AudioObjectPropertyAddress inAdr =
543 kAudioHardwarePropertyDevices,
544 kAudioObjectPropertyScopeGlobal,
545 kAudioObjectPropertyElementMaster
550 ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
551 inAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
552 ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
556 ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
557 inAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
558 ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
562 CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
566 ////////////////////////////////////////////////////////////////////////////////////////////
567 CAESinkDARWINOSX::CAESinkDARWINOSX()
568 : m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_planar(false), m_planarBuffer(NULL), m_buffer(NULL)
570 // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
571 // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
572 // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
573 // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
574 CFRunLoopRef theRunLoop = NULL;
575 AudioObjectPropertyAddress theAddress = {
576 kAudioHardwarePropertyRunLoop,
577 kAudioObjectPropertyScopeGlobal,
578 kAudioObjectPropertyElementMaster
580 OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
581 &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
582 if (theError != noErr)
584 CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
586 RegisterDeviceChangedCB(true, this);
591 CAESinkDARWINOSX::~CAESinkDARWINOSX()
593 RegisterDeviceChangedCB(false, this);
596 float scoreSampleRate(Float64 destinationRate, unsigned int sourceRate)
600 double fracPortion = modf(destinationRate / sourceRate, &intPortion);
602 score += (1 - fracPortion) * 1000; // prefer sample rates that are multiples of the source sample rate
603 score += (intPortion == 1.0) ? 500 : 0; // prefer exact matches over other multiples
604 score += (intPortion > 1 && intPortion < 100) ? (100 - intPortion) / 100 * 100 : 0; // prefer smaller multiples otherwise
609 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
612 if (format.m_dataFormat == AE_FMT_AC3 ||
613 format.m_dataFormat == AE_FMT_DTS)
615 if (desc.mFormatID == kAudioFormat60958AC3 ||
616 desc.mFormatID == 'IAC3' ||
617 desc.mFormatID == kAudioFormatAC3)
619 if (desc.mSampleRate == format.m_sampleRate &&
620 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
621 desc.mChannelsPerFrame == format.m_channelLayout.Count())
628 if (format.m_dataFormat == AE_FMT_AC3 ||
629 format.m_dataFormat == AE_FMT_DTS ||
630 format.m_dataFormat == AE_FMT_EAC3)
631 { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
632 if (desc.mSampleRate == format.m_sampleRate &&
633 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
634 desc.mChannelsPerFrame == format.m_channelLayout.Count() &&
635 desc.mFormatID == kAudioFormatLinearPCM)
641 { // non-passthrough, whatever works is fine
642 if (desc.mFormatID == kAudioFormatLinearPCM)
644 score += scoreSampleRate(desc.mSampleRate, format.m_sampleRate);
646 if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
648 else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
650 if (format.m_dataFormat == AE_FMT_FLOAT)
651 { // for float, prefer the highest bitdepth we have
652 if (desc.mBitsPerChannel >= 16)
653 score += (desc.mBitsPerChannel / 8);
657 if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
659 else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
667 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
669 AudioDeviceID deviceID = 0;
670 CADeviceList devices = GetDevices();
671 if (StringUtils::EqualsNoCase(device, "default"))
673 CCoreAudioHardware::GetOutputDeviceName(device);
674 deviceID = CCoreAudioHardware::GetDefaultOutputDevice();
675 CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
679 for (size_t i = 0; i < devices.size(); i++)
681 if (device.find(devices[i].second.m_deviceName) != std::string::npos)
683 deviceID = devices[i].first;
691 CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
695 m_device.Open(deviceID);
697 // Fetch a list of the streams defined by the output device
698 AudioStreamIdList streams;
699 m_device.GetStreams(&streams);
701 CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
703 bool passthrough = false;
704 UInt32 outputIndex = 0;
705 UInt32 numOutputChannels = 0;
706 float outputScore = 0;
707 AudioStreamBasicDescription outputFormat = {0};
708 AudioStreamID outputStream = 0;
710 /* The theory is to score based on
711 1. Matching passthrough characteristics (i.e. passthrough flag)
712 2. Matching sample rate.
713 3. Matching bits per channel (or higher).
714 4. Matching number of channels (or higher).
717 for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
719 // Probe physical formats
720 StreamFormatList formats;
721 CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
722 for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
724 AudioStreamBasicDescription desc = j->mFormat;
726 // for devices with kAudioStreamAnyRate
727 // assume that the user uses a fixed config
728 // and knows what he is doing - so we use
729 // the requested samplerate here
730 if (desc.mSampleRate == kAudioStreamAnyRate)
731 desc.mSampleRate = format.m_sampleRate;
733 float score = ScoreStream(desc, format);
735 std::string formatString;
736 CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
738 if (score > outputScore)
740 passthrough = score > 10000;
751 numOutputChannels = outputFormat.mChannelsPerFrame;
752 if (streams.size() > 1 && outputFormat.mChannelsPerFrame == 1)
754 numOutputChannels = std::min((size_t)format.m_channelLayout.Count(), streams.size());
756 CLog::Log(LOGDEBUG, "%s Found planar audio with %u channels using %u of them.", __FUNCTION__, (unsigned int)streams.size(), (unsigned int)numOutputChannels);
759 if (!outputFormat.mFormatID)
761 CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
765 /* Update our AE format */
766 format.m_sampleRate = outputFormat.mSampleRate;
768 m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
770 std::string formatString;
771 CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, (unsigned int)outputIndex, (unsigned int)outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
773 SetHogMode(passthrough);
775 // Configure the output stream object
776 m_outputStream.Open(outputStream);
778 AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
779 m_outputStream.GetVirtualFormat(&virtualFormat);
780 m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
781 CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
782 CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
784 m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
785 m_outputStream.GetVirtualFormat(&virtualFormat);
786 CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
787 CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
789 // update the channel map based on the new stream format
790 GetAEChannelMap(m_device, format.m_channelLayout, numOutputChannels);
793 m_latentFrames = m_device.GetNumLatencyFrames();
794 m_latentFrames += m_outputStream.GetNumLatencyFrames();
796 /* TODO: Should we use the virtual format to determine our data format? */
797 format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
798 format.m_frames = m_device.GetBufferSize();
799 format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
801 if (m_outputBitstream)
803 m_outputBuffer = new int16_t[format.m_frameSamples];
804 /* TODO: Do we need this? */
805 m_device.SetNominalSampleRate(format.m_sampleRate);
809 m_planarBuffer = new float[format.m_frameSamples];
811 unsigned int num_buffers = 4;
812 m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
813 CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
817 format.m_dataFormat = AE_FMT_S16NE;
819 format.m_dataFormat = AE_FMT_FLOAT;
821 // Register for data request callbacks from the driver and start
822 m_device.AddIOProc(renderCallback, this);
827 void CAESinkDARWINOSX::SetHogMode(bool on)
829 // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
830 // It appears that leaving this set will aslo restore the previous stream format when the
831 // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
832 // From the SDK docs: "If the AudioDevice is in a non-mixable mode, the HAL will automatically take hog mode on behalf of the first process to start an IOProc."
834 // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
835 // If it is attempted after the format change, there is a high likelihood of a deadlock
836 // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
839 // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
840 // Handle this on our own until it is fixed.
841 CCoreAudioHardware::SetAutoHogMode(false);
842 bool autoHog = CCoreAudioHardware::GetAutoHogMode();
843 CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
844 "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
848 m_device.SetHogStatus(on);
849 m_device.SetMixingSupport(!on);
852 void CAESinkDARWINOSX::Deinitialize()
855 m_device.RemoveIOProc();
857 m_outputStream.Close();
864 m_outputBitstream = false;
866 delete[] m_outputBuffer;
867 m_outputBuffer = NULL;
870 delete[] m_planarBuffer;
871 m_planarBuffer = NULL;
876 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
878 return ((m_format.m_sampleRate == format.m_sampleRate) &&
879 (m_format.m_dataFormat == format.m_dataFormat) &&
880 (m_format.m_channelLayout == format.m_channelLayout));
883 double CAESinkDARWINOSX::GetDelay()
887 // Calculate the duration of the data in the cache
888 double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
889 delay += (double)m_latentFrames;
890 delay /= (double)m_format.m_sampleRate;
896 double CAESinkDARWINOSX::GetCacheTotal()
898 return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
901 CCriticalSection mutex;
902 XbmcThreads::ConditionVariable condVar;
904 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
906 if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
907 { // no space to write - wait for a bit
908 CSingleLock lock(mutex);
909 unsigned int timeout = 900 * frames / m_format.m_sampleRate;
913 // we are using a timer here for beeing sure for timeouts
914 // condvar can be woken spuriously as signaled
915 XbmcThreads::EndTime timer(timeout);
916 condVar.wait(mutex, timeout);
917 if (!m_started && timer.IsTimePast())
919 CLog::Log(LOGERROR, "%s engine didn't start in %d ms!", __FUNCTION__, timeout);
924 unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
926 m_buffer->Write(data, write_frames * m_format.m_frameSize);
931 void CAESinkDARWINOSX::Drain()
933 int bytes = m_buffer->GetReadSize();
934 int totalBytes = bytes;
935 int maxNumTimeouts = 3;
936 unsigned int timeout = 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize);
937 while (bytes && maxNumTimeouts > 0)
939 CSingleLock lock(mutex);
940 XbmcThreads::EndTime timer(timeout);
941 condVar.wait(mutex, timeout);
943 bytes = m_buffer->GetReadSize();
944 // if we timeout and don't
945 // consum bytes - decrease maxNumTimeouts
946 if (timer.IsTimePast() && bytes == totalBytes)
952 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
956 for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
957 list.push_back(i->second);
960 inline void LogLevel(unsigned int got, unsigned int wanted)
962 static unsigned int lastReported = INT_MAX;
965 if (got != lastReported)
967 CLog::Log(LOGWARNING, "DARWINOSX: %sflow (%u vs %u bytes)", got > wanted ? "over" : "under", got, wanted);
972 lastReported = INT_MAX; // indicate we were good at least once
975 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
977 CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
979 sink->m_started = true;
982 unsigned int channels = std::min((unsigned int)outOutputData->mNumberBuffers, sink->m_format.m_channelLayout.Count());
983 unsigned int wanted = outOutputData->mBuffers[0].mDataByteSize;
984 unsigned int bytes = std::min(sink->m_buffer->GetReadSize() / channels, wanted);
985 sink->m_buffer->Read((unsigned char *)sink->m_planarBuffer, bytes * channels);
986 // transform from interleaved to planar
987 const float *src = sink->m_planarBuffer;
988 for (unsigned int i = 0; i < bytes / sizeof(float); i++)
990 for (unsigned int j = 0; j < channels; j++)
992 float *dst = (float *)outOutputData->mBuffers[j].mData;
996 LogLevel(bytes, wanted);
997 // tell the sink we're good for more data
1002 for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
1004 // NULL indicates a disabled stream
1006 if (outOutputData->mBuffers[i].mData == NULL)
1009 if (sink->m_outputBitstream)
1011 /* HACK for bitstreaming AC3/DTS via PCM.
1012 We reverse the float->S16LE conversion done in the stream or device */
1013 static const float mul = 1.0f / (INT16_MAX + 1);
1015 unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples) * sizeof(int16_t);
1016 if (wanted <= sink->m_buffer->GetReadSize())
1018 sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
1019 int16_t *src = sink->m_outputBuffer;
1020 float *dest = (float*)outOutputData->mBuffers[i].mData;
1021 for (unsigned int i = 0; i < wanted / 2; i++)
1022 *dest++ = *src++ * mul;
1027 /* buffers appear to come from CA already zero'd, so just copy what is wanted */
1028 unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
1029 unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
1030 sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
1031 LogLevel(bytes, wanted);
1034 // tell the sink we're good for more data
1035 condVar.notifyAll();