]> git.sesse.net Git - vlc/blob - modules/audio_output/auhal.c
shine: avoid double free
[vlc] / modules / audio_output / auhal.c
1 /*****************************************************************************
2  * auhal.c: AUHAL and Coreaudio output plugin
3  *****************************************************************************
4  * Copyright (C) 2005 - 2014 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #pragma mark includes
27
28 #ifdef HAVE_CONFIG_H
29 # import "config.h"
30 #endif
31
32 #import <vlc_common.h>
33 #import <vlc_plugin.h>
34 #import <vlc_dialog.h>                      // dialog_Fatal
35 #import <vlc_aout.h>                        // aout_*
36
37 #import <AudioUnit/AudioUnit.h>             // AudioUnit
38 #import <CoreAudio/CoreAudio.h>             // AudioDeviceID
39 #import <AudioToolbox/AudioFormat.h>        // AudioFormatGetProperty
40 #import <CoreServices/CoreServices.h>
41
42 #import "TPCircularBuffer.h"
43
44 #pragma mark -
45 #pragma mark private declarations
46
47 #ifndef verify_noerr
48 # define verify_noerr(a) assert((a) == noErr)
49 #endif
50
51 #define STREAM_FORMAT_MSG(pre, sfm) \
52     pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
53     sfm.mSampleRate, (char *)&sfm.mFormatID, \
54     (unsigned int)sfm.mFormatFlags, (unsigned int)sfm.mBytesPerPacket, \
55     (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
56     (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
57
58 #define AOUT_VAR_SPDIF_FLAG 0xf00000
59
60 #define AUDIO_BUFFER_SIZE_IN_SECONDS ((AOUT_MAX_ADVANCE_TIME + CLOCK_FREQ) / CLOCK_FREQ)
61
62
63 #define AOUT_VOLUME_DEFAULT             256
64 #define AOUT_VOLUME_MAX                 512
65
66 #define VOLUME_TEXT N_("Audio volume")
67 #define VOLUME_LONGTEXT VOLUME_TEXT
68
69 #define DEVICE_TEXT N_("Last audio device")
70 #define DEVICE_LONGTEXT DEVICE_TEXT
71
72 /*****************************************************************************
73  * aout_sys_t: private audio output method descriptor
74  *****************************************************************************
75  * This structure is part of the audio output thread descriptor.
76  * It describes the CoreAudio specific properties of an output thread.
77  *****************************************************************************/
78 struct aout_sys_t
79 {
80     AudioObjectID               i_selected_dev;     /* DeviceID of the selected device */
81     AudioObjectID               i_new_selected_dev; /* DeviceID of device which will be selected on start */
82     bool                        b_selected_dev_is_digital;
83     bool                        b_selected_dev_is_default; /* true if the user selected the default audio device (id 0) */
84
85     AudioDeviceIOProcID         i_procID;           /* DeviceID of current device */
86     bool                        b_digital;          /* Are we running in digital mode? */
87
88     uint8_t                     chans_to_reorder;   /* do we need channel reordering */
89     uint8_t                     chan_table[AOUT_CHAN_MAX];
90
91     TPCircularBuffer            circular_buffer;    /* circular buffer to swap the audio data */
92
93     /* AUHAL specific */
94     AudioComponent              au_component;       /* The AudioComponent we use */
95     AudioUnit                   au_unit;            /* The AudioUnit we use */
96
97     /* CoreAudio SPDIF mode specific */
98     pid_t                       i_hog_pid;          /* The keep the pid of our hog status */
99     AudioStreamID               i_stream_id;        /* The StreamID that has a cac3 streamformat */
100     int                         i_stream_index;     /* The index of i_stream_id in an AudioBufferList */
101     AudioStreamBasicDescription sfmt_revert;        /* The original format of the stream */
102     bool                        b_revert;           /* Whether we need to revert the stream format */
103     bool                        b_changed_mixing;   /* Whether we need to set the mixing mode back */
104
105     int                         i_rate;             /* media sample rate */
106     int                         i_bytes_per_sample;
107
108     CFArrayRef                  device_list;
109     vlc_mutex_t                 device_list_lock;    /* protects access to device_list */
110
111     vlc_mutex_t                 selected_device_lock;/* Synchronizes access to i_selected_dev. This is only needed
112                                                         between VLCs audio thread and the core audio callback thread.
113                                                         The value is only changed in Start, further access to this variable
114                                                         within the audio thread (start, stop, close) needs no protection. */
115
116     float                       f_volume;
117     bool                        b_mute;
118     bool                        b_paused;
119
120     vlc_mutex_t                 lock;
121     vlc_cond_t                  cond;
122
123     bool                        b_ignore_streams_changed_callback;
124
125     UInt32                      i_device_latency;    /* The time the device needs to process the data. In samples. */
126 };
127
128 #pragma mark -
129 #pragma mark local prototypes & module descriptor
130
131 static int      Open                    (vlc_object_t *);
132 static void     Close                   (vlc_object_t *);
133 static int      Start                   (audio_output_t *, audio_sample_format_t *);
134 static int      StartAnalog             (audio_output_t *, audio_sample_format_t *);
135 static int      StartSPDIF              (audio_output_t *, audio_sample_format_t *);
136 static void     Stop                    (audio_output_t *);
137
138 static void     RebuildDeviceList       (audio_output_t *);
139 static int      SwitchAudioDevice       (audio_output_t *p_aout, const char *name);
140 static int      VolumeSet               (audio_output_t *, float);
141 static int      MuteSet                 (audio_output_t *, bool);
142
143 static void     Play                    (audio_output_t *, block_t *);
144 static void     Pause                   (audio_output_t *, bool, mtime_t);
145 static void     Flush                   (audio_output_t *, bool);
146 static int      TimeGet                 (audio_output_t *, mtime_t *);
147 static OSStatus RenderCallbackAnalog    (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
148                                          UInt32 , UInt32, AudioBufferList *);
149
150 static OSStatus RenderCallbackSPDIF     (AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
151                                          AudioBufferList *, const AudioTimeStamp *, void *);
152
153 static OSStatus DevicesListener         (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
154 static OSStatus DeviceAliveListener     (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
155 static OSStatus DefaultDeviceChangedListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
156 static OSStatus StreamsChangedListener  (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
157
158 static OSStatus StreamListener          (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
159
160 static int      ManageAudioStreamsCallback(audio_output_t *p_aout, AudioDeviceID i_dev_id, bool b_register);
161 static int      AudioDeviceHasOutput    (AudioDeviceID);
162 static int      AudioDeviceSupportsDigital(audio_output_t *, AudioDeviceID);
163 static int      AudioStreamSupportsDigital(audio_output_t *, AudioStreamID);
164 static int      AudioStreamChangeFormat (audio_output_t *, AudioStreamID, AudioStreamBasicDescription);
165
166
167 vlc_module_begin ()
168     set_shortname("auhal")
169     set_description(N_("HAL AudioUnit output"))
170     set_capability("audio output", 101)
171     set_category(CAT_AUDIO)
172     set_subcategory(SUBCAT_AUDIO_AOUT)
173     set_callbacks(Open, Close)
174     add_integer("auhal-volume", AOUT_VOLUME_DEFAULT,
175                 VOLUME_TEXT, VOLUME_LONGTEXT, true)
176     change_integer_range(0, AOUT_VOLUME_MAX)
177     add_string("auhal-audio-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
178     add_obsolete_integer("macosx-audio-device") /* since 2.1.0 */
179 vlc_module_end ()
180
181 #pragma mark -
182 #pragma mark initialization
183
184 static int Open(vlc_object_t *obj)
185 {
186     audio_output_t *p_aout = (audio_output_t *)obj;
187     aout_sys_t *p_sys = malloc(sizeof (*p_sys));
188     if (unlikely(p_sys == NULL))
189         return VLC_ENOMEM;
190
191     OSStatus err = noErr;
192
193     vlc_mutex_init(&p_sys->device_list_lock);
194     vlc_mutex_init(&p_sys->selected_device_lock);
195     vlc_mutex_init(&p_sys->lock);
196     vlc_cond_init(&p_sys->cond);
197     p_sys->b_digital = false;
198     p_sys->b_ignore_streams_changed_callback = false;
199     p_sys->b_selected_dev_is_default = false;
200     p_sys->b_paused = false;
201     memset(&p_sys->sfmt_revert, 0, sizeof(p_sys->sfmt_revert));
202     p_sys->i_stream_id = 0;
203
204     p_aout->sys = p_sys;
205     p_aout->start = Start;
206     p_aout->stop = Stop;
207     p_aout->volume_set = VolumeSet;
208     p_aout->mute_set = MuteSet;
209     p_aout->device_select = SwitchAudioDevice;
210     p_sys->device_list = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
211
212     /*
213      * Force an own run loop for callbacks.
214      *
215      * According to rtaudio, this is absolutely necessary since 10.6 to get correct notifications.
216      * It might fix issues when using the module as a library where a proper loop is not setup already.
217      */
218     CFRunLoopRef theRunLoop = NULL;
219     AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster };
220     err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
221     if (err != noErr) {
222         msg_Err(p_aout, "failed to set the run loop property [%4.4s]", (char *)&err);
223     }
224
225     /* Attach a listener so that we are notified of a change in the device setup */
226     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
227     err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, DevicesListener, (void *)p_aout);
228     if (err != noErr)
229         msg_Err(p_aout, "failed to add listener for audio device configuration [%4.4s]", (char *)&err);
230
231     /* Attach a listener to be notified about changes in default audio device */
232     AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
233     err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
234     if (err != noErr)
235         msg_Err(p_aout, "failed to add listener for default audio device [%4.4s]", (char *)&err);
236
237     RebuildDeviceList(p_aout);
238
239     /* remember the volume */
240     p_sys->f_volume = var_InheritInteger(p_aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT;
241     aout_VolumeReport(p_aout, p_sys->f_volume);
242     p_sys->b_mute = var_InheritBool(p_aout, "mute");
243     aout_MuteReport(p_aout, p_sys->b_mute);
244
245     char *psz_audio_device = var_InheritString(p_aout, "auhal-audio-device");
246     SwitchAudioDevice(p_aout, psz_audio_device);
247     free(psz_audio_device);
248
249     return VLC_SUCCESS;
250 }
251
252 static void Close(vlc_object_t *obj)
253 {
254     audio_output_t *p_aout = (audio_output_t *)obj;
255     aout_sys_t *p_sys = p_aout->sys;
256
257     OSStatus err = noErr;
258
259     /* remove audio devices callback */
260     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
261     err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, DevicesListener, (void *)p_aout);
262     if (err != noErr)
263         msg_Err(p_aout, "AudioHardwareRemovePropertyListener failed [%4.4s]", (char *)&err);
264
265     /* remove listener to be notified about changes in default audio device */
266     AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
267     err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
268     if (err != noErr)
269         msg_Err(p_aout, "failed to remove listener for default audio device [%4.4s]", (char *)&err);
270
271     /*
272      * StreamsChangedListener can rebuild the device list and thus held the device_list_lock.
273      * To avoid a possible deadlock, an array copy is created here.
274      * In rare cases, this can lead to missing StreamsChangedListener callback deregistration (TODO).
275      */
276     vlc_mutex_lock(&p_sys->device_list_lock);
277     CFArrayRef device_list_cpy = CFArrayCreateCopy(NULL, p_sys->device_list);
278     vlc_mutex_unlock(&p_sys->device_list_lock);
279
280     /* remove streams callbacks */
281     CFIndex count = CFArrayGetCount(device_list_cpy);
282     if (count > 0) {
283         for (CFIndex x = 0; x < count; x++) {
284             AudioDeviceID deviceId = 0;
285             CFNumberRef cfn_device_id = CFArrayGetValueAtIndex(device_list_cpy, x);
286             if (!cfn_device_id)
287                 continue;
288
289             CFNumberGetValue(cfn_device_id, kCFNumberSInt32Type, &deviceId);
290             if (!(deviceId & AOUT_VAR_SPDIF_FLAG)) {
291                 ManageAudioStreamsCallback(p_aout, deviceId, false);
292             }
293         }
294     }
295
296     CFRelease(device_list_cpy);
297     CFRelease(p_sys->device_list);
298
299     char *psz_device = aout_DeviceGet(p_aout);
300     config_PutPsz(p_aout, "auhal-audio-device", psz_device);
301     free(psz_device);
302
303     vlc_mutex_destroy(&p_sys->selected_device_lock);
304     vlc_mutex_destroy(&p_sys->device_list_lock);
305     vlc_mutex_destroy(&p_sys->lock);
306     vlc_cond_destroy(&p_sys->cond);
307
308     free(p_sys);
309 }
310
311 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
312 {
313     OSStatus                err = noErr;
314     UInt32                  i_param_size = 0;
315     struct aout_sys_t       *p_sys = NULL;
316
317     /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
318      * property size */
319     int                     b_alive = false;
320
321     bool                    b_start_digital = false;
322
323     p_sys = p_aout->sys;
324     p_sys->b_digital = false;
325     p_sys->au_component = NULL;
326     p_sys->au_unit = NULL;
327     p_sys->i_hog_pid = -1;
328     p_sys->i_stream_index = -1;
329     p_sys->b_revert = false;
330     p_sys->b_changed_mixing = false;
331     p_sys->i_bytes_per_sample = 0;
332     p_sys->b_paused = false;
333     p_sys->i_device_latency = 0;
334
335     vlc_mutex_lock(&p_sys->selected_device_lock);
336     p_sys->i_selected_dev = p_sys->i_new_selected_dev;
337
338     aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
339
340     msg_Dbg(p_aout, "attempting to use device %i", p_sys->i_selected_dev);
341
342     AudioObjectPropertyAddress audioDeviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
343     if (p_sys->i_selected_dev > 0) {
344
345         /* Check if device is in devices list. Only checking for kAudioDevicePropertyDeviceIsAlive is not sufficient,
346          * as a former airplay device might be already gone, but the device number might be still valid. Core Audio
347          * even says that this device would be alive. Don't ask why, its Core Audio. */
348         CFIndex count = CFArrayGetCount(p_sys->device_list);
349         CFNumberRef deviceNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &p_sys->i_selected_dev);
350         if (CFArrayContainsValue(p_sys->device_list, CFRangeMake(0, count), deviceNumber)) {
351
352             /* Check if the desired device is alive and usable */
353             i_param_size = sizeof(b_alive);
354             err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceAliveAddress, 0, NULL, &i_param_size, &b_alive);
355             if (err != noErr) {
356                 /* Be tolerant, only give a warning here */
357                 msg_Warn(p_aout, "could not check whether device [0x%x] is alive [%4.4s]",
358                          (unsigned int)p_sys->i_selected_dev, (char *)&err);
359                 b_alive = false;
360             }
361
362             if (!b_alive)
363                 msg_Warn(p_aout, "selected audio device is not alive, switching to default device");
364
365         } else {
366             msg_Warn(p_aout, "device id %i not found in the current devices list, fallback to default device", p_sys->i_selected_dev);
367         }
368         CFRelease(deviceNumber);
369     }
370
371     p_sys->b_selected_dev_is_default = false;
372     if (!b_alive || p_sys->i_selected_dev == 0) {
373         p_sys->b_selected_dev_is_default = true;
374
375         AudioObjectID defaultDeviceID = 0;
376         UInt32 propertySize = sizeof(AudioObjectID);
377         AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
378         propertySize = sizeof(AudioObjectID);
379         err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
380         if (err != noErr) {
381             msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
382             vlc_mutex_unlock(&p_sys->selected_device_lock);
383             goto error;
384         }
385         else
386             msg_Dbg(p_aout, "using default audio device %i", defaultDeviceID);
387
388         p_sys->i_selected_dev = defaultDeviceID;
389         p_sys->b_selected_dev_is_digital = var_InheritBool(p_aout, "spdif");
390     }
391     vlc_mutex_unlock(&p_sys->selected_device_lock);
392
393     // recheck if device still supports digital
394     b_start_digital = p_sys->b_selected_dev_is_digital;
395     if(!AudioDeviceSupportsDigital(p_aout, p_sys->i_selected_dev))
396         b_start_digital = false;
397
398     if (b_start_digital)
399         msg_Dbg(p_aout, "Using audio device for digital output");
400     else
401         msg_Dbg(p_aout, "Audio device supports PCM mode only");
402
403     /* add a callback to see if the device dies later on */
404     err = AudioObjectAddPropertyListener(p_sys->i_selected_dev, &audioDeviceAliveAddress, DeviceAliveListener, (void *)p_aout);
405     if (err != noErr) {
406         /* Be tolerant, only give a warning here */
407         msg_Warn(p_aout, "could not set alive check callback on device [0x%x] [%4.4s]",
408                  (unsigned int)p_sys->i_selected_dev, (char *)&err);
409     }
410
411     AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
412                                   kAudioDevicePropertyScopeOutput,
413                                   kAudioObjectPropertyElementMaster };
414     i_param_size = sizeof(p_sys->i_hog_pid);
415     err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, &i_param_size, &p_sys->i_hog_pid);
416     if (err != noErr) {
417         /* This is not a fatal error. Some drivers simply don't support this property */
418         msg_Warn(p_aout, "could not check whether device is hogged [%4.4s]",
419                  (char *)&err);
420         p_sys->i_hog_pid = -1;
421     }
422
423     if (p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid()) {
424         msg_Err(p_aout, "Selected audio device is exclusively in use by another program.");
425         dialog_Fatal(p_aout, _("Audio output failed"), "%s",
426                         _("The selected audio output device is exclusively in "
427                           "use by another program."));
428         goto error;
429     }
430
431     /* get device latency */
432     AudioObjectPropertyAddress latencyAddress = { kAudioDevicePropertyLatency, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
433     i_param_size = sizeof(p_sys->i_device_latency);
434     err = AudioObjectGetPropertyData(p_sys->i_selected_dev,
435                                      &latencyAddress,
436                                      0,
437                                      NULL,
438                                      &i_param_size,
439                                      &p_sys->i_device_latency);
440     if (err != noErr) {
441         msg_Warn(p_aout, "Cannot get device latency [%4.4s]",
442                  (char *)&err);
443     }
444     msg_Dbg(p_aout, "Current device has a latency of %u frames", p_sys->i_device_latency);
445
446
447     bool b_success = false;
448
449     /* Check for Digital mode or Analog output mode */
450     if (AOUT_FMT_SPDIF (fmt) && b_start_digital) {
451         if (StartSPDIF (p_aout, fmt)) {
452             msg_Dbg(p_aout, "digital output successfully opened");
453             b_success = true;
454         }
455     } else {
456         if (StartAnalog(p_aout, fmt)) {
457             msg_Dbg(p_aout, "analog output successfully opened");
458             b_success = true;
459         }
460     }
461
462     if (b_success) {
463         p_aout->play = Play;
464         p_aout->flush = Flush;
465         p_aout->time_get = TimeGet;
466         p_aout->pause = Pause;
467         return VLC_SUCCESS;
468     }
469
470 error:
471     /* If we reach this, this aout has failed */
472     msg_Err(p_aout, "opening auhal output failed");
473     return VLC_EGENERIC;
474 }
475
476 /*
477  * StartAnalog: open and setup a HAL AudioUnit to do PCM audio output
478  */
479 static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
480 {
481     struct aout_sys_t           *p_sys = p_aout->sys;
482     OSStatus                    err = noErr;
483     UInt32                      i_param_size = 0;
484     int                         i_original;
485     AudioComponentDescription   desc;
486     AudioStreamBasicDescription DeviceFormat;
487     AudioChannelLayout          *layout;
488     AURenderCallbackStruct      input;
489     p_aout->sys->chans_to_reorder = 0;
490
491     SInt32 currentMinorSystemVersion;
492     if(Gestalt(gestaltSystemVersionMinor, &currentMinorSystemVersion) != noErr)
493         msg_Err(p_aout, "failed to check OSX version");
494
495     /* Lets go find our Component */
496     desc.componentType = kAudioUnitType_Output;
497     desc.componentSubType = kAudioUnitSubType_HALOutput;
498     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
499     desc.componentFlags = 0;
500     desc.componentFlagsMask = 0;
501
502     p_sys->au_component = AudioComponentFindNext(NULL, &desc);
503     if (p_sys->au_component == NULL) {
504         msg_Err(p_aout, "cannot find any HAL component, PCM output failed");
505         return false;
506     }
507
508     err = AudioComponentInstanceNew(p_sys->au_component, &p_sys->au_unit);
509     if (err != noErr) {
510         msg_Err(p_aout, "cannot open HAL component, PCM output failed [%4.4s]", (char *)&err);
511         return false;
512     }
513
514     /* Set the device we will use for this output unit */
515     err = AudioUnitSetProperty(p_sys->au_unit,
516                          kAudioOutputUnitProperty_CurrentDevice,
517                          kAudioUnitScope_Global,
518                          0,
519                          &p_sys->i_selected_dev,
520                          sizeof(AudioObjectID));
521
522     if (err != noErr) {
523         msg_Err(p_aout, "cannot select audio output device, PCM output failed [%4.4s]", (char *)&err);
524         return false;
525     }
526
527     /* Get the current format */
528     i_param_size = sizeof(AudioStreamBasicDescription);
529
530     err = AudioUnitGetProperty(p_sys->au_unit,
531                                    kAudioUnitProperty_StreamFormat,
532                                    kAudioUnitScope_Output,
533                                    0,
534                                    &DeviceFormat,
535                                    &i_param_size);
536
537     if (err != noErr) {
538         msg_Err(p_aout, "failed to detect supported stream formats [%4.4s]", (char *)&err);
539         return false;
540     } else
541         msg_Dbg(p_aout, STREAM_FORMAT_MSG("current format is: ", DeviceFormat));
542
543     /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
544     err = AudioUnitGetPropertyInfo(p_sys->au_unit,
545                                    kAudioDevicePropertyPreferredChannelLayout,
546                                    kAudioUnitScope_Output,
547                                    0,
548                                    &i_param_size,
549                                    NULL);
550
551     if (err == noErr) {
552         layout = (AudioChannelLayout *)malloc(i_param_size);
553
554         verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
555                                        kAudioDevicePropertyPreferredChannelLayout,
556                                        kAudioUnitScope_Output,
557                                        0,
558                                        layout,
559                                        &i_param_size));
560
561         /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
562         if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
563             /* bitmap defined channellayout */
564             verify_noerr(AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
565                                     sizeof(UInt32), &layout->mChannelBitmap,
566                                     &i_param_size,
567                                     layout));
568         } else if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
569         {
570             /* layouttags defined channellayout */
571             verify_noerr(AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
572                                     sizeof(AudioChannelLayoutTag), &layout->mChannelLayoutTag,
573                                     &i_param_size,
574                                     layout));
575         }
576
577         msg_Dbg(p_aout, "layout of AUHAL has %i channels" , layout->mNumberChannelDescriptions);
578
579         if (layout->mNumberChannelDescriptions == 0) {
580             msg_Err(p_aout, "insufficient number of output channels");
581             free(layout);
582             return false;
583         }
584
585         /* Initialize the VLC core channel count */
586         fmt->i_physical_channels = 0;
587         i_original = fmt->i_original_channels & AOUT_CHAN_PHYSMASK;
588
589         if (i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2) {
590             /* We only need Mono or cannot output more than 1 channel */
591             fmt->i_physical_channels = AOUT_CHAN_CENTER;
592         } else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3) {
593             /* We only need Stereo or cannot output more than 2 channels */
594             fmt->i_physical_channels = AOUT_CHANS_STEREO;
595         } else {
596
597             // maps auhal channels to vlc ones
598             static const unsigned i_auhal_channel_mapping[] = {
599                 [kAudioChannelLabel_Left]           = AOUT_CHAN_LEFT,
600                 [kAudioChannelLabel_Right]          = AOUT_CHAN_RIGHT,
601                 [kAudioChannelLabel_Center]         = AOUT_CHAN_CENTER,
602                 [kAudioChannelLabel_LFEScreen]      = AOUT_CHAN_LFE,
603                 [kAudioChannelLabel_LeftSurround]   = AOUT_CHAN_REARLEFT,
604                 [kAudioChannelLabel_RightSurround]  = AOUT_CHAN_REARRIGHT,
605                 [kAudioChannelLabel_RearSurroundLeft]  = AOUT_CHAN_MIDDLELEFT, // needs to be swapped with rear
606                 [kAudioChannelLabel_RearSurroundRight] = AOUT_CHAN_MIDDLERIGHT,// needs to be swapped with rear
607                 [kAudioChannelLabel_CenterSurround] = AOUT_CHAN_REARCENTER
608             };
609
610             /* We want more than stereo and we can do that */
611             for (unsigned int i = 0; i < layout->mNumberChannelDescriptions; i++) {
612 #ifndef NDEBUG
613                 msg_Dbg(p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
614 #endif
615
616                 AudioChannelLabel chan = layout->mChannelDescriptions[i].mChannelLabel;
617                 if(chan < sizeof(i_auhal_channel_mapping) / sizeof(i_auhal_channel_mapping[0])
618                    && i_auhal_channel_mapping[chan] > 0) {
619                     fmt->i_physical_channels |= i_auhal_channel_mapping[chan];
620                 } else {
621                     msg_Dbg(p_aout, "found nonrecognized channel %d at index %d", chan, i);
622                 }
623             }
624             if (fmt->i_physical_channels == 0) {
625                 fmt->i_physical_channels = AOUT_CHANS_STEREO;
626                 msg_Err(p_aout, "You should configure your speaker layout with Audio Midi Setup in /Applications/Utilities. VLC will output Stereo only.");
627                 dialog_Fatal(p_aout, _("Audio device is not configured"), "%s",
628                                 _("You should configure your speaker layout with "
629                                   "\"Audio Midi Setup\" in /Applications/"
630                                   "Utilities. VLC will output Stereo only."));
631             }
632         }
633         free(layout);
634     } else {
635         msg_Warn(p_aout, "device driver does not support kAudioDevicePropertyPreferredChannelLayout - using stereo fallback [%4.4s]", (char *)&err);
636         fmt->i_physical_channels = AOUT_CHANS_STEREO;
637     }
638     fmt->i_original_channels = fmt->i_physical_channels;
639
640     msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
641     msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
642
643     /* Now we set the INPUT layout of the AU */
644     AudioChannelLayout input_layout;
645     memset (&input_layout, 0, sizeof(input_layout));
646     uint32_t chans_out[AOUT_CHAN_MAX];
647
648     /* Some channel abbreviations used below:
649      * L - left
650      * R - right
651      * C - center
652      * Ls - left surround
653      * Rs - right surround
654      * Cs - center surround
655      * Rls - rear left surround
656      * Rrs - rear right surround
657      * Lw - left wide
658      * Rw - right wide
659      * Lsd - left surround direct
660      * Rsd - right surround direct
661      * Lc - left center
662      * Rc - right center
663      * Ts - top surround
664      * Vhl - vertical height left
665      * Vhc - vertical height center
666      * Vhr - vertical height right
667      * Lt - left matrix total. for matrix encoded stereo.
668      * Rt - right matrix total. for matrix encoded stereo. */
669
670     switch(aout_FormatNbChannels(fmt)) {
671         case 1:
672             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
673             break;
674         case 2:
675             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
676             break;
677         case 3:
678             if (fmt->i_physical_channels & AOUT_CHAN_CENTER)
679                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
680             else if (fmt->i_physical_channels & AOUT_CHAN_LFE)
681                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
682             break;
683         case 4:
684             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
685                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
686             else if (fmt->i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
687                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
688             else if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
689                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
690             break;
691         case 5:
692             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER))
693                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
694             else if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
695                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
696             break;
697         case 6:
698             if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) {
699                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
700
701                 chans_out[0] = AOUT_CHAN_LEFT;
702                 chans_out[1] = AOUT_CHAN_RIGHT;
703                 chans_out[2] = AOUT_CHAN_REARLEFT;
704                 chans_out[3] = AOUT_CHAN_REARRIGHT;
705                 chans_out[4] = AOUT_CHAN_CENTER;
706                 chans_out[5] = AOUT_CHAN_LFE;
707
708                 p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
709                 if (p_aout->sys->chans_to_reorder)
710                     msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
711             } else {
712                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
713
714                 chans_out[0] = AOUT_CHAN_LEFT;
715                 chans_out[1] = AOUT_CHAN_RIGHT;
716                 chans_out[2] = AOUT_CHAN_REARLEFT;
717                 chans_out[3] = AOUT_CHAN_REARRIGHT;
718                 chans_out[4] = AOUT_CHAN_CENTER;
719                 chans_out[5] = AOUT_CHAN_REARCENTER;
720
721                 p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
722                 if (p_aout->sys->chans_to_reorder)
723                     msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
724             }
725             break;
726         case 7:
727             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
728
729             chans_out[0] = AOUT_CHAN_LEFT;
730             chans_out[1] = AOUT_CHAN_RIGHT;
731             chans_out[2] = AOUT_CHAN_CENTER;
732             chans_out[3] = AOUT_CHAN_LFE;
733             chans_out[4] = AOUT_CHAN_REARLEFT;
734             chans_out[5] = AOUT_CHAN_REARRIGHT;
735             chans_out[6] = AOUT_CHAN_REARCENTER;
736
737             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
738             if (p_aout->sys->chans_to_reorder)
739                 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
740
741             break;
742         case 8:
743             if (fmt->i_physical_channels & (AOUT_CHAN_LFE) || currentMinorSystemVersion < 7) {
744                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
745
746                 chans_out[0] = AOUT_CHAN_LEFT;
747                 chans_out[1] = AOUT_CHAN_RIGHT;
748                 chans_out[2] = AOUT_CHAN_CENTER;
749                 chans_out[3] = AOUT_CHAN_LFE;
750                 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
751                 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
752                 chans_out[6] = AOUT_CHAN_REARLEFT;
753                 chans_out[7] = AOUT_CHAN_REARRIGHT;
754
755                 if (!(fmt->i_physical_channels & (AOUT_CHAN_LFE)))
756                     msg_Warn(p_aout, "8.0 audio output not supported on OS X 10.%i, layout will be incorrect", currentMinorSystemVersion);
757             }
758 #ifdef MAC_OS_X_VERSION_10_7
759             else {
760                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DTS_8_0_B; // Lc C Rc L R Ls Cs Rs
761
762                 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
763                 chans_out[1] = AOUT_CHAN_CENTER;
764                 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
765                 chans_out[3] = AOUT_CHAN_LEFT;
766                 chans_out[4] = AOUT_CHAN_RIGHT;
767                 chans_out[5] = AOUT_CHAN_REARLEFT;
768                 chans_out[6] = AOUT_CHAN_REARCENTER;
769                 chans_out[7] = AOUT_CHAN_REARRIGHT;
770             }
771 #endif
772             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
773             if (p_aout->sys->chans_to_reorder)
774                 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
775
776             break;
777         case 9:
778             if (currentMinorSystemVersion < 7) {
779                 msg_Warn(p_aout, "8.1 audio output not supported on OS X 10.%i", currentMinorSystemVersion);
780                 break;
781             }
782
783 #ifdef MAC_OS_X_VERSION_10_7
784             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DTS_8_1_B; // Lc C Rc L R Ls Cs Rs LFE
785             chans_out[0] = AOUT_CHAN_MIDDLELEFT;
786             chans_out[1] = AOUT_CHAN_CENTER;
787             chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
788             chans_out[3] = AOUT_CHAN_LEFT;
789             chans_out[4] = AOUT_CHAN_RIGHT;
790             chans_out[5] = AOUT_CHAN_REARLEFT;
791             chans_out[6] = AOUT_CHAN_REARCENTER;
792             chans_out[7] = AOUT_CHAN_REARRIGHT;
793             chans_out[8] = AOUT_CHAN_LFE;
794
795             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
796             if (p_aout->sys->chans_to_reorder)
797                 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
798 #endif
799             break;
800     }
801
802     /* Set up the format to be used */
803     DeviceFormat.mSampleRate = fmt->i_rate;
804     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
805     p_sys->i_rate = fmt->i_rate;
806
807     /* We use float 32 since this is VLC's endorsed format */
808     fmt->i_format = VLC_CODEC_FL32;
809     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
810     DeviceFormat.mBitsPerChannel = 32;
811     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(fmt);
812
813     /* Calculate framesizes and stuff */
814     DeviceFormat.mFramesPerPacket = 1;
815     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
816     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
817
818     /* Set the desired format */
819     i_param_size = sizeof(AudioStreamBasicDescription);
820     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
821                                    kAudioUnitProperty_StreamFormat,
822                                    kAudioUnitScope_Input,
823                                    0,
824                                    &DeviceFormat,
825                                    i_param_size));
826
827     msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , DeviceFormat));
828
829     /* Retrieve actual format */
830     verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
831                                    kAudioUnitProperty_StreamFormat,
832                                    kAudioUnitScope_Input,
833                                    0,
834                                    &DeviceFormat,
835                                    &i_param_size));
836
837     msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , DeviceFormat));
838
839     /* Do the last VLC aout setups */
840     aout_FormatPrepare(fmt);
841
842     /* set the IOproc callback */
843     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
844     input.inputProcRefCon = p_aout;
845
846     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
847                             kAudioUnitProperty_SetRenderCallback,
848                             kAudioUnitScope_Input,
849                             0, &input, sizeof(input)));
850
851     /* Set the input_layout as the layout VLC will use to feed the AU unit */
852     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
853                             kAudioUnitProperty_AudioChannelLayout,
854                             kAudioUnitScope_Input, /* yes, it must be the INPUT scope */
855                             0, &input_layout, sizeof(input_layout)));
856
857     /* AU initiliaze */
858     verify_noerr(AudioUnitInitialize(p_sys->au_unit));
859
860     /* setup circular buffer */
861     TPCircularBufferInit(&p_sys->circular_buffer, AUDIO_BUFFER_SIZE_IN_SECONDS *
862                          fmt->i_rate * fmt->i_bytes_per_frame);
863
864     verify_noerr(AudioOutputUnitStart(p_sys->au_unit));
865
866     /* Set volume for output unit */
867     VolumeSet(p_aout, p_sys->f_volume);
868     MuteSet(p_aout, p_sys->b_mute);
869
870     return true;
871 }
872
873 /*
874  * StartSPDIF: Setup an encoded digital stream (SPDIF) output
875  */
876 static int StartSPDIF(audio_output_t * p_aout, audio_sample_format_t *fmt)
877 {
878     struct aout_sys_t       *p_sys = p_aout->sys;
879     OSStatus                err = noErr;
880     UInt32                  i_param_size = 0, b_mix = 0;
881     Boolean                 b_writeable = false;
882     AudioStreamID           *p_streams = NULL;
883     unsigned                i_streams = 0;
884     AudioStreamBasicDescription desired_stream_format;
885     memset(&desired_stream_format, 0, sizeof(desired_stream_format));
886
887     /* Start doing the SPDIF setup proces */
888     p_sys->b_digital = true;
889
890     /* Hog the device */
891     AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
892     i_param_size = sizeof(p_sys->i_hog_pid);
893     p_sys->i_hog_pid = getpid() ;
894
895     /*
896      * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
897      * directly in the same thread. This call needs to be ignored to avoid endless restarting.
898      */
899     p_sys->b_ignore_streams_changed_callback = true;
900     err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
901     p_sys->b_ignore_streams_changed_callback = false;
902
903     if (err != noErr) {
904         msg_Err(p_aout, "failed to set hogmode [%4.4s]", (char *)&err);
905         return false;
906     }
907
908     AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
909
910     if (AudioObjectHasProperty(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress)) {
911         /* Set mixable to false if we are allowed to */
912         err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
913         err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size);
914         err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
915
916         if (err == noErr && b_writeable) {
917             b_mix = 0;
918             err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
919             p_sys->b_changed_mixing = true;
920         }
921
922         if (err != noErr) {
923             msg_Err(p_aout, "failed to set mixmode [%4.4s]", (char *)&err);
924             return false;
925         }
926     }
927
928     /* Get a list of all the streams on this device */
929     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
930     err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
931     if (err != noErr) {
932         msg_Err(p_aout, "could not get size of stream description packet [%4.4s]", (char *)&err);
933         return false;
934     }
935
936     i_streams = i_param_size / sizeof(AudioStreamID);
937     p_streams = (AudioStreamID *)malloc(i_param_size);
938     if (p_streams == NULL)
939         return false;
940
941     err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
942
943     if (err != noErr) {
944         msg_Err(p_aout, "could not fetch stream descriptions [%4.4s]", (char *)&err);
945         free(p_streams);
946         return false;
947     }
948
949     AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
950     for (unsigned i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++) {
951         /* Find a stream with a cac3 stream */
952         AudioStreamRangedDescription *p_format_list = NULL;
953         int                          i_formats = 0;
954         bool                         b_digital = false;
955
956         /* Retrieve all the stream formats supported by each output stream */
957         err = AudioObjectGetPropertyDataSize(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size);
958         if (err != noErr) {
959             msg_Err(p_aout, "could not get number of streamformats: [%4.4s] (%i)", (char *)&err, (int32_t)err);
960             continue;
961         }
962
963         i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
964         p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
965         if (p_format_list == NULL)
966             continue;
967
968         err = AudioObjectGetPropertyData(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
969         if (err != noErr) {
970             msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
971             free(p_format_list);
972             continue;
973         }
974
975         /* Check if one of the supported formats is a digital format */
976         for (int j = 0; j < i_formats; j++) {
977             if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
978                p_format_list[j].mFormat.mFormatID == 'iac3' ||
979                p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
980                p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
981                 b_digital = true;
982                 break;
983             }
984         }
985
986         if (b_digital) {
987             /* if this stream supports a digital (cac3) format, then go set it. */
988             int i_requested_rate_format = -1;
989             int i_current_rate_format = -1;
990             int i_backup_rate_format = -1;
991
992             if (!p_sys->b_revert) {
993                 AudioObjectPropertyAddress currentPhysicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
994                 /* Retrieve the original format of this stream first if not done so already */
995                 AudioStreamBasicDescription current_streamformat;
996                 i_param_size = sizeof(current_streamformat);
997                 err = AudioObjectGetPropertyData(p_streams[i], &currentPhysicalFormatAddress, 0, NULL, &i_param_size, &current_streamformat);
998                 if (err != noErr) {
999                     msg_Err(p_aout, "could not retrieve the original streamformat [%4.4s]", (char *)&err);
1000                     continue;
1001                 }
1002
1003                 /* 
1004                  * Only the first found format id is accepted. In case of another id later on, we still use the
1005                  * already saved one. This can happen if the user plugs in a spdif cable while a stream is already
1006                  * playing. Then, auhal already misleadingly reports an ac3 format here whereas the original format
1007                  * should be still pcm.
1008                  */
1009                 if (p_sys->sfmt_revert.mFormatID > 0 && p_sys->sfmt_revert.mFormatID != current_streamformat.mFormatID &&
1010                         p_streams[i] == p_sys->i_stream_id) {
1011                     msg_Warn(p_aout, STREAM_FORMAT_MSG("Detected current stream format: ", current_streamformat));
1012                     msg_Warn(p_aout, "... there is another stream format already stored, the current one is ignored");
1013                 } else {
1014                     p_sys->sfmt_revert = current_streamformat;
1015                 }
1016
1017                 p_sys->b_revert = true;
1018             }
1019
1020             p_sys->i_stream_id = p_streams[i];
1021             p_sys->i_stream_index = i;
1022
1023             for (int j = 0; j < i_formats; j++) {
1024                 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
1025                    p_format_list[j].mFormat.mFormatID == 'iac3' ||
1026                    p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
1027                    p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
1028                     if (p_format_list[j].mFormat.mSampleRate == fmt->i_rate) {
1029                         i_requested_rate_format = j;
1030                         break;
1031                     } else if (p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate)
1032                         i_current_rate_format = j;
1033                     else {
1034                         if (i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate)
1035                             i_backup_rate_format = j;
1036                     }
1037                 }
1038
1039             }
1040
1041             if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio */
1042                 desired_stream_format = p_format_list[i_requested_rate_format].mFormat;
1043             else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device */
1044                 desired_stream_format = p_format_list[i_current_rate_format].mFormat;
1045             else
1046                 desired_stream_format = p_format_list[i_backup_rate_format].mFormat; /* And if we have to, any digital format will be just fine (highest rate possible) */
1047         }
1048         free(p_format_list);
1049     }
1050     free(p_streams);
1051
1052     msg_Dbg(p_aout, STREAM_FORMAT_MSG("original stream format: ", p_sys->sfmt_revert));
1053
1054     if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, desired_stream_format)) {
1055         msg_Err(p_aout, "failed to change stream format for SPDIF output");
1056         return false;
1057     }
1058
1059     /* Set the format flags */
1060     if (desired_stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
1061         fmt->i_format = VLC_CODEC_SPDIFB;
1062     else
1063         fmt->i_format = VLC_CODEC_SPDIFL;
1064     fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
1065     fmt->i_frame_length = A52_FRAME_NB;
1066     fmt->i_rate = (unsigned int)desired_stream_format.mSampleRate;
1067     p_sys->i_rate = fmt->i_rate;
1068     aout_FormatPrepare(fmt);
1069
1070     /* Add IOProc callback */
1071     err = AudioDeviceCreateIOProcID(p_sys->i_selected_dev,
1072                                    (AudioDeviceIOProc)RenderCallbackSPDIF,
1073                                    (void *)p_aout,
1074                                    &p_sys->i_procID);
1075     if (err != noErr) {
1076         msg_Err(p_aout, "Failed to create Process ID [%4.4s]", (char *)&err);
1077         return false;
1078     }
1079
1080     /* Start device */
1081     err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
1082     if (err != noErr) {
1083         msg_Err(p_aout, "Failed to start audio device [%4.4s]", (char *)&err);
1084
1085         err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev, p_sys->i_procID);
1086         if (err != noErr)
1087             msg_Err(p_aout, "Failed to destroy process ID [%4.4s]", (char *)&err);
1088
1089         return false;
1090     }
1091
1092     /* setup circular buffer */
1093     TPCircularBufferInit(&p_sys->circular_buffer, 200 * AOUT_SPDIF_SIZE);
1094
1095     return true;
1096 }
1097
1098 static void Stop(audio_output_t *p_aout)
1099 {
1100     struct aout_sys_t   *p_sys = p_aout->sys;
1101     OSStatus            err = noErr;
1102     UInt32              i_param_size = 0;
1103
1104     msg_Dbg(p_aout, "Stopping the auhal module");
1105
1106     if (p_sys->au_unit) {
1107         verify_noerr(AudioOutputUnitStop(p_sys->au_unit));
1108         verify_noerr(AudioUnitUninitialize(p_sys->au_unit));
1109         verify_noerr(AudioComponentInstanceDispose(p_sys->au_unit));
1110     }
1111
1112     if (p_sys->b_digital) {
1113         /* Stop device */
1114         err = AudioDeviceStop(p_sys->i_selected_dev,
1115                                p_sys->i_procID);
1116         if (err != noErr)
1117             msg_Err(p_aout, "Failed to stop audio device [%4.4s]", (char *)&err);
1118
1119         /* Remove IOProc callback */
1120         err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev,
1121                                           p_sys->i_procID);
1122         if (err != noErr)
1123             msg_Err(p_aout, "Failed to destroy Process ID [%4.4s]", (char *)&err);
1124
1125         if (p_sys->b_revert) {
1126             if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->sfmt_revert)) {
1127                 msg_Err(p_aout, "failed to revert stream format in close");
1128             }
1129         }
1130
1131         if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
1132             int b_mix;
1133             Boolean b_writeable = false;
1134             i_param_size = sizeof(int);
1135             /* Revert mixable to true if we are allowed to */
1136             AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1137             err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
1138             err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
1139
1140             if (err == noErr && b_writeable) {
1141                 msg_Dbg(p_aout, "mixable is: %d", b_mix);
1142                 b_mix = 1;
1143                 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
1144             }
1145
1146             if (err != noErr)
1147                 msg_Err(p_aout, "failed to re-set mixmode [%4.4s]", (char *)&err);
1148         }
1149     }
1150
1151     if (p_sys->i_hog_pid == getpid()) {
1152         p_sys->i_hog_pid = -1;
1153         i_param_size = sizeof(p_sys->i_hog_pid);
1154         AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
1155             kAudioDevicePropertyScopeOutput,
1156             kAudioObjectPropertyElementMaster };
1157
1158         /*
1159          * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
1160          * directly in the same thread. This call needs to be ignored to avoid endless restarting.
1161          */
1162         p_sys->b_ignore_streams_changed_callback = true;
1163         err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
1164         p_sys->b_ignore_streams_changed_callback = false;
1165         if (err != noErr)
1166             msg_Err(p_aout, "Failed to release hogmode [%4.4s]", (char *)&err);
1167     }
1168
1169     /* remove audio device alive callback */
1170     AudioObjectPropertyAddress deviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1171     err = AudioObjectRemovePropertyListener(p_sys->i_selected_dev, &deviceAliveAddress, DeviceAliveListener, (void *)p_aout);
1172     if (err != noErr) {
1173         /* Be tolerant, only give a warning here */
1174         msg_Warn(p_aout, "failed to remove audio device life checker [%4.4s]", (char *)&err);
1175     }
1176
1177     p_sys->i_bytes_per_sample = 0;
1178     p_sys->b_digital = false;
1179
1180     /* clean-up circular buffer */
1181     TPCircularBufferCleanup(&p_sys->circular_buffer);
1182 }
1183
1184 #pragma mark -
1185 #pragma mark core interaction
1186
1187 static void ReportDevice(audio_output_t *p_aout, UInt32 i_id, char *name)
1188 {
1189     char deviceid[10];
1190     sprintf(deviceid, "%i", i_id);
1191
1192     aout_HotplugReport(p_aout, deviceid, name);
1193 }
1194
1195 static void RebuildDeviceList(audio_output_t * p_aout)
1196 {
1197     OSStatus            err = noErr;
1198     UInt32              propertySize = 0;
1199     AudioObjectID       *deviceIDs;
1200     UInt32              numberOfDevices;
1201     CFMutableArrayRef   currentListOfDevices;
1202
1203     struct aout_sys_t   *p_sys = p_aout->sys;
1204
1205     msg_Dbg(p_aout, "Rebuild device list");
1206
1207     ReportDevice(p_aout, 0, _("System Sound Output Device"));
1208
1209     /* setup local array */
1210     currentListOfDevices = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1211
1212     /* Get number of devices */
1213     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1214     err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &propertySize);
1215     if (err != noErr) {
1216         msg_Err(p_aout, "Could not get number of devices: [%4.4s]", (char *)&err);
1217         CFRelease(currentListOfDevices);
1218         return;
1219     }
1220
1221     numberOfDevices = propertySize / sizeof(AudioDeviceID);
1222
1223     if (numberOfDevices < 1) {
1224         msg_Err(p_aout, "No audio output devices found.");
1225         CFRelease(currentListOfDevices);
1226         return;
1227     }
1228     msg_Dbg(p_aout, "found %i audio device(s)", numberOfDevices);
1229
1230     /* Allocate DeviceID array */
1231     deviceIDs = (AudioDeviceID *)calloc(numberOfDevices, sizeof(AudioDeviceID));
1232     if (deviceIDs == NULL) {
1233         CFRelease(currentListOfDevices);
1234         return;
1235     }
1236
1237     /* Populate DeviceID array */
1238     err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &propertySize, deviceIDs);
1239     if (err != noErr) {
1240         msg_Err(p_aout, "could not get the device IDs [%4.4s]", (char *)&err);
1241         CFRelease(currentListOfDevices);
1242         free(deviceIDs);
1243         return;
1244     }
1245
1246     AudioObjectPropertyAddress deviceNameAddress = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1247
1248     for (unsigned int i = 0; i < numberOfDevices; i++) {
1249         CFStringRef device_name_ref;
1250         char *psz_name;
1251         CFIndex length;
1252         UInt32 i_id = deviceIDs[i];
1253
1254         /* Retrieve the length of the device name */
1255         err = AudioObjectGetPropertyDataSize(deviceIDs[i], &deviceNameAddress, 0, NULL, &propertySize);
1256         if (err != noErr) {
1257             msg_Dbg(p_aout, "failed to get name size for device %i", deviceIDs[i]);
1258             continue;
1259         }
1260
1261         /* Retrieve the name of the device */
1262         err = AudioObjectGetPropertyData(deviceIDs[i], &deviceNameAddress, 0, NULL, &propertySize, &device_name_ref);
1263         if (err != noErr) {
1264             msg_Dbg(p_aout, "failed to get name for device %i", deviceIDs[i]);
1265             continue;
1266         }
1267         length = CFStringGetLength(device_name_ref);
1268         length++;
1269         psz_name = malloc(length);
1270         if (!psz_name) {
1271             CFRelease(device_name_ref);
1272             return;
1273         }
1274         CFStringGetCString(device_name_ref, psz_name, length, kCFStringEncodingUTF8);
1275         CFRelease(device_name_ref);
1276
1277         msg_Dbg(p_aout, "DevID: %i DevName: %s", deviceIDs[i], psz_name);
1278
1279         if (!AudioDeviceHasOutput(deviceIDs[i])) {
1280             msg_Dbg(p_aout, "this '%s' is INPUT only. skipping...", psz_name);
1281             free(psz_name);
1282             continue;
1283         }
1284
1285         ReportDevice(p_aout, i_id, psz_name);
1286         CFNumberRef deviceNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i_id);
1287         CFArrayAppendValue(currentListOfDevices, deviceNumber);
1288         CFRelease(deviceNumber);
1289
1290         if (AudioDeviceSupportsDigital(p_aout, deviceIDs[i])) {
1291             msg_Dbg(p_aout, "'%s' supports digital output", psz_name);
1292             char *psz_encoded_name = nil;
1293             asprintf(&psz_encoded_name, _("%s (Encoded Output)"), psz_name);
1294             i_id = i_id | AOUT_VAR_SPDIF_FLAG;
1295             ReportDevice(p_aout, i_id, psz_encoded_name);
1296             deviceNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i_id);
1297             CFArrayAppendValue(currentListOfDevices, deviceNumber);
1298             CFRelease(deviceNumber);
1299             free(psz_encoded_name);
1300         }
1301
1302         // TODO: only register once for each device
1303         ManageAudioStreamsCallback(p_aout, deviceIDs[i], true);
1304
1305         free(psz_name);
1306     }
1307
1308     vlc_mutex_lock(&p_sys->device_list_lock);
1309     CFIndex count = 0;
1310     if (p_sys->device_list)
1311         count = CFArrayGetCount(p_sys->device_list);
1312     CFRange newListSearchRange = CFRangeMake(0, CFArrayGetCount(currentListOfDevices));
1313
1314     if (count > 0) {
1315         msg_Dbg(p_aout, "Looking for removed devices");
1316         CFNumberRef cfn_device_id;
1317         int i_device_id = 0;
1318         for (CFIndex x = 0; x < count; x++) {
1319             if (!CFArrayContainsValue(currentListOfDevices, newListSearchRange, CFArrayGetValueAtIndex(p_sys->device_list, x))) {
1320                 cfn_device_id = CFArrayGetValueAtIndex(p_sys->device_list, x);
1321                 if (cfn_device_id) {
1322                     CFNumberGetValue(cfn_device_id, kCFNumberSInt32Type, &i_device_id);
1323                     msg_Dbg(p_aout, "Device ID %i is not found in new array, deleting.", i_device_id);
1324
1325                     ReportDevice(p_aout, i_device_id, NULL);
1326                 }
1327             }
1328         }
1329     }
1330     if (p_sys->device_list)
1331         CFRelease(p_sys->device_list);
1332     p_sys->device_list = CFArrayCreateCopy(kCFAllocatorDefault, currentListOfDevices);
1333     CFRelease(currentListOfDevices);
1334     vlc_mutex_unlock(&p_sys->device_list_lock);
1335
1336     free(deviceIDs);
1337 }
1338
1339 static int SwitchAudioDevice(audio_output_t *p_aout, const char *name)
1340 {
1341     struct aout_sys_t *p_sys = p_aout->sys;
1342
1343     if (name)
1344         p_sys->i_new_selected_dev = atoi(name);
1345     else
1346         p_sys->i_new_selected_dev = 0;
1347
1348     bool b_supports_digital = (p_sys->i_new_selected_dev & AOUT_VAR_SPDIF_FLAG);
1349     if (b_supports_digital)
1350         p_sys->b_selected_dev_is_digital = true;
1351     else
1352         p_sys->b_selected_dev_is_digital = false;
1353
1354     p_sys->i_new_selected_dev = p_sys->i_new_selected_dev & ~AOUT_VAR_SPDIF_FLAG;
1355
1356     aout_DeviceReport(p_aout, name);
1357     aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1358
1359     return 0;
1360 }
1361
1362 static int VolumeSet(audio_output_t * p_aout, float volume)
1363 {
1364     struct aout_sys_t *p_sys = p_aout->sys;
1365     OSStatus ostatus = 0;
1366
1367     if(p_sys->b_digital)
1368         return VLC_EGENERIC;
1369
1370     p_sys->f_volume = volume;
1371     aout_VolumeReport(p_aout, volume);
1372
1373     /* Set volume for output unit */
1374     if(!p_sys->b_mute) {
1375         ostatus = AudioUnitSetParameter(p_sys->au_unit,
1376                                         kHALOutputParam_Volume,
1377                                         kAudioUnitScope_Global,
1378                                         0,
1379                                         volume * volume * volume,
1380                                         0);
1381     }
1382
1383     if (var_InheritBool(p_aout, "volume-save"))
1384         config_PutInt(p_aout, "auhal-volume", lroundf(volume * AOUT_VOLUME_DEFAULT));
1385
1386     return ostatus;
1387 }
1388
1389 static int MuteSet(audio_output_t * p_aout, bool mute)
1390 {
1391     struct   aout_sys_t *p_sys = p_aout->sys;
1392     OSStatus ostatus;
1393
1394     if(p_sys->b_digital)
1395         return VLC_EGENERIC;
1396
1397     p_sys->b_mute = mute;
1398     aout_MuteReport(p_aout, mute);
1399
1400     float volume = .0;
1401     if (!mute)
1402         volume = p_sys->f_volume;
1403
1404     ostatus = AudioUnitSetParameter(p_sys->au_unit,
1405                                     kHALOutputParam_Volume,
1406                                     kAudioUnitScope_Global,
1407                                     0,
1408                                     volume * volume * volume,
1409                                     0);
1410
1411     return ostatus;
1412 }
1413
1414 #pragma mark -
1415 #pragma mark actual playback
1416
1417 static void Play(audio_output_t * p_aout, block_t * p_block)
1418 {
1419     struct aout_sys_t *p_sys = p_aout->sys;
1420
1421     if (p_block->i_nb_samples > 0) {
1422         /* Do the channel reordering */
1423         if (p_sys->chans_to_reorder && !p_sys->b_digital) {
1424            aout_ChannelReorder(p_block->p_buffer,
1425                                p_block->i_buffer,
1426                                p_sys->chans_to_reorder,
1427                                p_sys->chan_table,
1428                                VLC_CODEC_FL32);
1429         }
1430
1431         /* move data to buffer */
1432         if (unlikely(!TPCircularBufferProduceBytes(&p_sys->circular_buffer, p_block->p_buffer, p_block->i_buffer)))
1433             msg_Warn(p_aout, "dropped buffer");
1434
1435         if (!p_sys->i_bytes_per_sample)
1436             p_sys->i_bytes_per_sample = p_block->i_buffer / p_block->i_nb_samples;
1437     }
1438
1439     block_Release(p_block);
1440 }
1441
1442 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
1443 {
1444     struct aout_sys_t * p_sys = p_aout->sys;
1445     VLC_UNUSED(date);
1446
1447     vlc_mutex_lock(&p_sys->lock);
1448     p_sys->b_paused = pause;
1449     vlc_mutex_unlock(&p_sys->lock);
1450 }
1451
1452 static void Flush(audio_output_t *p_aout, bool wait)
1453 {
1454     struct aout_sys_t *p_sys = p_aout->sys;
1455
1456     int32_t availableBytes;
1457     vlc_mutex_lock(&p_sys->lock);
1458     TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1459
1460     if (wait) {
1461         while (availableBytes > 0) {
1462             vlc_cond_wait(&p_sys->cond, &p_sys->lock);
1463             TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1464         }
1465     } else {
1466         /* flush circular buffer if data is left */
1467         if (availableBytes > 0)
1468             TPCircularBufferClear(&p_aout->sys->circular_buffer);
1469     }
1470
1471     vlc_mutex_unlock(&p_sys->lock);
1472 }
1473
1474 static int TimeGet(audio_output_t *p_aout, mtime_t *delay)
1475 {
1476     struct aout_sys_t * p_sys = p_aout->sys;
1477
1478     if (!p_sys->i_bytes_per_sample)
1479         return -1;
1480
1481     int32_t availableBytes;
1482     TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1483
1484     *delay = ((availableBytes / p_sys->i_bytes_per_sample) + p_sys->i_device_latency) * CLOCK_FREQ / p_sys->i_rate;
1485
1486     return 0;
1487 }
1488
1489 /*****************************************************************************
1490  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1491  * us to provide some more audio data.
1492  * Don't print anything during normal playback, calling blocking function from
1493  * this callback is not allowed.
1494  *****************************************************************************/
1495 static OSStatus RenderCallbackAnalog(vlc_object_t *p_obj,
1496                                     AudioUnitRenderActionFlags *ioActionFlags,
1497                                     const AudioTimeStamp *inTimeStamp,
1498                                     UInt32 inBusNumber,
1499                                     UInt32 inNumberFrames,
1500                                     AudioBufferList *ioData) {
1501     VLC_UNUSED(ioActionFlags);
1502     VLC_UNUSED(inTimeStamp);
1503     VLC_UNUSED(inBusNumber);
1504     VLC_UNUSED(inNumberFrames);
1505
1506     audio_output_t * p_aout = (audio_output_t *)p_obj;
1507     struct aout_sys_t * p_sys = p_aout->sys;
1508
1509     int bytesRequested = ioData->mBuffers[0].mDataByteSize;
1510     Float32 *targetBuffer = (Float32*)ioData->mBuffers[0].mData;
1511     if (unlikely(bytesRequested == 0)) /* cannot be negative */
1512         return noErr;
1513
1514     vlc_mutex_lock(&p_sys->lock);
1515     /* Pull audio from buffer */
1516     int32_t availableBytes;
1517     Float32 *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1518
1519     /* check if we have enough data */
1520     if (!availableBytes || p_sys->b_paused) {
1521         /* return an empty buffer so silence is played until we have data */
1522         memset(targetBuffer, 0, bytesRequested);
1523     } else {
1524         int32_t bytesToCopy = __MIN(bytesRequested, availableBytes);
1525
1526         memcpy(targetBuffer, buffer, bytesToCopy);
1527         TPCircularBufferConsume(&p_sys->circular_buffer, bytesToCopy);
1528         ioData->mBuffers[0].mDataByteSize = bytesToCopy;
1529     }
1530
1531     vlc_cond_signal(&p_sys->cond);
1532     vlc_mutex_unlock(&p_sys->lock);
1533
1534     return noErr;
1535 }
1536
1537 /*
1538  * RenderCallbackSPDIF: callback for SPDIF audio output
1539  */
1540 static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
1541                                     const AudioTimeStamp * inNow,
1542                                     const void * inInputData,
1543                                     const AudioTimeStamp * inInputTime,
1544                                     AudioBufferList * outOutputData,
1545                                     const AudioTimeStamp * inOutputTime,
1546                                     void * threadGlobals)
1547 {
1548     VLC_UNUSED(inNow);
1549     VLC_UNUSED(inDevice);
1550     VLC_UNUSED(inInputData);
1551     VLC_UNUSED(inInputTime);
1552     VLC_UNUSED(inOutputTime);
1553
1554     audio_output_t * p_aout = (audio_output_t *)threadGlobals;
1555     struct aout_sys_t * p_sys = p_aout->sys;
1556
1557     int bytesRequested = outOutputData->mBuffers[p_sys->i_stream_index].mDataByteSize;
1558     char *targetBuffer = outOutputData->mBuffers[p_sys->i_stream_index].mData;
1559     if (unlikely(bytesRequested == 0)) /* cannot be negative */
1560         return noErr;
1561
1562     vlc_mutex_lock(&p_sys->lock);
1563     /* Pull audio from buffer */
1564     int32_t availableBytes;
1565     char *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1566
1567     /* check if we have enough data */
1568     if (!availableBytes || p_sys->b_paused) {
1569         /* return an empty buffer so silence is played until we have data */
1570         memset(targetBuffer, 0, bytesRequested);
1571     } else {
1572         int32_t bytesToCopy = __MIN(bytesRequested, availableBytes);
1573
1574         memcpy(targetBuffer, buffer, bytesToCopy);
1575         TPCircularBufferConsume(&p_sys->circular_buffer, bytesToCopy);
1576         outOutputData->mBuffers[p_sys->i_stream_index].mDataByteSize = bytesToCopy;
1577     }
1578
1579     vlc_cond_signal(&p_sys->cond);
1580     vlc_mutex_unlock(&p_sys->lock);
1581
1582     return noErr;
1583 }
1584
1585 #pragma mark -
1586 #pragma mark Stream / Hardware Listeners
1587
1588 /*
1589  * Callback when device list changed
1590  */
1591 static OSStatus DevicesListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1592 {
1593     VLC_UNUSED(inObjectID);
1594     VLC_UNUSED(inNumberAddresses);
1595     VLC_UNUSED(inAddresses);
1596
1597     audio_output_t *p_aout = (audio_output_t *)inClientData;
1598     if (!p_aout)
1599         return -1;
1600     aout_sys_t *p_sys = p_aout->sys;
1601
1602     msg_Dbg(p_aout, "audio device configuration changed, resetting cache");
1603     RebuildDeviceList(p_aout);
1604
1605     vlc_mutex_lock(&p_sys->selected_device_lock);
1606     vlc_mutex_lock(&p_sys->device_list_lock);
1607     CFNumberRef selectedDevice = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &p_sys->i_selected_dev);
1608     if(!CFArrayContainsValue(p_sys->device_list, CFRangeMake(0, CFArrayGetCount(p_sys->device_list)), selectedDevice))
1609         aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1610     CFRelease(selectedDevice);
1611     vlc_mutex_unlock(&p_sys->device_list_lock);
1612     vlc_mutex_unlock(&p_sys->selected_device_lock);
1613
1614     return noErr;
1615 }
1616
1617 /*
1618  * Callback when current device is not alive anymore
1619  */
1620 static OSStatus DeviceAliveListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1621 {
1622     VLC_UNUSED(inObjectID);
1623     VLC_UNUSED(inNumberAddresses);
1624     VLC_UNUSED(inAddresses);
1625
1626     audio_output_t *p_aout = (audio_output_t *)inClientData;
1627     if (!p_aout)
1628         return -1;
1629
1630     msg_Warn(p_aout, "audio device died, resetting aout");
1631     aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1632
1633     return noErr;
1634 }
1635
1636 /*
1637  * Callback when default audio device changed
1638  */
1639 static OSStatus DefaultDeviceChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1640 {
1641     VLC_UNUSED(inObjectID);
1642     VLC_UNUSED(inNumberAddresses);
1643     VLC_UNUSED(inAddresses);
1644
1645     audio_output_t *p_aout = (audio_output_t *)inClientData;
1646     if (!p_aout)
1647         return -1;
1648
1649     aout_sys_t *p_sys = p_aout->sys;
1650
1651     if (!p_aout->sys->b_selected_dev_is_default)
1652         return noErr;
1653
1654     AudioObjectID defaultDeviceID = 0;
1655     UInt32 propertySize = sizeof(AudioObjectID);
1656     AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1657     propertySize = sizeof(AudioObjectID);
1658     OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
1659     if (err != noErr) {
1660         msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
1661         return -1;
1662     }
1663
1664     msg_Dbg(p_aout, "default device changed to %i", defaultDeviceID);
1665
1666     /* Default device is changed by the os to allow other apps to play sound while in digital
1667        mode. But this should not affect ourself. */
1668     if (p_aout->sys->b_digital) {
1669         msg_Dbg(p_aout, "ignore, as digital mode is active");
1670         return noErr;
1671     }
1672
1673     vlc_mutex_lock(&p_sys->selected_device_lock);
1674     /* Also ignore events which announce the same device id */
1675     if(defaultDeviceID != p_aout->sys->i_selected_dev) {
1676         msg_Dbg(p_aout, "default device actually changed, resetting aout");
1677         aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1678     }
1679     vlc_mutex_unlock(&p_sys->selected_device_lock);
1680
1681     return noErr;
1682 }
1683
1684 /*
1685  * Callback when physical formats for device change
1686  */
1687 static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1688 {
1689     OSStatus                    err = noErr;
1690     UInt32                      i_param_size = 0;
1691     AudioStreamID               *p_streams = NULL;
1692     int                         i_streams = 0;
1693
1694     VLC_UNUSED(inNumberAddresses);
1695     VLC_UNUSED(inAddresses);
1696
1697     audio_output_t *p_aout = (audio_output_t *)inClientData;
1698     if (!p_aout)
1699         return -1;
1700
1701     aout_sys_t *p_sys = p_aout->sys;
1702     if(unlikely(p_sys->b_ignore_streams_changed_callback == true))
1703         return 0;
1704
1705     msg_Dbg(p_aout, "available physical formats for audio device changed");
1706     RebuildDeviceList(p_aout);
1707
1708     vlc_mutex_lock(&p_sys->selected_device_lock);
1709     /* In this case audio has not yet started. Below code will not work and is not needed here. */
1710     if (p_sys->i_selected_dev == 0) {
1711         vlc_mutex_unlock(&p_sys->selected_device_lock);
1712         return 0;
1713     }
1714
1715     /*
1716      * check if changed stream id belongs to current device
1717      */
1718     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1719     err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
1720     if (err != noErr) {
1721         msg_Err(p_aout, "could not get number of streams for device %i [%4.4s]", p_sys->i_selected_dev, (char *)&err);
1722         vlc_mutex_unlock(&p_sys->selected_device_lock);
1723         return VLC_EGENERIC;
1724     }
1725
1726     i_streams = i_param_size / sizeof(AudioStreamID);
1727     p_streams = (AudioStreamID *)malloc(i_param_size);
1728     if (p_streams == NULL) {
1729         vlc_mutex_unlock(&p_sys->selected_device_lock);
1730         return VLC_ENOMEM;
1731     }
1732
1733     err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1734     if (err != noErr) {
1735         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1736         vlc_mutex_unlock(&p_sys->selected_device_lock);
1737         free(p_streams);
1738         return VLC_EGENERIC;
1739     }
1740     vlc_mutex_unlock(&p_sys->selected_device_lock);
1741
1742     for (int i = 0; i < i_streams; i++) {
1743         if (p_streams[i] == inObjectID) {
1744             msg_Dbg(p_aout, "Restart aout as this affects current device");
1745             aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1746             break;
1747         }
1748     }
1749     free(p_streams);
1750
1751     return noErr;
1752 }
1753
1754 /*
1755  * StreamListener: check whether the device's physical format change is complete
1756  */
1757 static OSStatus StreamListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1758 {
1759     OSStatus err = noErr;
1760     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1761
1762     VLC_UNUSED(inObjectID);
1763
1764     for (unsigned int i = 0; i < inNumberAddresses; i++) {
1765         if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
1766             int canc = vlc_savecancel();
1767             vlc_mutex_lock(&w->lock);
1768             vlc_cond_signal(&w->cond);
1769             vlc_mutex_unlock(&w->lock);
1770             vlc_restorecancel(canc);
1771             break;
1772         }
1773     }
1774     return err;
1775 }
1776
1777 #pragma mark -
1778 #pragma mark helpers
1779
1780 static int ManageAudioStreamsCallback(audio_output_t *p_aout, AudioDeviceID i_dev_id, bool b_register)
1781 {
1782     OSStatus                    err = noErr;
1783     UInt32                      i_param_size = 0;
1784     AudioStreamID               *p_streams = NULL;
1785     int                         i_streams = 0;
1786
1787     /* Retrieve all the output streams */
1788     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1789     err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1790     if (err != noErr) {
1791         msg_Err(p_aout, "could not get number of streams for device id %i [%4.4s]", i_dev_id, (char *)&err);
1792         return VLC_EGENERIC;
1793     }
1794
1795     i_streams = i_param_size / sizeof(AudioStreamID);
1796     p_streams = (AudioStreamID *)malloc(i_param_size);
1797     if (p_streams == NULL)
1798         return VLC_ENOMEM;
1799
1800     err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1801     if (err != noErr) {
1802         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1803         free(p_streams);
1804         return VLC_EGENERIC;
1805     }
1806
1807     for (int i = 0; i < i_streams; i++) {
1808         /* get notified when physical formats change */
1809         AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1810
1811         if (b_register) {
1812             err = AudioObjectAddPropertyListener(p_streams[i], &physicalFormatsAddress, StreamsChangedListener, (void *)p_aout);
1813             if (err != noErr) {
1814                 // nope just means that we already have a callback
1815                 if (err == kAudioHardwareIllegalOperationError) {
1816                     msg_Warn(p_aout, "could not set audio stream formats property callback on stream id %i, callback already set? [%4.4s]", p_streams[i],
1817                              (char *)&err);
1818                 } else {
1819                     msg_Err(p_aout, "could not set audio stream formats property callback on stream id %i [%4.4s]", p_streams[i],
1820                             (char *)&err);
1821                 }
1822             }
1823
1824         } else {  /* unregister callback */
1825             err = AudioObjectRemovePropertyListener(p_streams[i], &physicalFormatsAddress, StreamsChangedListener, (void *)p_aout);
1826             if (err != noErr)
1827                 msg_Err(p_aout, "failed to remove audio device property streams callback [%4.4s]", (char *)&err);
1828         }
1829
1830     }
1831
1832     free(p_streams);
1833     return VLC_SUCCESS;
1834 }
1835
1836 /*
1837  * AudioDeviceHasOutput: Checks if the device is actually an output device
1838  */
1839 static int AudioDeviceHasOutput(AudioDeviceID i_dev_id)
1840 {
1841     UInt32 dataSize = 0;
1842     OSStatus status;
1843
1844     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1845     status = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &dataSize);
1846
1847     if (dataSize == 0 || status != noErr)
1848         return FALSE;
1849
1850     return TRUE;
1851 }
1852
1853 /*
1854  * AudioDeviceSupportsDigital: Checks if device supports raw bitstreams
1855  */
1856 static int AudioDeviceSupportsDigital(audio_output_t *p_aout, AudioDeviceID i_dev_id)
1857 {
1858     OSStatus                    err = noErr;
1859     UInt32                      i_param_size = 0;
1860     AudioStreamID               *p_streams = NULL;
1861     int                         i_streams = 0;
1862     bool                        b_return = false;
1863
1864     /* Retrieve all the output streams */
1865     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1866     err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1867     if (err != noErr) {
1868         msg_Err(p_aout, "could not get number of streams [%4.4s] (%i)", (char *)&err, (int32_t)err);
1869         return false;
1870     }
1871
1872     i_streams = i_param_size / sizeof(AudioStreamID);
1873     p_streams = (AudioStreamID *)malloc(i_param_size);
1874     if (p_streams == NULL)
1875         return VLC_ENOMEM;
1876
1877     err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1878     if (err != noErr) {
1879         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1880         free(p_streams);
1881         return false;
1882     }
1883
1884     for (int i = 0; i < i_streams; i++) {
1885         if (AudioStreamSupportsDigital(p_aout, p_streams[i]))
1886             b_return = true;
1887     }
1888
1889     free(p_streams);
1890     return b_return;
1891 }
1892
1893 /*
1894  * AudioStreamSupportsDigital: Checks if audio stream is compatible with raw bitstreams
1895  */
1896 static int AudioStreamSupportsDigital(audio_output_t *p_aout, AudioStreamID i_stream_id)
1897 {
1898     OSStatus                    err = noErr;
1899     UInt32                      i_param_size = 0;
1900     AudioStreamRangedDescription *p_format_list = NULL;
1901     int                         i_formats = 0;
1902     bool                        b_return = false;
1903
1904     /* Retrieve all the stream formats supported by each output stream */
1905     AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1906     err = AudioObjectGetPropertyDataSize(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size);
1907     if (err != noErr) {
1908         msg_Err(p_aout, "could not get number of streamformats [%4.4s] (%i)", (char *)&err, (int32_t)err);
1909         return false;
1910     }
1911
1912     i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
1913     msg_Dbg(p_aout, "found %i stream formats for stream id %i", i_formats, i_stream_id);
1914
1915     p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
1916     if (p_format_list == NULL)
1917         return false;
1918
1919     err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
1920     if (err != noErr) {
1921         msg_Err(p_aout, "could not get the list of streamformats [%4.4s]", (char *)&err);
1922         free(p_format_list);
1923         p_format_list = NULL;
1924         return false;
1925     }
1926
1927     for (int i = 0; i < i_formats; i++) {
1928 #ifndef NDEBUG
1929         msg_Dbg(p_aout, STREAM_FORMAT_MSG("supported format: ", p_format_list[i].mFormat));
1930 #endif
1931
1932         if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
1933             p_format_list[i].mFormat.mFormatID == 'iac3' ||
1934             p_format_list[i].mFormat.mFormatID == kAudioFormat60958AC3 ||
1935             p_format_list[i].mFormat.mFormatID == kAudioFormatAC3)
1936             b_return = true;
1937     }
1938
1939     free(p_format_list);
1940     return b_return;
1941 }
1942
1943 /*
1944  * AudioStreamChangeFormat: switch stream format based on the provided description
1945  */
1946 static int AudioStreamChangeFormat(audio_output_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format)
1947 {
1948     OSStatus err = noErr;
1949     int retValue = false;
1950
1951     AudioObjectPropertyAddress physicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1952
1953     struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1954
1955     msg_Dbg(p_aout, STREAM_FORMAT_MSG("setting stream format: ", change_format));
1956
1957     /* Condition because SetProperty is asynchronious */
1958     vlc_cond_init(&w.cond);
1959     vlc_mutex_init(&w.lock);
1960     vlc_mutex_lock(&w.lock);
1961
1962     /* Install the callback */
1963     err = AudioObjectAddPropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1964     if (err != noErr) {
1965         msg_Err(p_aout, "AudioObjectAddPropertyListener for kAudioStreamPropertyPhysicalFormat failed [%4.4s]", (char *)&err);
1966         retValue = false;
1967         goto out;
1968     }
1969
1970     /* change the format */
1971     err = AudioObjectSetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, sizeof(AudioStreamBasicDescription),
1972                                      &change_format);
1973     if (err != noErr) {
1974         msg_Err(p_aout, "could not set the stream format [%4.4s]", (char *)&err);
1975         retValue = false;
1976         goto out;
1977     }
1978
1979     /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1980      * it is also not atomic in its behaviour.
1981      * Therefore we check 9 times before we really give up.
1982      */
1983     AudioStreamBasicDescription actual_format;
1984     UInt32 i_param_size = sizeof(AudioStreamBasicDescription);
1985     for (int i = 0; i < 9; i++) {
1986         /* Callback is not always invoked. So first check if format is already set. */
1987         if (i > 0) {
1988             mtime_t timeout = mdate() + 500000;
1989             if (vlc_cond_timedwait(&w.cond, &w.lock, timeout))
1990                 msg_Dbg(p_aout, "reached timeout");
1991         }
1992
1993         err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, &i_param_size, &actual_format);
1994
1995         msg_Dbg(p_aout, STREAM_FORMAT_MSG("actual format in use: ", actual_format));
1996         if (actual_format.mSampleRate == change_format.mSampleRate &&
1997             actual_format.mFormatID == change_format.mFormatID &&
1998             actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
1999             /* The right format is now active */
2000             retValue = true;
2001             break;
2002         }
2003
2004         /* We need to check again */
2005     }
2006
2007 out:
2008     vlc_mutex_unlock(&w.lock);
2009
2010     /* Removing the property listener */
2011     err = AudioObjectRemovePropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
2012     if (err != noErr) {
2013         msg_Err(p_aout, "AudioStreamRemovePropertyListener failed [%4.4s]", (char *)&err);
2014         retValue = false;
2015     }
2016
2017     vlc_mutex_destroy(&w.lock);
2018     vlc_cond_destroy(&w.cond);
2019
2020     return retValue;
2021 }