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