[osxsink/iossink] - don't spam the log with audiobuffer underruns with 0 bytes availa...
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkDARWINOSX.cpp
1 /*
2  *      Copyright (C) 2005-2014 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 "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 "osx/DarwinUtils.h"
28 #include "utils/log.h"
29 #include "utils/StringUtils.h"
30 #include "threads/Condition.h"
31 #include "threads/CriticalSection.h"
32
33 #include <sstream>
34
35 #define CA_MAX_CHANNELS 8
36 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
37   AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
38   AE_CH_NULL
39 };
40
41 static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
42 {
43   for (size_t i = 0; i < list.size(); ++i)
44   {
45     if (list[i] == samplerate)
46       return true;
47   }
48   return false;
49 }
50
51 static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
52 {
53   for (size_t i = 0; i < list.size(); ++i)
54   {
55     if (list[i] == format)
56       return true;
57   }
58   return false;
59 }
60
61 typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
62
63 static void EnumerateDevices(CADeviceList &list)
64 {
65   CAEDeviceInfo device;
66
67   std::string defaultDeviceName;
68   CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
69
70   CoreAudioDeviceList deviceIDList;
71   CCoreAudioHardware::GetOutputDevices(&deviceIDList);
72   while (!deviceIDList.empty())
73   {
74     AudioDeviceID deviceID = deviceIDList.front();
75     CCoreAudioDevice caDevice(deviceID);
76
77     device.m_channels.Reset();
78     device.m_dataFormats.clear();
79     device.m_sampleRates.clear();
80
81     device.m_deviceType = AE_DEVTYPE_PCM;
82     device.m_deviceName = caDevice.GetName();
83     device.m_displayName = device.m_deviceName;
84     device.m_displayNameExtra = "";
85
86     // flag indicating that passthroughformats where added throughout the stream enumeration
87     bool hasPassthroughFormats = false;
88     // the maximum number of channels found in the streams
89     UInt32 numMaxChannels = 0;
90     // the terminal type as reported by ca
91     UInt32 caTerminalType = 0;
92       
93     bool isDigital = caDevice.IsDigital(caTerminalType);
94
95
96     CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
97     AudioStreamIdList streams;
98     if (caDevice.GetStreams(&streams))
99     {
100       for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
101       {
102         StreamFormatList streams;
103         if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streams))
104         {
105           for (StreamFormatList::iterator i = streams.begin(); i != streams.end(); ++i)
106           {
107             AudioStreamBasicDescription desc = i->mFormat;
108             std::string formatString;
109             CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
110                                 StreamDescriptionToString(desc, formatString));
111
112             // add stream format info
113             switch (desc.mFormatID)
114             {
115               case kAudioFormatAC3:
116               case kAudioFormat60958AC3:
117                 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
118                   device.m_dataFormats.push_back(AE_FMT_AC3);
119                 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
120                   device.m_dataFormats.push_back(AE_FMT_DTS);
121                 hasPassthroughFormats = true;
122                 isDigital = true;// sanity - those are always digital devices!
123                 break;
124               default:
125                 AEDataFormat format = AE_FMT_INVALID;
126                 switch(desc.mBitsPerChannel)
127                 {
128                   case 16:
129                     if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
130                       format = AE_FMT_S16BE;
131                     else
132                     {
133                       // if it is no digital stream per definition
134                       // check if the device name suggests that it is digital
135                       // (some hackintonshs are not so smart in announcing correct
136                       // ca devices ...
137                       if (!isDigital)
138                       {
139                         std::string devNameLower = device.m_deviceName;
140                         StringUtils::ToLower(devNameLower);                       
141                         isDigital = devNameLower.find("digital") != std::string::npos;
142                       }
143
144                       /* Passthrough is possible with a 2ch digital output */
145                       if (desc.mChannelsPerFrame == 2 && isDigital)
146                       {
147                         if (desc.mSampleRate == 48000)
148                         {
149                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
150                             device.m_dataFormats.push_back(AE_FMT_AC3);
151                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
152                             device.m_dataFormats.push_back(AE_FMT_DTS);
153                           hasPassthroughFormats = true;
154                         }
155                         else if (desc.mSampleRate == 192000)
156                         {
157                           if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
158                             device.m_dataFormats.push_back(AE_FMT_EAC3);
159                           hasPassthroughFormats = true;
160                         }
161                       }
162                       format = AE_FMT_S16LE;
163                     }
164                     break;
165                   case 24:
166                     if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
167                       format = AE_FMT_S24BE3;
168                     else
169                       format = AE_FMT_S24LE3;
170                     break;
171                   case 32:
172                     if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
173                       format = AE_FMT_FLOAT;
174                     else
175                     {
176                       if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
177                         format = AE_FMT_S32BE;
178                       else
179                         format = AE_FMT_S32LE;
180                     }
181                     break;
182                 }
183                 
184                 if (numMaxChannels < desc.mChannelsPerFrame)
185                   numMaxChannels = desc.mChannelsPerFrame;
186                 
187                 if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
188                   device.m_dataFormats.push_back(format);
189                 break;
190             }
191
192             // add channel info
193             CAEChannelInfo channel_info;
194             for (UInt32 chan = 0; chan < CA_MAX_CHANNELS && chan < desc.mChannelsPerFrame; ++chan)
195             {
196               if (!device.m_channels.HasChannel(CAChannelMap[chan]))
197                 device.m_channels += CAChannelMap[chan];
198               channel_info += CAChannelMap[chan];
199             }
200
201             // add sample rate info
202             if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
203               device.m_sampleRates.push_back(desc.mSampleRate);
204           }
205         }
206       }
207     }
208
209     
210     // flag indicating that the device name "sounds" like HDMI
211     bool hasHdmiName = device.m_deviceName.find("HDMI") != std::string::npos;
212     // flag indicating that the device name "sounds" like DisplayPort
213     bool hasDisplayPortName = device.m_deviceName.find("DisplayPort") != std::string::npos;
214     
215     // decide the type of the device based on the discovered information
216     // in the streams
217     // device defaults to PCM (see start of the while loop)
218     // it can be HDMI, DisplayPort or Optical
219     // for all of those types it needs to support
220     // passthroughformats and needs to be a digital port
221     if (hasPassthroughFormats && isDigital)
222     {
223       // if the max number of channels was more then 2
224       // this can be HDMI or DisplayPort or Thunderbolt
225       if (numMaxChannels > 2)
226       {
227         // either the devicename suggests its HDMI
228         // or CA reported the terminalType as HDMI
229         if (hasHdmiName || caTerminalType == kIOAudioDeviceTransportTypeHdmi)
230           device.m_deviceType = AE_DEVTYPE_HDMI;
231
232         // either the devicename suggests its DisplayPort
233         // or CA reported the terminalType as DisplayPort or Thunderbolt
234         if (hasDisplayPortName || caTerminalType == kIOAudioDeviceTransportTypeDisplayPort || caTerminalType == kIOAudioDeviceTransportTypeThunderbolt)
235           device.m_deviceType = AE_DEVTYPE_DP;
236       }
237       else// treat all other digital passthrough devices as optical
238         device.m_deviceType = AE_DEVTYPE_IEC958;
239     }
240
241     // devicename based overwrites from former code - maybe FIXME at some point when we
242     // are sure that the upper detection does its job in all[tm] use cases
243     if (hasHdmiName)
244       device.m_deviceType = AE_DEVTYPE_HDMI;
245     if (hasDisplayPortName)
246       device.m_deviceType = AE_DEVTYPE_DP;
247     
248     
249     list.push_back(std::make_pair(deviceID, device));
250     //in the first place of the list add the default device
251     //with name "default" - if this is selected
252     //we will output to whatever osx claims to be default
253     //(allows transition from headphones to speaker and stuff
254     //like that
255     if(defaultDeviceName == device.m_deviceName)
256     {
257       device.m_deviceName = "default";
258       device.m_displayName = "Default";
259       list.insert(list.begin(), std::make_pair(deviceID, device));
260     }
261
262     deviceIDList.pop_front();
263   }
264 }
265
266 /* static, threadsafe access to the device list */
267 static CADeviceList     s_devices;
268 static CCriticalSection s_devicesLock;
269
270 static void EnumerateDevices()
271 {
272   CADeviceList devices;
273   EnumerateDevices(devices);
274   {
275     CSingleLock lock(s_devicesLock);
276     s_devices = devices;
277   }
278 }
279
280 static CADeviceList GetDevices()
281 {
282   CADeviceList list;
283   {
284     CSingleLock lock(s_devicesLock);
285     list = s_devices;
286   }
287   return list;
288 }
289
290 OSStatus deviceChangedCB(AudioObjectID                       inObjectID,
291                          UInt32                              inNumberAddresses,
292                          const AudioObjectPropertyAddress    inAddresses[],
293                          void*                               inClientData)
294 {
295   CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
296   CAEFactory::DeviceChange();
297   CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
298   return noErr;
299 }
300
301 void RegisterDeviceChangedCB(bool bRegister, void *ref)
302 {
303   OSStatus ret = noErr;
304   const AudioObjectPropertyAddress inAdr =
305   {
306     kAudioHardwarePropertyDevices,
307     kAudioObjectPropertyScopeGlobal,
308     kAudioObjectPropertyElementMaster
309   };
310
311   if (bRegister)
312     ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
313   else
314     ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
315
316   if (ret != noErr)
317     CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
318 }
319
320
321 ////////////////////////////////////////////////////////////////////////////////////////////
322 CAESinkDARWINOSX::CAESinkDARWINOSX()
323 : m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_buffer(NULL)
324 {
325   // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
326   // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
327   // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
328   // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
329   CFRunLoopRef theRunLoop = NULL;
330   AudioObjectPropertyAddress theAddress = {
331     kAudioHardwarePropertyRunLoop,
332     kAudioObjectPropertyScopeGlobal,
333     kAudioObjectPropertyElementMaster
334   };
335   OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
336                                                  &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
337   if (theError != noErr)
338   {
339     CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
340   }
341   RegisterDeviceChangedCB(true, this);
342   m_started = false;
343 }
344
345 CAESinkDARWINOSX::~CAESinkDARWINOSX()
346 {
347   RegisterDeviceChangedCB(false, this);
348 }
349
350 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
351 {
352   float score = 0;
353   if (format.m_dataFormat == AE_FMT_AC3 ||
354       format.m_dataFormat == AE_FMT_DTS)
355   {
356     if (desc.mFormatID == kAudioFormat60958AC3 ||
357         desc.mFormatID == 'IAC3' ||
358         desc.mFormatID == kAudioFormatAC3)
359     {
360       if (desc.mSampleRate == format.m_sampleRate &&
361           desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
362           desc.mChannelsPerFrame == format.m_channelLayout.Count())
363       {
364         // perfect match
365         score = FLT_MAX;
366       }
367     }
368   }
369   if (format.m_dataFormat == AE_FMT_AC3 ||
370       format.m_dataFormat == AE_FMT_DTS ||
371       format.m_dataFormat == AE_FMT_EAC3)
372   { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
373     if (desc.mSampleRate       == format.m_sampleRate                            &&
374         desc.mBitsPerChannel   == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
375         desc.mChannelsPerFrame == format.m_channelLayout.Count()                 &&
376         desc.mFormatID         == kAudioFormatLinearPCM)
377     {
378       score = FLT_MAX / 2;
379     }
380   }
381   else
382   { // non-passthrough, whatever works is fine
383     if (desc.mFormatID == kAudioFormatLinearPCM)
384     {
385       if (desc.mSampleRate == format.m_sampleRate)
386         score += 10;
387       else if (desc.mSampleRate > format.m_sampleRate)
388         score += 1;
389       if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
390         score += 5;
391       else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
392         score += 1;
393       if (format.m_dataFormat == AE_FMT_FLOAT)
394       { // for float, prefer the highest bitdepth we have
395         if (desc.mBitsPerChannel >= 16)
396           score += (desc.mBitsPerChannel / 8);
397       }
398       else
399       {
400         if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
401           score += 5;
402         else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
403           score += 1;
404       }
405     }
406   }
407   return score;
408 }
409
410 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
411 {
412   AudioDeviceID deviceID = 0;
413   CADeviceList devices = GetDevices();
414   if (StringUtils::EqualsNoCase(device, "default"))
415   {
416     CCoreAudioHardware::GetOutputDeviceName(device);
417     CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
418   }
419       
420   for (size_t i = 0; i < devices.size(); i++)
421   {
422     if (device.find(devices[i].second.m_deviceName) != std::string::npos)
423     {
424       m_info = devices[i].second;
425       deviceID = devices[i].first;
426       break;
427     }
428   }
429   if (!deviceID)
430   {
431     CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
432     return false;
433   }
434
435   m_device.Open(deviceID);
436
437   // Fetch a list of the streams defined by the output device
438   AudioStreamIdList streams;
439   m_device.GetStreams(&streams);
440
441   CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
442
443   bool                        passthrough  = false;
444   UInt32                      outputIndex  = 0;
445   float                       outputScore  = 0;
446   AudioStreamBasicDescription outputFormat = {0};
447   AudioStreamID               outputStream = 0;
448
449   /* The theory is to score based on
450    1. Matching passthrough characteristics (i.e. passthrough flag)
451    2. Matching sample rate.
452    3. Matching bits per channel (or higher).
453    4. Matching number of channels (or higher).
454    */
455   UInt32 index = 0;
456   for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
457   {
458     // Probe physical formats
459     StreamFormatList formats;
460     CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
461     for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
462     {
463       const AudioStreamBasicDescription &desc = j->mFormat;
464
465       float score = ScoreStream(desc, format);
466
467       std::string formatString;
468       CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
469
470       if (score > outputScore)
471       {
472         passthrough  = score > 1000;
473         outputScore  = score;
474         outputFormat = desc;
475         outputStream = *i;
476         outputIndex  = index;
477       }
478     }
479     index++;
480   }
481
482   if (!outputFormat.mFormatID)
483   {
484     CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
485     return false;
486   }
487
488   /* Update our AE format */
489   format.m_sampleRate    = outputFormat.mSampleRate;
490   if (outputFormat.mChannelsPerFrame != format.m_channelLayout.Count())
491   { /* update the channel count.  We assume that they're layed out as given in CAChannelMap.
492        if they're not, this is plain wrong */
493     format.m_channelLayout.Reset();
494     for (unsigned int i = 0; i < outputFormat.mChannelsPerFrame; i++)
495       format.m_channelLayout += CAChannelMap[i];
496   }
497
498   m_outputBitstream   = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
499
500   std::string formatString;
501   CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, outputIndex, outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
502
503   SetHogMode(passthrough);
504
505   // Configure the output stream object
506   m_outputStream.Open(outputStream);
507
508   AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
509   m_outputStream.GetVirtualFormat(&virtualFormat);
510   m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
511   CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
512   CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
513
514   m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
515   m_outputStream.GetVirtualFormat(&virtualFormat);
516   CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
517   CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
518
519   m_latentFrames = m_device.GetNumLatencyFrames();
520   m_latentFrames += m_outputStream.GetNumLatencyFrames();
521
522   /* TODO: Should we use the virtual format to determine our data format? */
523   format.m_frameSize     = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
524   format.m_frames        = m_device.GetBufferSize();
525   format.m_frameSamples  = format.m_frames * format.m_channelLayout.Count();
526
527   if (m_outputBitstream)
528   {
529     m_outputBuffer = new int16_t[format.m_frameSamples];
530     /* TODO: Do we need this? */
531     m_device.SetNominalSampleRate(format.m_sampleRate);
532   }
533
534   unsigned int num_buffers = 4;
535   m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
536   CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
537
538   m_format = format;
539   if (passthrough)
540     format.m_dataFormat = AE_FMT_S16NE;
541   else
542     format.m_dataFormat = AE_FMT_FLOAT;
543
544   // Register for data request callbacks from the driver and start
545   m_device.AddIOProc(renderCallback, this);
546   m_device.Start();
547   return true;
548 }
549
550 void CAESinkDARWINOSX::SetHogMode(bool on)
551 {
552   // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
553   // It appears that leaving this set will aslo restore the previous stream format when the
554   // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
555   // 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."
556
557   // Lock down the device.  This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
558   // If it is attempted after the format change, there is a high likelihood of a deadlock
559   // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
560   if (on)
561   {
562     // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
563     // Handle this on our own until it is fixed.
564     CCoreAudioHardware::SetAutoHogMode(false);
565     bool autoHog = CCoreAudioHardware::GetAutoHogMode();
566     CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
567               "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
568     if (autoHog)
569       return;
570   }
571   m_device.SetHogStatus(on);
572   m_device.SetMixingSupport(!on);
573 }
574
575 void CAESinkDARWINOSX::Deinitialize()
576 {
577   m_device.Stop();
578   m_device.RemoveIOProc();
579
580   m_outputStream.Close();
581   m_device.Close();
582   if (m_buffer)
583   {
584     delete m_buffer;
585     m_buffer = NULL;
586   }
587   m_outputBitstream = false;
588
589   delete[] m_outputBuffer;
590   m_outputBuffer = NULL;
591
592   m_started = false;
593 }
594
595 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
596 {
597   return ((m_format.m_sampleRate    == format.m_sampleRate) &&
598           (m_format.m_dataFormat    == format.m_dataFormat) &&
599           (m_format.m_channelLayout == format.m_channelLayout));
600 }
601
602 double CAESinkDARWINOSX::GetDelay()
603 {
604   if (m_buffer)
605   {
606     // Calculate the duration of the data in the cache
607     double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
608     delay += (double)m_latentFrames;
609     delay /= (double)m_format.m_sampleRate;
610     return delay;
611   }
612   return 0.0;
613 }
614
615 double CAESinkDARWINOSX::GetCacheTotal()
616 {
617   return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
618 }
619
620 CCriticalSection mutex;
621 XbmcThreads::ConditionVariable condVar;
622
623 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
624 {
625   if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
626   { // no space to write - wait for a bit
627     CSingleLock lock(mutex);
628     unsigned int timeout = 900 * frames / m_format.m_sampleRate;
629     if (!m_started)
630       timeout = 500;
631
632     // we are using a timer here for beeing sure for timeouts
633     // condvar can be woken spuriously as signaled
634     XbmcThreads::EndTime timer(timeout);
635     condVar.wait(mutex, timeout);
636     if (!m_started && timer.IsTimePast())
637       return INT_MAX;    
638   }
639
640   unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
641   if (write_frames)
642     m_buffer->Write(data, write_frames * m_format.m_frameSize);
643
644   return write_frames;
645 }
646
647 void CAESinkDARWINOSX::Drain()
648 {
649   int bytes = m_buffer->GetReadSize();
650   int totalBytes = bytes;
651   int maxNumTimeouts = 3;
652   unsigned int timeout = 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize);
653   while (bytes && maxNumTimeouts > 0)
654   {
655     CSingleLock lock(mutex);
656     XbmcThreads::EndTime timer(timeout);
657     condVar.wait(mutex, timeout);
658
659     bytes = m_buffer->GetReadSize();
660     // if we timeout and don't
661     // consum bytes - decrease maxNumTimeouts
662     if (timer.IsTimePast() && bytes == totalBytes)
663       maxNumTimeouts--;
664     totalBytes = bytes;
665   }
666 }
667
668 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
669 {
670   EnumerateDevices();
671   list.clear();
672   for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
673     list.push_back(i->second);
674 }
675
676 inline void LogLevel(unsigned int got, unsigned int wanted)
677 {
678   static unsigned int lastReported = INT_MAX;
679   if (got != wanted)
680   {
681     if (got != lastReported)
682     {
683       CLog::Log(LOGWARNING, "DARWINOSX: %sflow (%u vs %u bytes)", got > wanted ? "over" : "under", got, wanted);
684       lastReported = got;
685     }    
686   }
687   else
688     lastReported = INT_MAX; // indicate we were good at least once
689 }
690
691 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
692 {
693   CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
694
695   sink->m_started = true;
696   for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
697   {
698     if (sink->m_outputBitstream)
699     {
700       /* HACK for bitstreaming AC3/DTS via PCM.
701        We reverse the float->S16LE conversion done in the stream or device */
702       static const float mul = 1.0f / (INT16_MAX + 1);
703
704       unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples)  * sizeof(int16_t);
705       if (wanted <= sink->m_buffer->GetReadSize())
706       {
707         sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
708         int16_t *src = sink->m_outputBuffer;
709         float  *dest = (float*)outOutputData->mBuffers[i].mData;
710         for (unsigned int i = 0; i < wanted / 2; i++)
711           *dest++ = *src++ * mul;
712       }
713     }
714     else
715     {
716       /* buffers appear to come from CA already zero'd, so just copy what is wanted */
717       unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
718       unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
719       sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
720       LogLevel(bytes, wanted);
721     }
722
723     // tell the sink we're good for more data
724     condVar.notifyAll();
725   }
726   return noErr;
727 }