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