dialog: use new API
[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>                      // vlc_dialog_display_error
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         vlc_dialog_display_error(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                 vlc_dialog_display_error(p_aout,
634                     _("Audio device is not configured"), "%s",
635                     _("You should configure your speaker layout with "
636                     "\"Audio Midi Setup\" in /Applications/"
637                     "Utilities. VLC will output Stereo only."));
638             }
639         }
640         free(layout);
641     } else {
642         msg_Warn(p_aout, "device driver does not support kAudioDevicePropertyPreferredChannelLayout - using stereo fallback [%4.4s]", (char *)&err);
643         fmt->i_physical_channels = AOUT_CHANS_STEREO;
644     }
645     fmt->i_original_channels = fmt->i_physical_channels;
646
647     msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
648     msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
649
650     /* Now we set the INPUT layout of the AU */
651     AudioChannelLayout input_layout;
652     memset (&input_layout, 0, sizeof(input_layout));
653     uint32_t chans_out[AOUT_CHAN_MAX];
654
655     /* Some channel abbreviations used below:
656      * L - left
657      * R - right
658      * C - center
659      * Ls - left surround
660      * Rs - right surround
661      * Cs - center surround
662      * Rls - rear left surround
663      * Rrs - rear right surround
664      * Lw - left wide
665      * Rw - right wide
666      * Lsd - left surround direct
667      * Rsd - right surround direct
668      * Lc - left center
669      * Rc - right center
670      * Ts - top surround
671      * Vhl - vertical height left
672      * Vhc - vertical height center
673      * Vhr - vertical height right
674      * Lt - left matrix total. for matrix encoded stereo.
675      * Rt - right matrix total. for matrix encoded stereo. */
676
677     switch(aout_FormatNbChannels(fmt)) {
678         case 1:
679             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
680             break;
681         case 2:
682             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
683             break;
684         case 3:
685             if (fmt->i_physical_channels & AOUT_CHAN_CENTER)
686                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
687             else if (fmt->i_physical_channels & AOUT_CHAN_LFE)
688                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
689             break;
690         case 4:
691             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
692                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
693             else if (fmt->i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
694                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
695             else if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
696                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
697             break;
698         case 5:
699             if (fmt->i_physical_channels & (AOUT_CHAN_CENTER))
700                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
701             else if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
702                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
703             break;
704         case 6:
705             if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) {
706                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
707
708                 chans_out[0] = AOUT_CHAN_LEFT;
709                 chans_out[1] = AOUT_CHAN_RIGHT;
710                 chans_out[2] = AOUT_CHAN_REARLEFT;
711                 chans_out[3] = AOUT_CHAN_REARRIGHT;
712                 chans_out[4] = AOUT_CHAN_CENTER;
713                 chans_out[5] = AOUT_CHAN_LFE;
714
715                 p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
716                 if (p_aout->sys->chans_to_reorder)
717                     msg_Dbg(p_aout, "channel reordering needed for 5.1 output");
718             } else {
719                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
720
721                 chans_out[0] = AOUT_CHAN_LEFT;
722                 chans_out[1] = AOUT_CHAN_RIGHT;
723                 chans_out[2] = AOUT_CHAN_REARLEFT;
724                 chans_out[3] = AOUT_CHAN_REARRIGHT;
725                 chans_out[4] = AOUT_CHAN_CENTER;
726                 chans_out[5] = AOUT_CHAN_REARCENTER;
727
728                 p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
729                 if (p_aout->sys->chans_to_reorder)
730                     msg_Dbg(p_aout, "channel reordering needed for 6.0 output");
731             }
732             break;
733         case 7:
734             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
735
736             chans_out[0] = AOUT_CHAN_LEFT;
737             chans_out[1] = AOUT_CHAN_RIGHT;
738             chans_out[2] = AOUT_CHAN_CENTER;
739             chans_out[3] = AOUT_CHAN_LFE;
740             chans_out[4] = AOUT_CHAN_REARLEFT;
741             chans_out[5] = AOUT_CHAN_REARRIGHT;
742             chans_out[6] = AOUT_CHAN_REARCENTER;
743
744             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
745             if (p_aout->sys->chans_to_reorder)
746                 msg_Dbg(p_aout, "channel reordering needed for 6.1 output");
747
748             break;
749         case 8:
750             if (fmt->i_physical_channels & (AOUT_CHAN_LFE) || currentMinorSystemVersion < 7) {
751                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
752
753                 chans_out[0] = AOUT_CHAN_LEFT;
754                 chans_out[1] = AOUT_CHAN_RIGHT;
755                 chans_out[2] = AOUT_CHAN_CENTER;
756                 chans_out[3] = AOUT_CHAN_LFE;
757                 chans_out[4] = AOUT_CHAN_MIDDLELEFT;
758                 chans_out[5] = AOUT_CHAN_MIDDLERIGHT;
759                 chans_out[6] = AOUT_CHAN_REARLEFT;
760                 chans_out[7] = AOUT_CHAN_REARRIGHT;
761
762                 if (!(fmt->i_physical_channels & (AOUT_CHAN_LFE)))
763                     msg_Warn(p_aout, "8.0 audio output not supported on OS X 10.%i, layout will be incorrect", currentMinorSystemVersion);
764             }
765 #ifdef MAC_OS_X_VERSION_10_7
766             else {
767                 input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DTS_8_0_B; // Lc C Rc L R Ls Cs Rs
768
769                 chans_out[0] = AOUT_CHAN_MIDDLELEFT;
770                 chans_out[1] = AOUT_CHAN_CENTER;
771                 chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
772                 chans_out[3] = AOUT_CHAN_LEFT;
773                 chans_out[4] = AOUT_CHAN_RIGHT;
774                 chans_out[5] = AOUT_CHAN_REARLEFT;
775                 chans_out[6] = AOUT_CHAN_REARCENTER;
776                 chans_out[7] = AOUT_CHAN_REARRIGHT;
777             }
778 #endif
779             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
780             if (p_aout->sys->chans_to_reorder)
781                 msg_Dbg(p_aout, "channel reordering needed for 7.1 / 8.0 output");
782
783             break;
784         case 9:
785             if (currentMinorSystemVersion < 7) {
786                 msg_Warn(p_aout, "8.1 audio output not supported on OS X 10.%i", currentMinorSystemVersion);
787                 break;
788             }
789
790 #ifdef MAC_OS_X_VERSION_10_7
791             input_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DTS_8_1_B; // Lc C Rc L R Ls Cs Rs LFE
792             chans_out[0] = AOUT_CHAN_MIDDLELEFT;
793             chans_out[1] = AOUT_CHAN_CENTER;
794             chans_out[2] = AOUT_CHAN_MIDDLERIGHT;
795             chans_out[3] = AOUT_CHAN_LEFT;
796             chans_out[4] = AOUT_CHAN_RIGHT;
797             chans_out[5] = AOUT_CHAN_REARLEFT;
798             chans_out[6] = AOUT_CHAN_REARCENTER;
799             chans_out[7] = AOUT_CHAN_REARRIGHT;
800             chans_out[8] = AOUT_CHAN_LFE;
801
802             p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
803             if (p_aout->sys->chans_to_reorder)
804                 msg_Dbg(p_aout, "channel reordering needed for 8.1 output");
805 #endif
806             break;
807     }
808
809     /* Set up the format to be used */
810     DeviceFormat.mSampleRate = fmt->i_rate;
811     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
812     p_sys->i_rate = fmt->i_rate;
813
814     /* We use float 32 since this is VLC's endorsed format */
815     fmt->i_format = VLC_CODEC_FL32;
816     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
817     DeviceFormat.mBitsPerChannel = 32;
818     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(fmt);
819
820     /* Calculate framesizes and stuff */
821     DeviceFormat.mFramesPerPacket = 1;
822     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
823     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
824
825     /* Set the desired format */
826     i_param_size = sizeof(AudioStreamBasicDescription);
827     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
828                                    kAudioUnitProperty_StreamFormat,
829                                    kAudioUnitScope_Input,
830                                    0,
831                                    &DeviceFormat,
832                                    i_param_size));
833
834     msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , DeviceFormat));
835
836     /* Retrieve actual format */
837     verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
838                                    kAudioUnitProperty_StreamFormat,
839                                    kAudioUnitScope_Input,
840                                    0,
841                                    &DeviceFormat,
842                                    &i_param_size));
843
844     msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , DeviceFormat));
845
846     /* Do the last VLC aout setups */
847     aout_FormatPrepare(fmt);
848
849     /* set the IOproc callback */
850     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
851     input.inputProcRefCon = p_aout;
852
853     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
854                             kAudioUnitProperty_SetRenderCallback,
855                             kAudioUnitScope_Input,
856                             0, &input, sizeof(input)));
857
858     /* Set the input_layout as the layout VLC will use to feed the AU unit */
859     verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
860                             kAudioUnitProperty_AudioChannelLayout,
861                             kAudioUnitScope_Input, /* yes, it must be the INPUT scope */
862                             0, &input_layout, sizeof(input_layout)));
863
864     /* AU initiliaze */
865     verify_noerr(AudioUnitInitialize(p_sys->au_unit));
866
867     /* setup circular buffer */
868     TPCircularBufferInit(&p_sys->circular_buffer, AUDIO_BUFFER_SIZE_IN_SECONDS *
869                          fmt->i_rate * fmt->i_bytes_per_frame);
870
871     verify_noerr(AudioOutputUnitStart(p_sys->au_unit));
872
873     /* Set volume for output unit */
874     VolumeSet(p_aout, p_sys->f_volume);
875     MuteSet(p_aout, p_sys->b_mute);
876
877     return true;
878 }
879
880 /*
881  * StartSPDIF: Setup an encoded digital stream (SPDIF) output
882  */
883 static int StartSPDIF(audio_output_t * p_aout, audio_sample_format_t *fmt)
884 {
885     struct aout_sys_t       *p_sys = p_aout->sys;
886     OSStatus                err = noErr;
887     UInt32                  i_param_size = 0, b_mix = 0;
888     Boolean                 b_writeable = false;
889     AudioStreamID           *p_streams = NULL;
890     unsigned                i_streams = 0;
891     AudioStreamBasicDescription desired_stream_format;
892     memset(&desired_stream_format, 0, sizeof(desired_stream_format));
893
894     /* Start doing the SPDIF setup proces */
895     p_sys->b_digital = true;
896
897     /* Hog the device */
898     AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
899     i_param_size = sizeof(p_sys->i_hog_pid);
900     p_sys->i_hog_pid = getpid() ;
901
902     /*
903      * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
904      * directly in the same thread. This call needs to be ignored to avoid endless restarting.
905      */
906     p_sys->b_ignore_streams_changed_callback = true;
907     err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
908     p_sys->b_ignore_streams_changed_callback = false;
909
910     if (err != noErr) {
911         msg_Err(p_aout, "failed to set hogmode [%4.4s]", (char *)&err);
912         return false;
913     }
914
915     AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
916
917     if (AudioObjectHasProperty(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress)) {
918         /* Set mixable to false if we are allowed to */
919         err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
920         err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size);
921         err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
922
923         if (err == noErr && b_writeable) {
924             b_mix = 0;
925             err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
926             p_sys->b_changed_mixing = true;
927         }
928
929         if (err != noErr) {
930             msg_Err(p_aout, "failed to set mixmode [%4.4s]", (char *)&err);
931             return false;
932         }
933     }
934
935     /* Get a list of all the streams on this device */
936     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
937     err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
938     if (err != noErr) {
939         msg_Err(p_aout, "could not get size of stream description packet [%4.4s]", (char *)&err);
940         return false;
941     }
942
943     i_streams = i_param_size / sizeof(AudioStreamID);
944     p_streams = (AudioStreamID *)malloc(i_param_size);
945     if (p_streams == NULL)
946         return false;
947
948     err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
949
950     if (err != noErr) {
951         msg_Err(p_aout, "could not fetch stream descriptions [%4.4s]", (char *)&err);
952         free(p_streams);
953         return false;
954     }
955
956     AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
957     for (unsigned i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++) {
958         /* Find a stream with a cac3 stream */
959         AudioStreamRangedDescription *p_format_list = NULL;
960         int                          i_formats = 0;
961         bool                         b_digital = false;
962
963         /* Retrieve all the stream formats supported by each output stream */
964         err = AudioObjectGetPropertyDataSize(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size);
965         if (err != noErr) {
966             msg_Err(p_aout, "could not get number of streamformats: [%4.4s] (%i)", (char *)&err, (int32_t)err);
967             continue;
968         }
969
970         i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
971         p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
972         if (p_format_list == NULL)
973             continue;
974
975         err = AudioObjectGetPropertyData(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
976         if (err != noErr) {
977             msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
978             free(p_format_list);
979             continue;
980         }
981
982         /* Check if one of the supported formats is a digital format */
983         for (int j = 0; j < i_formats; j++) {
984             if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
985                p_format_list[j].mFormat.mFormatID == 'iac3' ||
986                p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
987                p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
988                 b_digital = true;
989                 break;
990             }
991         }
992
993         if (b_digital) {
994             /* if this stream supports a digital (cac3) format, then go set it. */
995             int i_requested_rate_format = -1;
996             int i_current_rate_format = -1;
997             int i_backup_rate_format = -1;
998
999             if (!p_sys->b_revert) {
1000                 AudioObjectPropertyAddress currentPhysicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1001                 /* Retrieve the original format of this stream first if not done so already */
1002                 AudioStreamBasicDescription current_streamformat;
1003                 i_param_size = sizeof(current_streamformat);
1004                 err = AudioObjectGetPropertyData(p_streams[i], &currentPhysicalFormatAddress, 0, NULL, &i_param_size, &current_streamformat);
1005                 if (err != noErr) {
1006                     msg_Err(p_aout, "could not retrieve the original streamformat [%4.4s]", (char *)&err);
1007                     continue;
1008                 }
1009
1010                 /* 
1011                  * Only the first found format id is accepted. In case of another id later on, we still use the
1012                  * already saved one. This can happen if the user plugs in a spdif cable while a stream is already
1013                  * playing. Then, auhal already misleadingly reports an ac3 format here whereas the original format
1014                  * should be still pcm.
1015                  */
1016                 if (p_sys->sfmt_revert.mFormatID > 0 && p_sys->sfmt_revert.mFormatID != current_streamformat.mFormatID &&
1017                         p_streams[i] == p_sys->i_stream_id) {
1018                     msg_Warn(p_aout, STREAM_FORMAT_MSG("Detected current stream format: ", current_streamformat));
1019                     msg_Warn(p_aout, "... there is another stream format already stored, the current one is ignored");
1020                 } else {
1021                     p_sys->sfmt_revert = current_streamformat;
1022                 }
1023
1024                 p_sys->b_revert = true;
1025             }
1026
1027             p_sys->i_stream_id = p_streams[i];
1028             p_sys->i_stream_index = i;
1029
1030             for (int j = 0; j < i_formats; j++) {
1031                 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
1032                    p_format_list[j].mFormat.mFormatID == 'iac3' ||
1033                    p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
1034                    p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
1035                     if (p_format_list[j].mFormat.mSampleRate == fmt->i_rate) {
1036                         i_requested_rate_format = j;
1037                         break;
1038                     } else if (p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate)
1039                         i_current_rate_format = j;
1040                     else {
1041                         if (i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate)
1042                             i_backup_rate_format = j;
1043                     }
1044                 }
1045
1046             }
1047
1048             if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio */
1049                 desired_stream_format = p_format_list[i_requested_rate_format].mFormat;
1050             else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device */
1051                 desired_stream_format = p_format_list[i_current_rate_format].mFormat;
1052             else
1053                 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) */
1054         }
1055         free(p_format_list);
1056     }
1057     free(p_streams);
1058
1059     msg_Dbg(p_aout, STREAM_FORMAT_MSG("original stream format: ", p_sys->sfmt_revert));
1060
1061     if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, desired_stream_format)) {
1062         msg_Err(p_aout, "failed to change stream format for SPDIF output");
1063         return false;
1064     }
1065
1066     /* Set the format flags */
1067     if (desired_stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
1068         fmt->i_format = VLC_CODEC_SPDIFB;
1069     else
1070         fmt->i_format = VLC_CODEC_SPDIFL;
1071     fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
1072     fmt->i_frame_length = A52_FRAME_NB;
1073     fmt->i_rate = (unsigned int)desired_stream_format.mSampleRate;
1074     p_sys->i_rate = fmt->i_rate;
1075     aout_FormatPrepare(fmt);
1076
1077     /* Add IOProc callback */
1078     err = AudioDeviceCreateIOProcID(p_sys->i_selected_dev,
1079                                    (AudioDeviceIOProc)RenderCallbackSPDIF,
1080                                    (void *)p_aout,
1081                                    &p_sys->i_procID);
1082     if (err != noErr) {
1083         msg_Err(p_aout, "Failed to create Process ID [%4.4s]", (char *)&err);
1084         return false;
1085     }
1086
1087     /* Start device */
1088     err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
1089     if (err != noErr) {
1090         msg_Err(p_aout, "Failed to start audio device [%4.4s]", (char *)&err);
1091
1092         err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev, p_sys->i_procID);
1093         if (err != noErr)
1094             msg_Err(p_aout, "Failed to destroy process ID [%4.4s]", (char *)&err);
1095
1096         return false;
1097     }
1098
1099     /* setup circular buffer */
1100     TPCircularBufferInit(&p_sys->circular_buffer, 200 * AOUT_SPDIF_SIZE);
1101
1102     return true;
1103 }
1104
1105 static void Stop(audio_output_t *p_aout)
1106 {
1107     struct aout_sys_t   *p_sys = p_aout->sys;
1108     OSStatus            err = noErr;
1109     UInt32              i_param_size = 0;
1110
1111     msg_Dbg(p_aout, "Stopping the auhal module");
1112
1113     if (p_sys->au_unit) {
1114         verify_noerr(AudioOutputUnitStop(p_sys->au_unit));
1115         verify_noerr(AudioUnitUninitialize(p_sys->au_unit));
1116         verify_noerr(AudioComponentInstanceDispose(p_sys->au_unit));
1117     }
1118
1119     if (p_sys->b_digital) {
1120         /* Stop device */
1121         err = AudioDeviceStop(p_sys->i_selected_dev,
1122                                p_sys->i_procID);
1123         if (err != noErr)
1124             msg_Err(p_aout, "Failed to stop audio device [%4.4s]", (char *)&err);
1125
1126         /* Remove IOProc callback */
1127         err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev,
1128                                           p_sys->i_procID);
1129         if (err != noErr)
1130             msg_Err(p_aout, "Failed to destroy Process ID [%4.4s]", (char *)&err);
1131
1132         if (p_sys->b_revert) {
1133             if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->sfmt_revert)) {
1134                 msg_Err(p_aout, "failed to revert stream format in close");
1135             }
1136         }
1137
1138         if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
1139             int b_mix;
1140             Boolean b_writeable = false;
1141             i_param_size = sizeof(int);
1142             /* Revert mixable to true if we are allowed to */
1143             AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1144             err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
1145             err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
1146
1147             if (err == noErr && b_writeable) {
1148                 msg_Dbg(p_aout, "mixable is: %d", b_mix);
1149                 b_mix = 1;
1150                 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
1151             }
1152
1153             if (err != noErr)
1154                 msg_Err(p_aout, "failed to re-set mixmode [%4.4s]", (char *)&err);
1155         }
1156     }
1157
1158     if (p_sys->i_hog_pid == getpid()) {
1159         p_sys->i_hog_pid = -1;
1160         i_param_size = sizeof(p_sys->i_hog_pid);
1161         AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
1162             kAudioDevicePropertyScopeOutput,
1163             kAudioObjectPropertyElementMaster };
1164
1165         /*
1166          * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
1167          * directly in the same thread. This call needs to be ignored to avoid endless restarting.
1168          */
1169         p_sys->b_ignore_streams_changed_callback = true;
1170         err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
1171         p_sys->b_ignore_streams_changed_callback = false;
1172         if (err != noErr)
1173             msg_Err(p_aout, "Failed to release hogmode [%4.4s]", (char *)&err);
1174     }
1175
1176     /* remove audio device alive callback */
1177     AudioObjectPropertyAddress deviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1178     err = AudioObjectRemovePropertyListener(p_sys->i_selected_dev, &deviceAliveAddress, DeviceAliveListener, (void *)p_aout);
1179     if (err != noErr) {
1180         /* Be tolerant, only give a warning here */
1181         msg_Warn(p_aout, "failed to remove audio device life checker [%4.4s]", (char *)&err);
1182     }
1183
1184     p_sys->i_bytes_per_sample = 0;
1185     p_sys->b_digital = false;
1186
1187     /* clean-up circular buffer */
1188     TPCircularBufferCleanup(&p_sys->circular_buffer);
1189 }
1190
1191 #pragma mark -
1192 #pragma mark core interaction
1193
1194 static void ReportDevice(audio_output_t *p_aout, UInt32 i_id, char *name)
1195 {
1196     char deviceid[10];
1197     sprintf(deviceid, "%i", i_id);
1198
1199     aout_HotplugReport(p_aout, deviceid, name);
1200 }
1201
1202 static void RebuildDeviceList(audio_output_t * p_aout)
1203 {
1204     OSStatus            err = noErr;
1205     UInt32              propertySize = 0;
1206     AudioObjectID       *deviceIDs;
1207     UInt32              numberOfDevices;
1208     CFMutableArrayRef   currentListOfDevices;
1209
1210     struct aout_sys_t   *p_sys = p_aout->sys;
1211
1212     msg_Dbg(p_aout, "Rebuild device list");
1213
1214     ReportDevice(p_aout, 0, _("System Sound Output Device"));
1215
1216     /* setup local array */
1217     currentListOfDevices = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1218
1219     /* Get number of devices */
1220     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1221     err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &propertySize);
1222     if (err != noErr) {
1223         msg_Err(p_aout, "Could not get number of devices: [%4.4s]", (char *)&err);
1224         CFRelease(currentListOfDevices);
1225         return;
1226     }
1227
1228     numberOfDevices = propertySize / sizeof(AudioDeviceID);
1229
1230     if (numberOfDevices < 1) {
1231         msg_Err(p_aout, "No audio output devices found.");
1232         CFRelease(currentListOfDevices);
1233         return;
1234     }
1235     msg_Dbg(p_aout, "found %i audio device(s)", numberOfDevices);
1236
1237     /* Allocate DeviceID array */
1238     deviceIDs = (AudioDeviceID *)calloc(numberOfDevices, sizeof(AudioDeviceID));
1239     if (deviceIDs == NULL) {
1240         CFRelease(currentListOfDevices);
1241         return;
1242     }
1243
1244     /* Populate DeviceID array */
1245     err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &propertySize, deviceIDs);
1246     if (err != noErr) {
1247         msg_Err(p_aout, "could not get the device IDs [%4.4s]", (char *)&err);
1248         CFRelease(currentListOfDevices);
1249         free(deviceIDs);
1250         return;
1251     }
1252
1253     AudioObjectPropertyAddress deviceNameAddress = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1254
1255     for (unsigned int i = 0; i < numberOfDevices; i++) {
1256         CFStringRef device_name_ref;
1257         char *psz_name;
1258         CFIndex length;
1259         UInt32 i_id = deviceIDs[i];
1260
1261         /* Retrieve the length of the device name */
1262         err = AudioObjectGetPropertyDataSize(deviceIDs[i], &deviceNameAddress, 0, NULL, &propertySize);
1263         if (err != noErr) {
1264             msg_Dbg(p_aout, "failed to get name size for device %i", deviceIDs[i]);
1265             continue;
1266         }
1267
1268         /* Retrieve the name of the device */
1269         err = AudioObjectGetPropertyData(deviceIDs[i], &deviceNameAddress, 0, NULL, &propertySize, &device_name_ref);
1270         if (err != noErr) {
1271             msg_Dbg(p_aout, "failed to get name for device %i", deviceIDs[i]);
1272             continue;
1273         }
1274         length = CFStringGetLength(device_name_ref);
1275         length++;
1276         psz_name = malloc(length);
1277         if (!psz_name) {
1278             CFRelease(device_name_ref);
1279             return;
1280         }
1281         CFStringGetCString(device_name_ref, psz_name, length, kCFStringEncodingUTF8);
1282         CFRelease(device_name_ref);
1283
1284         msg_Dbg(p_aout, "DevID: %i DevName: %s", deviceIDs[i], psz_name);
1285
1286         if (!AudioDeviceHasOutput(deviceIDs[i])) {
1287             msg_Dbg(p_aout, "this '%s' is INPUT only. skipping...", psz_name);
1288             free(psz_name);
1289             continue;
1290         }
1291
1292         ReportDevice(p_aout, i_id, psz_name);
1293         CFNumberRef deviceNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i_id);
1294         CFArrayAppendValue(currentListOfDevices, deviceNumber);
1295         CFRelease(deviceNumber);
1296
1297         if (AudioDeviceSupportsDigital(p_aout, deviceIDs[i])) {
1298             msg_Dbg(p_aout, "'%s' supports digital output", psz_name);
1299             char *psz_encoded_name = nil;
1300             asprintf(&psz_encoded_name, _("%s (Encoded Output)"), psz_name);
1301             i_id = i_id | AOUT_VAR_SPDIF_FLAG;
1302             ReportDevice(p_aout, i_id, psz_encoded_name);
1303             deviceNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i_id);
1304             CFArrayAppendValue(currentListOfDevices, deviceNumber);
1305             CFRelease(deviceNumber);
1306             free(psz_encoded_name);
1307         }
1308
1309         // TODO: only register once for each device
1310         ManageAudioStreamsCallback(p_aout, deviceIDs[i], true);
1311
1312         free(psz_name);
1313     }
1314
1315     vlc_mutex_lock(&p_sys->device_list_lock);
1316     CFIndex count = 0;
1317     if (p_sys->device_list)
1318         count = CFArrayGetCount(p_sys->device_list);
1319     CFRange newListSearchRange = CFRangeMake(0, CFArrayGetCount(currentListOfDevices));
1320
1321     if (count > 0) {
1322         msg_Dbg(p_aout, "Looking for removed devices");
1323         CFNumberRef cfn_device_id;
1324         int i_device_id = 0;
1325         for (CFIndex x = 0; x < count; x++) {
1326             if (!CFArrayContainsValue(currentListOfDevices, newListSearchRange, CFArrayGetValueAtIndex(p_sys->device_list, x))) {
1327                 cfn_device_id = CFArrayGetValueAtIndex(p_sys->device_list, x);
1328                 if (cfn_device_id) {
1329                     CFNumberGetValue(cfn_device_id, kCFNumberSInt32Type, &i_device_id);
1330                     msg_Dbg(p_aout, "Device ID %i is not found in new array, deleting.", i_device_id);
1331
1332                     ReportDevice(p_aout, i_device_id, NULL);
1333                 }
1334             }
1335         }
1336     }
1337     if (p_sys->device_list)
1338         CFRelease(p_sys->device_list);
1339     p_sys->device_list = CFArrayCreateCopy(kCFAllocatorDefault, currentListOfDevices);
1340     CFRelease(currentListOfDevices);
1341     vlc_mutex_unlock(&p_sys->device_list_lock);
1342
1343     free(deviceIDs);
1344 }
1345
1346 static int SwitchAudioDevice(audio_output_t *p_aout, const char *name)
1347 {
1348     struct aout_sys_t *p_sys = p_aout->sys;
1349
1350     if (name)
1351         p_sys->i_new_selected_dev = atoi(name);
1352     else
1353         p_sys->i_new_selected_dev = 0;
1354
1355     bool b_supports_digital = (p_sys->i_new_selected_dev & AOUT_VAR_SPDIF_FLAG);
1356     if (b_supports_digital)
1357         p_sys->b_selected_dev_is_digital = true;
1358     else
1359         p_sys->b_selected_dev_is_digital = false;
1360
1361     p_sys->i_new_selected_dev = p_sys->i_new_selected_dev & ~AOUT_VAR_SPDIF_FLAG;
1362
1363     aout_DeviceReport(p_aout, name);
1364     aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1365
1366     return 0;
1367 }
1368
1369 static int VolumeSet(audio_output_t * p_aout, float volume)
1370 {
1371     struct aout_sys_t *p_sys = p_aout->sys;
1372     OSStatus ostatus = 0;
1373
1374     if(p_sys->b_digital)
1375         return VLC_EGENERIC;
1376
1377     p_sys->f_volume = volume;
1378     aout_VolumeReport(p_aout, volume);
1379
1380     /* Set volume for output unit */
1381     if(!p_sys->b_mute) {
1382         ostatus = AudioUnitSetParameter(p_sys->au_unit,
1383                                         kHALOutputParam_Volume,
1384                                         kAudioUnitScope_Global,
1385                                         0,
1386                                         volume * volume * volume,
1387                                         0);
1388     }
1389
1390     if (var_InheritBool(p_aout, "volume-save"))
1391         config_PutInt(p_aout, "auhal-volume", lroundf(volume * AOUT_VOLUME_DEFAULT));
1392
1393     return ostatus;
1394 }
1395
1396 static int MuteSet(audio_output_t * p_aout, bool mute)
1397 {
1398     struct   aout_sys_t *p_sys = p_aout->sys;
1399     OSStatus ostatus;
1400
1401     if(p_sys->b_digital)
1402         return VLC_EGENERIC;
1403
1404     p_sys->b_mute = mute;
1405     aout_MuteReport(p_aout, mute);
1406
1407     float volume = .0;
1408     if (!mute)
1409         volume = p_sys->f_volume;
1410
1411     ostatus = AudioUnitSetParameter(p_sys->au_unit,
1412                                     kHALOutputParam_Volume,
1413                                     kAudioUnitScope_Global,
1414                                     0,
1415                                     volume * volume * volume,
1416                                     0);
1417
1418     return ostatus;
1419 }
1420
1421 #pragma mark -
1422 #pragma mark actual playback
1423
1424 static void Play(audio_output_t * p_aout, block_t * p_block)
1425 {
1426     struct aout_sys_t *p_sys = p_aout->sys;
1427
1428     if (p_block->i_nb_samples > 0) {
1429         /* Do the channel reordering */
1430         if (p_sys->chans_to_reorder && !p_sys->b_digital) {
1431            aout_ChannelReorder(p_block->p_buffer,
1432                                p_block->i_buffer,
1433                                p_sys->chans_to_reorder,
1434                                p_sys->chan_table,
1435                                VLC_CODEC_FL32);
1436         }
1437
1438         /* move data to buffer */
1439         if (unlikely(!TPCircularBufferProduceBytes(&p_sys->circular_buffer, p_block->p_buffer, p_block->i_buffer)))
1440             msg_Warn(p_aout, "dropped buffer");
1441
1442         if (!p_sys->i_bytes_per_sample)
1443             p_sys->i_bytes_per_sample = p_block->i_buffer / p_block->i_nb_samples;
1444     }
1445
1446     block_Release(p_block);
1447 }
1448
1449 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
1450 {
1451     struct aout_sys_t * p_sys = p_aout->sys;
1452     VLC_UNUSED(date);
1453
1454     vlc_mutex_lock(&p_sys->lock);
1455     p_sys->b_paused = pause;
1456     vlc_mutex_unlock(&p_sys->lock);
1457 }
1458
1459 static void Flush(audio_output_t *p_aout, bool wait)
1460 {
1461     struct aout_sys_t *p_sys = p_aout->sys;
1462
1463     int32_t availableBytes;
1464     vlc_mutex_lock(&p_sys->lock);
1465     TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1466
1467     if (wait) {
1468         while (availableBytes > 0) {
1469             vlc_cond_wait(&p_sys->cond, &p_sys->lock);
1470             TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1471         }
1472     } else {
1473         /* flush circular buffer if data is left */
1474         if (availableBytes > 0)
1475             TPCircularBufferClear(&p_aout->sys->circular_buffer);
1476     }
1477
1478     vlc_mutex_unlock(&p_sys->lock);
1479 }
1480
1481 static int TimeGet(audio_output_t *p_aout, mtime_t *delay)
1482 {
1483     struct aout_sys_t * p_sys = p_aout->sys;
1484
1485     if (!p_sys->i_bytes_per_sample)
1486         return -1;
1487
1488     int32_t availableBytes;
1489     TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1490
1491     *delay = ((availableBytes / p_sys->i_bytes_per_sample) + p_sys->i_device_latency) * CLOCK_FREQ / p_sys->i_rate;
1492
1493     return 0;
1494 }
1495
1496 /*****************************************************************************
1497  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1498  * us to provide some more audio data.
1499  * Don't print anything during normal playback, calling blocking function from
1500  * this callback is not allowed.
1501  *****************************************************************************/
1502 static OSStatus RenderCallbackAnalog(vlc_object_t *p_obj,
1503                                     AudioUnitRenderActionFlags *ioActionFlags,
1504                                     const AudioTimeStamp *inTimeStamp,
1505                                     UInt32 inBusNumber,
1506                                     UInt32 inNumberFrames,
1507                                     AudioBufferList *ioData) {
1508     VLC_UNUSED(ioActionFlags);
1509     VLC_UNUSED(inTimeStamp);
1510     VLC_UNUSED(inBusNumber);
1511     VLC_UNUSED(inNumberFrames);
1512
1513     audio_output_t * p_aout = (audio_output_t *)p_obj;
1514     struct aout_sys_t * p_sys = p_aout->sys;
1515
1516     int bytesRequested = ioData->mBuffers[0].mDataByteSize;
1517     Float32 *targetBuffer = (Float32*)ioData->mBuffers[0].mData;
1518     if (unlikely(bytesRequested == 0)) /* cannot be negative */
1519         return noErr;
1520
1521     vlc_mutex_lock(&p_sys->lock);
1522     /* Pull audio from buffer */
1523     int32_t availableBytes;
1524     Float32 *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1525
1526     /* check if we have enough data */
1527     if (!availableBytes || p_sys->b_paused) {
1528         /* return an empty buffer so silence is played until we have data */
1529         memset(targetBuffer, 0, bytesRequested);
1530     } else {
1531         int32_t bytesToCopy = __MIN(bytesRequested, availableBytes);
1532
1533         memcpy(targetBuffer, buffer, bytesToCopy);
1534         TPCircularBufferConsume(&p_sys->circular_buffer, bytesToCopy);
1535         ioData->mBuffers[0].mDataByteSize = bytesToCopy;
1536     }
1537
1538     vlc_cond_signal(&p_sys->cond);
1539     vlc_mutex_unlock(&p_sys->lock);
1540
1541     return noErr;
1542 }
1543
1544 /*
1545  * RenderCallbackSPDIF: callback for SPDIF audio output
1546  */
1547 static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
1548                                     const AudioTimeStamp * inNow,
1549                                     const void * inInputData,
1550                                     const AudioTimeStamp * inInputTime,
1551                                     AudioBufferList * outOutputData,
1552                                     const AudioTimeStamp * inOutputTime,
1553                                     void * threadGlobals)
1554 {
1555     VLC_UNUSED(inNow);
1556     VLC_UNUSED(inDevice);
1557     VLC_UNUSED(inInputData);
1558     VLC_UNUSED(inInputTime);
1559     VLC_UNUSED(inOutputTime);
1560
1561     audio_output_t * p_aout = (audio_output_t *)threadGlobals;
1562     struct aout_sys_t * p_sys = p_aout->sys;
1563
1564     int bytesRequested = outOutputData->mBuffers[p_sys->i_stream_index].mDataByteSize;
1565     char *targetBuffer = outOutputData->mBuffers[p_sys->i_stream_index].mData;
1566     if (unlikely(bytesRequested == 0)) /* cannot be negative */
1567         return noErr;
1568
1569     vlc_mutex_lock(&p_sys->lock);
1570     /* Pull audio from buffer */
1571     int32_t availableBytes;
1572     char *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
1573
1574     /* check if we have enough data */
1575     if (!availableBytes || p_sys->b_paused) {
1576         /* return an empty buffer so silence is played until we have data */
1577         memset(targetBuffer, 0, bytesRequested);
1578     } else {
1579         int32_t bytesToCopy = __MIN(bytesRequested, availableBytes);
1580
1581         memcpy(targetBuffer, buffer, bytesToCopy);
1582         TPCircularBufferConsume(&p_sys->circular_buffer, bytesToCopy);
1583         outOutputData->mBuffers[p_sys->i_stream_index].mDataByteSize = bytesToCopy;
1584     }
1585
1586     vlc_cond_signal(&p_sys->cond);
1587     vlc_mutex_unlock(&p_sys->lock);
1588
1589     return noErr;
1590 }
1591
1592 #pragma mark -
1593 #pragma mark Stream / Hardware Listeners
1594
1595 /*
1596  * Callback when device list changed
1597  */
1598 static OSStatus DevicesListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1599 {
1600     VLC_UNUSED(inObjectID);
1601     VLC_UNUSED(inNumberAddresses);
1602     VLC_UNUSED(inAddresses);
1603
1604     audio_output_t *p_aout = (audio_output_t *)inClientData;
1605     if (!p_aout)
1606         return -1;
1607     aout_sys_t *p_sys = p_aout->sys;
1608
1609     msg_Dbg(p_aout, "audio device configuration changed, resetting cache");
1610     RebuildDeviceList(p_aout);
1611
1612     vlc_mutex_lock(&p_sys->selected_device_lock);
1613     vlc_mutex_lock(&p_sys->device_list_lock);
1614     CFNumberRef selectedDevice = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &p_sys->i_selected_dev);
1615     if(!CFArrayContainsValue(p_sys->device_list, CFRangeMake(0, CFArrayGetCount(p_sys->device_list)), selectedDevice))
1616         aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1617     CFRelease(selectedDevice);
1618     vlc_mutex_unlock(&p_sys->device_list_lock);
1619     vlc_mutex_unlock(&p_sys->selected_device_lock);
1620
1621     return noErr;
1622 }
1623
1624 /*
1625  * Callback when current device is not alive anymore
1626  */
1627 static OSStatus DeviceAliveListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1628 {
1629     VLC_UNUSED(inObjectID);
1630     VLC_UNUSED(inNumberAddresses);
1631     VLC_UNUSED(inAddresses);
1632
1633     audio_output_t *p_aout = (audio_output_t *)inClientData;
1634     if (!p_aout)
1635         return -1;
1636
1637     msg_Warn(p_aout, "audio device died, resetting aout");
1638     aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1639
1640     return noErr;
1641 }
1642
1643 /*
1644  * Callback when default audio device changed
1645  */
1646 static OSStatus DefaultDeviceChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1647 {
1648     VLC_UNUSED(inObjectID);
1649     VLC_UNUSED(inNumberAddresses);
1650     VLC_UNUSED(inAddresses);
1651
1652     audio_output_t *p_aout = (audio_output_t *)inClientData;
1653     if (!p_aout)
1654         return -1;
1655
1656     aout_sys_t *p_sys = p_aout->sys;
1657
1658     if (!p_aout->sys->b_selected_dev_is_default)
1659         return noErr;
1660
1661     AudioObjectID defaultDeviceID = 0;
1662     UInt32 propertySize = sizeof(AudioObjectID);
1663     AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1664     propertySize = sizeof(AudioObjectID);
1665     OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
1666     if (err != noErr) {
1667         msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
1668         return -1;
1669     }
1670
1671     msg_Dbg(p_aout, "default device changed to %i", defaultDeviceID);
1672
1673     /* Default device is changed by the os to allow other apps to play sound while in digital
1674        mode. But this should not affect ourself. */
1675     if (p_aout->sys->b_digital) {
1676         msg_Dbg(p_aout, "ignore, as digital mode is active");
1677         return noErr;
1678     }
1679
1680     vlc_mutex_lock(&p_sys->selected_device_lock);
1681     /* Also ignore events which announce the same device id */
1682     if(defaultDeviceID != p_aout->sys->i_selected_dev) {
1683         msg_Dbg(p_aout, "default device actually changed, resetting aout");
1684         aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1685     }
1686     vlc_mutex_unlock(&p_sys->selected_device_lock);
1687
1688     return noErr;
1689 }
1690
1691 /*
1692  * Callback when physical formats for device change
1693  */
1694 static OSStatus StreamsChangedListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1695 {
1696     OSStatus                    err = noErr;
1697     UInt32                      i_param_size = 0;
1698     AudioStreamID               *p_streams = NULL;
1699     int                         i_streams = 0;
1700
1701     VLC_UNUSED(inNumberAddresses);
1702     VLC_UNUSED(inAddresses);
1703
1704     audio_output_t *p_aout = (audio_output_t *)inClientData;
1705     if (!p_aout)
1706         return -1;
1707
1708     aout_sys_t *p_sys = p_aout->sys;
1709     if(unlikely(p_sys->b_ignore_streams_changed_callback == true))
1710         return 0;
1711
1712     msg_Dbg(p_aout, "available physical formats for audio device changed");
1713     RebuildDeviceList(p_aout);
1714
1715     vlc_mutex_lock(&p_sys->selected_device_lock);
1716     /* In this case audio has not yet started. Below code will not work and is not needed here. */
1717     if (p_sys->i_selected_dev == 0) {
1718         vlc_mutex_unlock(&p_sys->selected_device_lock);
1719         return 0;
1720     }
1721
1722     /*
1723      * check if changed stream id belongs to current device
1724      */
1725     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1726     err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
1727     if (err != noErr) {
1728         msg_Err(p_aout, "could not get number of streams for device %i [%4.4s]", p_sys->i_selected_dev, (char *)&err);
1729         vlc_mutex_unlock(&p_sys->selected_device_lock);
1730         return VLC_EGENERIC;
1731     }
1732
1733     i_streams = i_param_size / sizeof(AudioStreamID);
1734     p_streams = (AudioStreamID *)malloc(i_param_size);
1735     if (p_streams == NULL) {
1736         vlc_mutex_unlock(&p_sys->selected_device_lock);
1737         return VLC_ENOMEM;
1738     }
1739
1740     err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1741     if (err != noErr) {
1742         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1743         vlc_mutex_unlock(&p_sys->selected_device_lock);
1744         free(p_streams);
1745         return VLC_EGENERIC;
1746     }
1747     vlc_mutex_unlock(&p_sys->selected_device_lock);
1748
1749     for (int i = 0; i < i_streams; i++) {
1750         if (p_streams[i] == inObjectID) {
1751             msg_Dbg(p_aout, "Restart aout as this affects current device");
1752             aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
1753             break;
1754         }
1755     }
1756     free(p_streams);
1757
1758     return noErr;
1759 }
1760
1761 /*
1762  * StreamListener: check whether the device's physical format change is complete
1763  */
1764 static OSStatus StreamListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
1765 {
1766     OSStatus err = noErr;
1767     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1768
1769     VLC_UNUSED(inObjectID);
1770
1771     for (unsigned int i = 0; i < inNumberAddresses; i++) {
1772         if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
1773             int canc = vlc_savecancel();
1774             vlc_mutex_lock(&w->lock);
1775             vlc_cond_signal(&w->cond);
1776             vlc_mutex_unlock(&w->lock);
1777             vlc_restorecancel(canc);
1778             break;
1779         }
1780     }
1781     return err;
1782 }
1783
1784 #pragma mark -
1785 #pragma mark helpers
1786
1787 static int ManageAudioStreamsCallback(audio_output_t *p_aout, AudioDeviceID i_dev_id, bool b_register)
1788 {
1789     OSStatus                    err = noErr;
1790     UInt32                      i_param_size = 0;
1791     AudioStreamID               *p_streams = NULL;
1792     int                         i_streams = 0;
1793
1794     /* Retrieve all the output streams */
1795     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1796     err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1797     if (err != noErr) {
1798         msg_Err(p_aout, "could not get number of streams for device id %i [%4.4s]", i_dev_id, (char *)&err);
1799         return VLC_EGENERIC;
1800     }
1801
1802     i_streams = i_param_size / sizeof(AudioStreamID);
1803     p_streams = (AudioStreamID *)malloc(i_param_size);
1804     if (p_streams == NULL)
1805         return VLC_ENOMEM;
1806
1807     err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1808     if (err != noErr) {
1809         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1810         free(p_streams);
1811         return VLC_EGENERIC;
1812     }
1813
1814     for (int i = 0; i < i_streams; i++) {
1815         /* get notified when physical formats change */
1816         AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1817
1818         if (b_register) {
1819             err = AudioObjectAddPropertyListener(p_streams[i], &physicalFormatsAddress, StreamsChangedListener, (void *)p_aout);
1820             if (err != noErr) {
1821                 // nope just means that we already have a callback
1822                 if (err == kAudioHardwareIllegalOperationError) {
1823                     msg_Warn(p_aout, "could not set audio stream formats property callback on stream id %i, callback already set? [%4.4s]", p_streams[i],
1824                              (char *)&err);
1825                 } else {
1826                     msg_Err(p_aout, "could not set audio stream formats property callback on stream id %i [%4.4s]", p_streams[i],
1827                             (char *)&err);
1828                 }
1829             }
1830
1831         } else {  /* unregister callback */
1832             err = AudioObjectRemovePropertyListener(p_streams[i], &physicalFormatsAddress, StreamsChangedListener, (void *)p_aout);
1833             if (err != noErr)
1834                 msg_Err(p_aout, "failed to remove audio device property streams callback [%4.4s]", (char *)&err);
1835         }
1836
1837     }
1838
1839     free(p_streams);
1840     return VLC_SUCCESS;
1841 }
1842
1843 /*
1844  * AudioDeviceHasOutput: Checks if the device is actually an output device
1845  */
1846 static int AudioDeviceHasOutput(AudioDeviceID i_dev_id)
1847 {
1848     UInt32 dataSize = 0;
1849     OSStatus status;
1850
1851     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1852     status = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &dataSize);
1853
1854     if (dataSize == 0 || status != noErr)
1855         return FALSE;
1856
1857     return TRUE;
1858 }
1859
1860 /*
1861  * AudioDeviceSupportsDigital: Checks if device supports raw bitstreams
1862  */
1863 static int AudioDeviceSupportsDigital(audio_output_t *p_aout, AudioDeviceID i_dev_id)
1864 {
1865     OSStatus                    err = noErr;
1866     UInt32                      i_param_size = 0;
1867     AudioStreamID               *p_streams = NULL;
1868     int                         i_streams = 0;
1869     bool                        b_return = false;
1870
1871     /* Retrieve all the output streams */
1872     AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1873     err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1874     if (err != noErr) {
1875         msg_Err(p_aout, "could not get number of streams [%4.4s] (%i)", (char *)&err, (int32_t)err);
1876         return false;
1877     }
1878
1879     i_streams = i_param_size / sizeof(AudioStreamID);
1880     p_streams = (AudioStreamID *)malloc(i_param_size);
1881     if (p_streams == NULL)
1882         return VLC_ENOMEM;
1883
1884     err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1885     if (err != noErr) {
1886         msg_Err(p_aout, "could not get list of streams [%4.4s]", (char *)&err);
1887         free(p_streams);
1888         return false;
1889     }
1890
1891     for (int i = 0; i < i_streams; i++) {
1892         if (AudioStreamSupportsDigital(p_aout, p_streams[i]))
1893             b_return = true;
1894     }
1895
1896     free(p_streams);
1897     return b_return;
1898 }
1899
1900 /*
1901  * AudioStreamSupportsDigital: Checks if audio stream is compatible with raw bitstreams
1902  */
1903 static int AudioStreamSupportsDigital(audio_output_t *p_aout, AudioStreamID i_stream_id)
1904 {
1905     OSStatus                    err = noErr;
1906     UInt32                      i_param_size = 0;
1907     AudioStreamRangedDescription *p_format_list = NULL;
1908     int                         i_formats = 0;
1909     bool                        b_return = false;
1910
1911     /* Retrieve all the stream formats supported by each output stream */
1912     AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1913     err = AudioObjectGetPropertyDataSize(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size);
1914     if (err != noErr) {
1915         msg_Err(p_aout, "could not get number of streamformats [%4.4s] (%i)", (char *)&err, (int32_t)err);
1916         return false;
1917     }
1918
1919     i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
1920     msg_Dbg(p_aout, "found %i stream formats for stream id %i", i_formats, i_stream_id);
1921
1922     p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
1923     if (p_format_list == NULL)
1924         return false;
1925
1926     err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
1927     if (err != noErr) {
1928         msg_Err(p_aout, "could not get the list of streamformats [%4.4s]", (char *)&err);
1929         free(p_format_list);
1930         p_format_list = NULL;
1931         return false;
1932     }
1933
1934     for (int i = 0; i < i_formats; i++) {
1935 #ifndef NDEBUG
1936         msg_Dbg(p_aout, STREAM_FORMAT_MSG("supported format: ", p_format_list[i].mFormat));
1937 #endif
1938
1939         if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
1940             p_format_list[i].mFormat.mFormatID == 'iac3' ||
1941             p_format_list[i].mFormat.mFormatID == kAudioFormat60958AC3 ||
1942             p_format_list[i].mFormat.mFormatID == kAudioFormatAC3)
1943             b_return = true;
1944     }
1945
1946     free(p_format_list);
1947     return b_return;
1948 }
1949
1950 /*
1951  * AudioStreamChangeFormat: switch stream format based on the provided description
1952  */
1953 static int AudioStreamChangeFormat(audio_output_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format)
1954 {
1955     OSStatus err = noErr;
1956     int retValue = false;
1957
1958     AudioObjectPropertyAddress physicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1959
1960     struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1961
1962     msg_Dbg(p_aout, STREAM_FORMAT_MSG("setting stream format: ", change_format));
1963
1964     /* Condition because SetProperty is asynchronious */
1965     vlc_cond_init(&w.cond);
1966     vlc_mutex_init(&w.lock);
1967     vlc_mutex_lock(&w.lock);
1968
1969     /* Install the callback */
1970     err = AudioObjectAddPropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1971     if (err != noErr) {
1972         msg_Err(p_aout, "AudioObjectAddPropertyListener for kAudioStreamPropertyPhysicalFormat failed [%4.4s]", (char *)&err);
1973         retValue = false;
1974         goto out;
1975     }
1976
1977     /* change the format */
1978     err = AudioObjectSetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, sizeof(AudioStreamBasicDescription),
1979                                      &change_format);
1980     if (err != noErr) {
1981         msg_Err(p_aout, "could not set the stream format [%4.4s]", (char *)&err);
1982         retValue = false;
1983         goto out;
1984     }
1985
1986     /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1987      * it is also not atomic in its behaviour.
1988      * Therefore we check 9 times before we really give up.
1989      */
1990     AudioStreamBasicDescription actual_format;
1991     UInt32 i_param_size = sizeof(AudioStreamBasicDescription);
1992     for (int i = 0; i < 9; i++) {
1993         /* Callback is not always invoked. So first check if format is already set. */
1994         if (i > 0) {
1995             mtime_t timeout = mdate() + 500000;
1996             if (vlc_cond_timedwait(&w.cond, &w.lock, timeout))
1997                 msg_Dbg(p_aout, "reached timeout");
1998         }
1999
2000         err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, &i_param_size, &actual_format);
2001
2002         msg_Dbg(p_aout, STREAM_FORMAT_MSG("actual format in use: ", actual_format));
2003         if (actual_format.mSampleRate == change_format.mSampleRate &&
2004             actual_format.mFormatID == change_format.mFormatID &&
2005             actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
2006             /* The right format is now active */
2007             retValue = true;
2008             break;
2009         }
2010
2011         /* We need to check again */
2012     }
2013
2014 out:
2015     vlc_mutex_unlock(&w.lock);
2016
2017     /* Removing the property listener */
2018     err = AudioObjectRemovePropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
2019     if (err != noErr) {
2020         msg_Err(p_aout, "AudioStreamRemovePropertyListener failed [%4.4s]", (char *)&err);
2021         retValue = false;
2022     }
2023
2024     vlc_mutex_destroy(&w.lock);
2025     vlc_cond_destroy(&w.cond);
2026
2027     return retValue;
2028 }