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