]> git.sesse.net Git - vlc/blob - modules/audio_output/auhal.c
Converted oss to vlc_clone().
[vlc] / modules / audio_output / auhal.c
1 /*****************************************************************************
2  * auhal.c: AUHAL and Coreaudio output plugin
3  *****************************************************************************
4  * Copyright (C) 2005, 2011 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <unistd.h>
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_dialog.h>
37 #include <vlc_aout.h>
38
39 #include <CoreAudio/CoreAudio.h>
40 #include <AudioUnit/AudioUnit.h>
41 #include <AudioToolbox/AudioFormat.h>
42
43 #include <CoreServices/CoreServices.h>
44
45 #ifndef verify_noerr
46 #define verify_noerr(a) assert((a) == noErr)
47 #endif
48
49 #define STREAM_FORMAT_MSG( pre, sfm ) \
50     pre "[%u][%4.4s][%u][%u][%u][%u][%u][%u]", \
51     (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
52     sfm.mFormatFlags, sfm.mBytesPerPacket, \
53     sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
54     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
55
56 #define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
57     pre ":\nsamplerate: [%u]\nFormatID: [%4.4s]\nFormatFlags: [%u]\nBypesPerPacket: [%u]\nFramesPerPacket: [%u]\nBytesPerFrame: [%u]\nChannelsPerFrame: [%u]\nBitsPerChannel[%u]", \
58     (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
59     sfm.mFormatFlags, sfm.mBytesPerPacket, \
60     sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
61     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
62
63 #define FRAMESIZE 2048
64 #define BUFSIZE (FRAMESIZE * 8) * 8
65 #define AOUT_VAR_SPDIF_FLAG 0xf00000
66
67 /*
68  * TODO:
69  * - clean up the debug info
70  * - be better at changing stream setup or devices setup changes while playing.
71  * - fix 6.1 and 7.1
72  */
73
74 /*****************************************************************************
75  * aout_sys_t: private audio output method descriptor
76  *****************************************************************************
77  * This structure is part of the audio output thread descriptor.
78  * It describes the CoreAudio specific properties of an output thread.
79  *****************************************************************************/
80 struct aout_sys_t
81 {
82     AudioDeviceID               i_default_dev;  /* Keeps DeviceID of defaultOutputDevice */
83     AudioDeviceID               i_selected_dev; /* Keeps DeviceID of the selected device */
84     AudioDeviceIOProcID         i_procID;       /* DeviceID of current device */
85     UInt32                      i_devices;      /* Number of CoreAudio Devices */
86     bool                        b_supports_digital;/* Does the currently selected device support digital mode? */
87     bool                        b_digital;      /* Are we running in digital mode? */
88     mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
89
90     /* AUHAL specific */
91     Component                   au_component;   /* The Audiocomponent we use */
92     AudioUnit                   au_unit;        /* The AudioUnit we use */
93     uint8_t                     p_remainder_buffer[BUFSIZE];
94     uint32_t                    i_read_bytes;
95     uint32_t                    i_total_bytes;
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 stream_format;  /* The format we changed the stream to */
102     AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
103     bool                        b_revert;       /* Wether we need to revert the stream format */
104     bool                        b_changed_mixing;/* Wether we need to set the mixing mode back */
105 };
106
107 /*****************************************************************************
108  * Local prototypes.
109  *****************************************************************************/
110 static int      Open                    ( vlc_object_t * );
111 static int      OpenAnalog              ( aout_instance_t * );
112 static int      OpenSPDIF               ( aout_instance_t * );
113 static void     Close                   ( vlc_object_t * );
114
115 static void     Play                    ( aout_instance_t * );
116 static void     Probe                   ( aout_instance_t * );
117
118 static int      AudioDeviceHasOutput    ( AudioDeviceID );
119 static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
120 static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
121 static int      AudioStreamChangeFormat ( aout_instance_t *, AudioStreamID, AudioStreamBasicDescription );
122
123 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
124                                           unsigned int, unsigned int, AudioBufferList *);
125 static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
126                                           AudioBufferList *, const AudioTimeStamp *, void * );
127 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
128 static OSStatus StreamListener          ( AudioStreamID, UInt32,
129                                           AudioDevicePropertyID, void * );
130 static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
131                                           vlc_value_t, vlc_value_t, void * );
132
133
134
135 /*****************************************************************************
136  * Module descriptor
137  *****************************************************************************/
138 #define ADEV_TEXT N_("Audio Device")
139 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
140     "audio device, as listed in your 'Audio Device' menu. This device will " \
141     "then be used by default for audio playback.")
142
143 vlc_module_begin ()
144     set_shortname( "auhal" )
145     set_description( N_("HAL AudioUnit output") )
146     set_capability( "audio output", 101 )
147     set_category( CAT_AUDIO )
148     set_subcategory( SUBCAT_AUDIO_AOUT )
149     set_callbacks( Open, Close )
150     add_integer( "macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false )
151 vlc_module_end ()
152
153 /*****************************************************************************
154  * Open: open macosx audio output
155  *****************************************************************************/
156 static int Open( vlc_object_t * p_this )
157 {
158     OSStatus                err = noErr;
159     UInt32                  i_param_size = 0;
160     struct aout_sys_t       *p_sys = NULL;
161     vlc_value_t             val;
162     aout_instance_t         *p_aout = (aout_instance_t *)p_this;
163
164     /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
165      * property size */
166     int                     b_alive = false;
167
168     /* Allocate structure */
169     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
170     if( p_aout->output.p_sys == NULL )
171         return VLC_ENOMEM;
172
173     p_sys = p_aout->output.p_sys;
174     p_sys->i_default_dev = 0;
175     p_sys->i_selected_dev = 0;
176     p_sys->i_devices = 0;
177     p_sys->b_supports_digital = false;
178     p_sys->b_digital = false;
179     p_sys->au_component = NULL;
180     p_sys->au_unit = NULL;
181     p_sys->clock_diff = (mtime_t) 0;
182     p_sys->i_read_bytes = 0;
183     p_sys->i_total_bytes = 0;
184     p_sys->i_hog_pid = -1;
185     p_sys->i_stream_id = 0;
186     p_sys->i_stream_index = -1;
187     p_sys->b_revert = false;
188     p_sys->b_changed_mixing = false;
189     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
190
191     p_aout->output.pf_play = Play;
192
193     aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
194
195     /* Persistent device variable */
196     if( var_Type( p_aout->p_libvlc, "macosx-audio-device" ) == 0 )
197     {
198         var_Create( p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
199     }
200
201     /* Build a list of devices */
202     if( var_Type( p_aout, "audio-device" ) == 0 )
203     {
204         Probe( p_aout );
205     }
206
207     /* What device do we want? */
208     if( var_Get( p_aout, "audio-device", &val ) < 0 )
209     {
210         msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
211         goto error;
212     }
213
214     p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
215     p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? true : false;
216
217     /* Check if the desired device is alive and usable */
218     /* TODO: add a callback to the device to alert us if the device dies */
219     i_param_size = sizeof( b_alive );
220     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
221                                   kAudioDevicePropertyDeviceIsAlive,
222                                   &i_param_size, &b_alive );
223
224     if( err != noErr )
225     {
226         /* Be tolerant, only give a warning here */
227         msg_Warn( p_aout, "could not check whether device [0x%x] is alive: %4.4s", (unsigned int)p_sys->i_selected_dev, (char *)&err );
228         b_alive = false;
229     }
230
231     if( !b_alive )
232     {
233         msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
234         p_sys->i_selected_dev = p_sys->i_default_dev;
235     }
236
237     i_param_size = sizeof( p_sys->i_hog_pid );
238     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
239                                   kAudioDevicePropertyHogMode,
240                                   &i_param_size, &p_sys->i_hog_pid );
241
242     if( err != noErr )
243     {
244         /* This is not a fatal error. Some drivers simply don't support this property */
245         msg_Warn( p_aout, "could not check whether device is hogged: %4.4s",
246                  (char *)&err );
247         p_sys->i_hog_pid = -1;
248     }
249
250     if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
251     {
252         msg_Err( p_aout, "Selected audio device is exclusively in use by another program." );
253         dialog_Fatal( p_aout, _("Audio output failed"), "%s",
254                         _("The selected audio output device is exclusively in "
255                           "use by another program.") );
256         goto error;
257     }
258
259     /* Check for Digital mode or Analog output mode */
260     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
261     {
262         if( OpenSPDIF( p_aout ) )
263             return VLC_SUCCESS;
264     }
265     else
266     {
267         if( OpenAnalog( p_aout ) )
268             return VLC_SUCCESS;
269     }
270
271 error:
272     /* If we reach this, this aout has failed */
273     var_Destroy( p_aout, "audio-device" );
274     free( p_sys );
275     return VLC_EGENERIC;
276 }
277
278 /*****************************************************************************
279  * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
280  *****************************************************************************/
281 static int OpenAnalog( aout_instance_t *p_aout )
282 {
283     struct aout_sys_t           *p_sys = p_aout->output.p_sys;
284     OSStatus                    err = noErr;
285     UInt32                      i_param_size = 0, i = 0;
286     int                         i_original;
287     ComponentDescription        desc;
288     AudioStreamBasicDescription DeviceFormat;
289     AudioChannelLayout          *layout;
290     AudioChannelLayout          new_layout;
291     AURenderCallbackStruct      input;
292
293     /* Lets go find our Component */
294     desc.componentType = kAudioUnitType_Output;
295     desc.componentSubType = kAudioUnitSubType_HALOutput;
296     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
297     desc.componentFlags = 0;
298     desc.componentFlagsMask = 0;
299
300     p_sys->au_component = FindNextComponent( NULL, &desc );
301     if( p_sys->au_component == NULL )
302     {
303         msg_Warn( p_aout, "we cannot find our HAL component" );
304         return false;
305     }
306
307     err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
308     if( err != noErr )
309     {
310         msg_Warn( p_aout, "we cannot open our HAL component" );
311         return false;
312     }
313
314     /* Set the device we will use for this output unit */
315     err = AudioUnitSetProperty( p_sys->au_unit,
316                          kAudioOutputUnitProperty_CurrentDevice,
317                          kAudioUnitScope_Global,
318                          0,
319                          &p_sys->i_selected_dev,
320                          sizeof( AudioDeviceID ));
321
322     if( err != noErr )
323     {
324         msg_Warn( p_aout, "we cannot select the audio device" );
325         return false;
326     }
327
328     /* Get the current format */
329     i_param_size = sizeof(AudioStreamBasicDescription);
330
331     err = AudioUnitGetProperty( p_sys->au_unit,
332                                    kAudioUnitProperty_StreamFormat,
333                                    kAudioUnitScope_Input,
334                                    0,
335                                    &DeviceFormat,
336                                    &i_param_size );
337
338     if( err != noErr ) return false;
339     else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
340
341     /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
342     err = AudioUnitGetPropertyInfo( p_sys->au_unit,
343                                    kAudioDevicePropertyPreferredChannelLayout,
344                                    kAudioUnitScope_Output,
345                                    0,
346                                    &i_param_size,
347                                    NULL );
348
349     if( err == noErr )
350     {
351         layout = (AudioChannelLayout *)malloc( i_param_size);
352
353         verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
354                                        kAudioDevicePropertyPreferredChannelLayout,
355                                        kAudioUnitScope_Output,
356                                        0,
357                                        layout,
358                                        &i_param_size ));
359
360         /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
361         if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
362         {
363             /* bitmap defined channellayout */
364             verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
365                                     sizeof( UInt32), &layout->mChannelBitmap,
366                                     &i_param_size,
367                                     layout ));
368         }
369         else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
370         {
371             /* layouttags defined channellayout */
372             verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
373                                     sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
374                                     &i_param_size,
375                                     layout ));
376         }
377
378         msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
379
380         /* Initialize the VLC core channel count */
381         p_aout->output.output.i_physical_channels = 0;
382         i_original = p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK;
383
384         if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
385         {
386             /* We only need Mono or cannot output more than 1 channel */
387             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
388         }
389         else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
390         {
391             /* We only need Stereo or cannot output more than 2 channels */
392             p_aout->output.output.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
393         }
394         else
395         {
396             /* We want more than stereo and we can do that */
397             for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
398             {
399                 msg_Dbg( p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
400
401                 switch( layout->mChannelDescriptions[i].mChannelLabel )
402                 {
403                     case kAudioChannelLabel_Left:
404                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
405                         continue;
406                     case kAudioChannelLabel_Right:
407                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
408                         continue;
409                     case kAudioChannelLabel_Center:
410                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
411                         continue;
412                     case kAudioChannelLabel_LFEScreen:
413                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
414                         continue;
415                     case kAudioChannelLabel_LeftSurround:
416                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
417                         continue;
418                     case kAudioChannelLabel_RightSurround:
419                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
420                         continue;
421                     case kAudioChannelLabel_RearSurroundLeft:
422                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
423                         continue;
424                     case kAudioChannelLabel_RearSurroundRight:
425                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
426                         continue;
427                     case kAudioChannelLabel_CenterSurround:
428                         p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
429                         continue;
430                     default:
431                         msg_Warn( p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
432                 }
433             }
434             if( p_aout->output.output.i_physical_channels == 0 )
435             {
436                 p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
437                 msg_Err( p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
438                 dialog_Fatal( p_aout, _("Audio device is not configured"), "%s",
439                                 _("You should configure your speaker layout with "
440                                   "the \"Audio Midi Setup\" utility in /Applications/"
441                                   "Utilities. Stereo mode is being used now.") );
442             }
443         }
444         free( layout );
445     }
446     else
447     {
448         msg_Warn( p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
449         p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
450     }
451
452     msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
453     msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
454
455     memset (&new_layout, 0, sizeof(new_layout));
456     switch( aout_FormatNbChannels( &p_aout->output.output ) )
457     {
458         case 1:
459             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
460             break;
461         case 2:
462             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
463             break;
464         case 3:
465             if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
466             {
467                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
468             }
469             else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
470             {
471                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
472             }
473             break;
474         case 4:
475             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
476             {
477                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
478             }
479             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
480             {
481                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
482             }
483             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
484             {
485                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
486             }
487             break;
488         case 5:
489             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
490             {
491                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
492             }
493             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
494             {
495                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
496             }
497             break;
498         case 6:
499             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
500             {
501                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
502             }
503             else
504             {
505                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
506             }
507             break;
508         case 7:
509             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
510             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
511             break;
512         case 8:
513             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
514             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
515             break;
516     }
517
518     /* Set up the format to be used */
519     DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
520     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
521
522     /* We use float 32. It's the best supported format by both VLC and Coreaudio */
523     p_aout->output.output.i_format = VLC_CODEC_FL32;
524     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
525     DeviceFormat.mBitsPerChannel = 32;
526     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
527
528     /* Calculate framesizes and stuff */
529     DeviceFormat.mFramesPerPacket = 1;
530     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
531     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
532
533     /* Set the desired format */
534     i_param_size = sizeof(AudioStreamBasicDescription);
535     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
536                                    kAudioUnitProperty_StreamFormat,
537                                    kAudioUnitScope_Input,
538                                    0,
539                                    &DeviceFormat,
540                                    i_param_size ));
541
542     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
543
544     /* Retrieve actual format */
545     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
546                                    kAudioUnitProperty_StreamFormat,
547                                    kAudioUnitScope_Input,
548                                    0,
549                                    &DeviceFormat,
550                                    &i_param_size ));
551
552     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
553
554     /* Do the last VLC aout setups */
555     aout_FormatPrepare( &p_aout->output.output );
556     p_aout->output.i_nb_samples = FRAMESIZE;
557     aout_VolumeSoftInit( p_aout );
558
559     /* set the IOproc callback */
560     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
561     input.inputProcRefCon = p_aout;
562
563     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
564                             kAudioUnitProperty_SetRenderCallback,
565                             kAudioUnitScope_Input,
566                             0, &input, sizeof( input ) ) );
567
568     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
569     input.inputProcRefCon = p_aout;
570
571     /* Set the new_layout as the layout VLC will use to feed the AU unit */
572     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
573                             kAudioUnitProperty_AudioChannelLayout,
574                             kAudioUnitScope_Input,
575                             0, &new_layout, sizeof(new_layout) ) );
576
577     if( new_layout.mNumberChannelDescriptions > 0 )
578         free( new_layout.mChannelDescriptions );
579
580     /* AU initiliaze */
581     verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
582
583     /* Find the difference between device clock and mdate clock */
584     p_sys->clock_diff = - (mtime_t)
585         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
586     p_sys->clock_diff += mdate();
587
588     /* Start the AU */
589     verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
590
591     return true;
592 }
593
594 /*****************************************************************************
595  * Setup a encoded digital stream (SPDIF)
596  *****************************************************************************/
597 static int OpenSPDIF( aout_instance_t * p_aout )
598 {
599     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
600     OSStatus                err = noErr;
601     UInt32                  i_param_size = 0, b_mix = 0;
602     Boolean                 b_writeable = false;
603     AudioStreamID           *p_streams = NULL;
604     int                     i = 0, i_streams = 0;
605
606     /* Start doing the SPDIF setup proces */
607     p_sys->b_digital = true;
608
609     /* Hog the device */
610     i_param_size = sizeof( p_sys->i_hog_pid );
611     p_sys->i_hog_pid = getpid() ;
612
613     err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
614                                   kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
615
616     if( err != noErr )
617     {
618         msg_Err( p_aout, "failed to set hogmode: [%4.4s]", (char *)&err );
619         return false;
620     }
621
622     /* Set mixable to false if we are allowed to */
623     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
624                                     &i_param_size, &b_writeable );
625
626     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
627                                     &i_param_size, &b_mix );
628
629     if( !err && b_writeable )
630     {
631         b_mix = 0;
632         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
633                             kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
634         p_sys->b_changed_mixing = true;
635     }
636
637     if( err != noErr )
638     {
639         msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
640         return false;
641     }
642
643     /* Get a list of all the streams on this device */
644     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
645                                       kAudioDevicePropertyStreams,
646                                       &i_param_size, NULL );
647     if( err != noErr )
648     {
649         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
650         return false;
651     }
652
653     i_streams = i_param_size / sizeof( AudioStreamID );
654     p_streams = (AudioStreamID *)malloc( i_param_size );
655     if( p_streams == NULL )
656         return false;
657
658     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
659                                     kAudioDevicePropertyStreams,
660                                     &i_param_size, p_streams );
661
662     if( err != noErr )
663     {
664         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
665         free( p_streams );
666         return false;
667     }
668
669     for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
670     {
671         /* Find a stream with a cac3 stream */
672         AudioStreamBasicDescription *p_format_list = NULL;
673         int                         i_formats = 0, j = 0;
674         bool                  b_digital = false;
675
676         /* Retrieve all the stream formats supported by each output stream */
677         err = AudioStreamGetPropertyInfo( p_streams[i], 0,
678                                           kAudioStreamPropertyPhysicalFormats,
679                                           &i_param_size, NULL );
680         if( err != noErr )
681         {
682             msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
683             continue;
684         }
685
686         i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
687         p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
688         if( p_format_list == NULL )
689             continue;
690
691         err = AudioStreamGetProperty( p_streams[i], 0,
692                                           kAudioStreamPropertyPhysicalFormats,
693                                           &i_param_size, p_format_list );
694         if( err != noErr )
695         {
696             msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
697             free( p_format_list );
698             continue;
699         }
700
701         /* Check if one of the supported formats is a digital format */
702         for( j = 0; j < i_formats; j++ )
703         {
704             if( p_format_list[j].mFormatID == 'IAC3' ||
705                   p_format_list[j].mFormatID == kAudioFormat60958AC3 )
706             {
707                 b_digital = true;
708                 break;
709             }
710         }
711
712         if( b_digital )
713         {
714             /* if this stream supports a digital (cac3) format, then go set it. */
715             int i_requested_rate_format = -1;
716             int i_current_rate_format = -1;
717             int i_backup_rate_format = -1;
718
719             p_sys->i_stream_id = p_streams[i];
720             p_sys->i_stream_index = i;
721
722             if( !p_sys->b_revert )
723             {
724                 /* Retrieve the original format of this stream first if not done so already */
725                 i_param_size = sizeof( p_sys->sfmt_revert );
726                 err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
727                                               kAudioStreamPropertyPhysicalFormat,
728                                               &i_param_size,
729                                               &p_sys->sfmt_revert );
730                 if( err != noErr )
731                 {
732                     msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
733                     continue;
734                 }
735                 p_sys->b_revert = true;
736             }
737
738             for( j = 0; j < i_formats; j++ )
739             {
740                 if( p_format_list[j].mFormatID == 'IAC3' ||
741                       p_format_list[j].mFormatID == kAudioFormat60958AC3 )
742                 {
743                     if( p_format_list[j].mSampleRate == p_aout->output.output.i_rate )
744                     {
745                         i_requested_rate_format = j;
746                         break;
747                     }
748                     else if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
749                     {
750                         i_current_rate_format = j;
751                     }
752                     else
753                     {
754                         if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
755                             i_backup_rate_format = j;
756                     }
757                 }
758
759             }
760
761             if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
762                 p_sys->stream_format = p_format_list[i_requested_rate_format];
763             else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
764                 p_sys->stream_format = p_format_list[i_current_rate_format];
765             else p_sys->stream_format = p_format_list[i_backup_rate_format]; /* And if we have to, any digital format will be just fine (highest rate possible) */
766         }
767         free( p_format_list );
768     }
769     free( p_streams );
770
771     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );
772
773     if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
774         return false;
775
776     /* Set the format flags */
777     if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
778         p_aout->output.output.i_format = VLC_CODEC_SPDIFB;
779     else
780         p_aout->output.output.i_format = VLC_CODEC_SPDIFL;
781     p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
782     p_aout->output.output.i_frame_length = A52_FRAME_NB;
783     p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
784     p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
785     aout_FormatPrepare( &p_aout->output.output );
786     aout_VolumeNoneInit( p_aout );
787
788     /* Add IOProc callback */
789     err = AudioDeviceCreateIOProcID( p_sys->i_selected_dev,
790                                    (AudioDeviceIOProc)RenderCallbackSPDIF,
791                                    (void *)p_aout,
792                                    &p_sys->i_procID );
793     if( err != noErr )
794     {
795         msg_Err( p_aout, "AudioDeviceCreateIOProcID failed: [%4.4s]", (char *)&err );
796         return false;
797     }
798
799     /* Check for the difference between the Device clock and mdate */
800     p_sys->clock_diff = - (mtime_t)
801         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
802     p_sys->clock_diff += mdate();
803
804     /* Start device */
805     err = AudioDeviceStart( p_sys->i_selected_dev, p_sys->i_procID );
806     if( err != noErr )
807     {
808         msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );
809
810         err = AudioDeviceDestroyIOProcID( p_sys->i_selected_dev,
811                                           p_sys->i_procID );
812         if( err != noErr )
813         {
814             msg_Err( p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err );
815         }
816         return false;
817     }
818
819     return true;
820 }
821
822
823 /*****************************************************************************
824  * Close: Close HAL AudioUnit
825  *****************************************************************************/
826 static void Close( vlc_object_t * p_this )
827 {
828     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
829     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
830     OSStatus            err = noErr;
831     UInt32              i_param_size = 0;
832
833     if( p_sys->au_unit )
834     {
835         verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
836         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
837         verify_noerr( CloseComponent( p_sys->au_unit ) );
838     }
839
840     if( p_sys->b_digital )
841     {
842         /* Stop device */
843         err = AudioDeviceStop( p_sys->i_selected_dev,
844                                p_sys->i_procID );
845         if( err != noErr )
846         {
847             msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
848         }
849
850         /* Remove IOProc callback */
851         err = AudioDeviceDestroyIOProcID( p_sys->i_selected_dev,
852                                           p_sys->i_procID );
853         if( err != noErr )
854         {
855             msg_Err( p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err );
856         }
857
858         if( p_sys->b_revert )
859         {
860             AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->sfmt_revert );
861         }
862
863         if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
864         {
865             int b_mix;
866             Boolean b_writeable;
867             /* Revert mixable to true if we are allowed to */
868             err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
869                                         &i_param_size, &b_writeable );
870
871             err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
872                                         &i_param_size, &b_mix );
873
874             if( !err && b_writeable )
875             {
876                 msg_Dbg( p_aout, "mixable is: %d", b_mix );
877                 b_mix = 1;
878                 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
879                                     kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
880             }
881
882             if( err != noErr )
883             {
884                 msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
885             }
886         }
887     }
888
889     err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
890                                                HardwareListener );
891
892     if( err != noErr )
893     {
894         msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
895     }
896
897     if( p_sys->i_hog_pid == getpid() )
898     {
899         p_sys->i_hog_pid = -1;
900         i_param_size = sizeof( p_sys->i_hog_pid );
901         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
902                                          kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
903         if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
904     }
905
906     free( p_sys );
907 }
908
909 /*****************************************************************************
910  * Play: nothing to do
911  *****************************************************************************/
912 static void Play( aout_instance_t * p_aout )
913 {
914     VLC_UNUSED(p_aout);
915 }
916
917
918 /*****************************************************************************
919  * Probe: Check which devices the OS has, and add them to our audio-device menu
920  *****************************************************************************/
921 static void Probe( aout_instance_t * p_aout )
922 {
923     OSStatus            err = noErr;
924     UInt32              i = 0, i_param_size = 0;
925     AudioDeviceID       devid_def = 0;
926     AudioDeviceID       *p_devices = NULL;
927     vlc_value_t         val, text;
928
929     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
930
931     /* Get number of devices */
932     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
933                                         &i_param_size, NULL );
934     if( err != noErr )
935     {
936         msg_Err( p_aout, "Could not get number of devices: [%4.4s]", (char *)&err );
937         goto error;
938     }
939
940     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
941
942     if( p_sys->i_devices < 1 )
943     {
944         msg_Err( p_aout, "No audio output devices were found." );
945         goto error;
946     }
947
948     msg_Dbg( p_aout, "system has [%u] device(s)", p_sys->i_devices );
949
950     /* Allocate DeviceID array */
951     p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
952     if( p_devices == NULL )
953         goto error;
954
955     /* Populate DeviceID array */
956     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
957                                     &i_param_size, p_devices );
958     if( err != noErr )
959     {
960         msg_Err( p_aout, "could not get the device IDs: [%4.4s]", (char *)&err );
961         goto error;
962     }
963
964     /* Find the ID of the default Device */
965     i_param_size = sizeof( AudioDeviceID );
966     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
967                                     &i_param_size, &devid_def );
968     if( err != noErr )
969     {
970         msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
971         goto error;
972     }
973     p_sys->i_default_dev = devid_def;
974
975     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
976     text.psz_string = (char*)_("Audio Device");
977     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
978
979     for( i = 0; i < p_sys->i_devices; i++ )
980     {
981         char *psz_name;
982         i_param_size = 0;
983
984         /* Retrieve the length of the device name */
985         err = AudioDeviceGetPropertyInfo(
986                     p_devices[i], 0, false,
987                     kAudioDevicePropertyDeviceName,
988                     &i_param_size, NULL);
989         if( err ) goto error;
990
991         /* Retrieve the name of the device */
992         psz_name = (char *)malloc( i_param_size );
993         err = AudioDeviceGetProperty(
994                     p_devices[i], 0, false,
995                     kAudioDevicePropertyDeviceName,
996                     &i_param_size, psz_name);
997         if( err ) goto error;
998
999         msg_Dbg( p_aout, "DevID: %u DevName: %s", p_devices[i], psz_name );
1000
1001         if( !AudioDeviceHasOutput( p_devices[i]) )
1002         {
1003             msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
1004             free( psz_name );
1005             continue;
1006         }
1007
1008         /* Add the menu entries */
1009         val.i_int = (int)p_devices[i];
1010         text.psz_string = psz_name;
1011         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1012         text.psz_string = NULL;
1013         if( p_sys->i_default_dev == p_devices[i] )
1014         {
1015             /* The default device is the selected device normally */
1016             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1017             var_Set( p_aout, "audio-device", val );
1018         }
1019
1020         if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
1021         {
1022             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
1023             if( asprintf( &text.psz_string, _("%s (Encoded Output)"), psz_name ) != -1 )
1024             {
1025                 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1026                 free( text.psz_string );
1027                 if( p_sys->i_default_dev == p_devices[i]
1028                  && var_InheritBool( p_aout, "spdif" ) )
1029                 {
1030                     /* We selected to prefer SPDIF output if available
1031                      * then this "dummy" entry should be selected */
1032                     var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1033                     var_Set( p_aout, "audio-device", val );
1034                 }
1035             }
1036         }
1037
1038         free( psz_name);
1039     }
1040
1041     /* If a device is already "preselected", then use this device */
1042     var_Get( p_aout->p_libvlc, "macosx-audio-device", &val );
1043     if( val.i_int > 0 )
1044     {
1045         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1046         var_Set( p_aout, "audio-device", val );
1047     }
1048
1049     /* If we change the device we want to use, we should renegotiate the audio chain */
1050     var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );
1051
1052     /* Attach a Listener so that we are notified of a change in the Device setup */
1053     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
1054                                             HardwareListener,
1055                                             (void *)p_aout );
1056     if( err )
1057         goto error;
1058
1059     free( p_devices );
1060     return;
1061
1062 error:
1063     msg_Warn( p_aout, "audio device already in use" );
1064     free( p_devices );
1065     return;
1066 }
1067
1068 /*****************************************************************************
1069  * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
1070  *****************************************************************************/
1071 static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
1072 {
1073     UInt32            dataSize;
1074     Boolean            isWritable;
1075
1076     verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
1077     if (dataSize == 0) return FALSE;
1078
1079     return TRUE;
1080 }
1081
1082 /*****************************************************************************
1083  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1084  *****************************************************************************/
1085 static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
1086 {
1087     OSStatus                    err = noErr;
1088     UInt32                      i_param_size = 0;
1089     AudioStreamID               *p_streams = NULL;
1090     int                         i = 0, i_streams = 0;
1091     bool                  b_return = false;
1092
1093     /* Retrieve all the output streams */
1094     err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
1095                                       kAudioDevicePropertyStreams,
1096                                       &i_param_size, NULL );
1097     if( err != noErr )
1098     {
1099         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1100         return false;
1101     }
1102
1103     i_streams = i_param_size / sizeof( AudioStreamID );
1104     p_streams = (AudioStreamID *)malloc( i_param_size );
1105     if( p_streams == NULL )
1106         return VLC_ENOMEM;
1107
1108     err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
1109                                     kAudioDevicePropertyStreams,
1110                                     &i_param_size, p_streams );
1111
1112     if( err != noErr )
1113     {
1114         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1115         return false;
1116     }
1117
1118     for( i = 0; i < i_streams; i++ )
1119     {
1120         if( AudioStreamSupportsDigital( p_aout, p_streams[i] ) )
1121             b_return = true;
1122     }
1123
1124     free( p_streams );
1125     return b_return;
1126 }
1127
1128 /*****************************************************************************
1129  * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1130  *****************************************************************************/
1131 static int AudioStreamSupportsDigital( aout_instance_t *p_aout, AudioStreamID i_stream_id )
1132 {
1133     OSStatus                    err = noErr;
1134     UInt32                      i_param_size = 0;
1135     AudioStreamBasicDescription *p_format_list = NULL;
1136     int                         i = 0, i_formats = 0;
1137     bool                  b_return = false;
1138
1139     /* Retrieve all the stream formats supported by each output stream */
1140     err = AudioStreamGetPropertyInfo( i_stream_id, 0,
1141                                       kAudioStreamPropertyPhysicalFormats,
1142                                       &i_param_size, NULL );
1143     if( err != noErr )
1144     {
1145         msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
1146         return false;
1147     }
1148
1149     i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
1150     p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
1151     if( p_format_list == NULL )
1152         return false;
1153
1154     err = AudioStreamGetProperty( i_stream_id, 0,
1155                                       kAudioStreamPropertyPhysicalFormats,
1156                                       &i_param_size, p_format_list );
1157     if( err != noErr )
1158     {
1159         msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
1160         free( p_format_list);
1161         p_format_list = NULL;
1162         return false;
1163     }
1164
1165     for( i = 0; i < i_formats; i++ )
1166     {
1167         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format: ", p_format_list[i] ) );
1168
1169         if( p_format_list[i].mFormatID == 'IAC3' ||
1170                   p_format_list[i].mFormatID == kAudioFormat60958AC3 )
1171         {
1172             b_return = true;
1173         }
1174     }
1175
1176     free( p_format_list );
1177     return b_return;
1178 }
1179
1180 /*****************************************************************************
1181  * AudioStreamChangeFormat: Change i_stream_id to change_format
1182  *****************************************************************************/
1183 static int AudioStreamChangeFormat( aout_instance_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format )
1184 {
1185     OSStatus            err = noErr;
1186     UInt32              i_param_size = 0;
1187     int i;
1188
1189     struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1190
1191     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", change_format ) );
1192
1193     /* Condition because SetProperty is asynchronious */
1194     vlc_cond_init( &w.cond );
1195     vlc_mutex_init( &w.lock );
1196     vlc_mutex_lock( &w.lock );
1197
1198     /* Install the callback */
1199     err = AudioStreamAddPropertyListener( i_stream_id, 0,
1200                                       kAudioStreamPropertyPhysicalFormat,
1201                                       StreamListener, (void *)&w );
1202     if( err != noErr )
1203     {
1204         msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
1205         return false;
1206     }
1207
1208     /* change the format */
1209     err = AudioStreamSetProperty( i_stream_id, 0, 0,
1210                                   kAudioStreamPropertyPhysicalFormat,
1211                                   sizeof( AudioStreamBasicDescription ),
1212                                   &change_format );
1213     if( err != noErr )
1214     {
1215         msg_Err( p_aout, "could not set the stream format: [%4.4s]", (char *)&err );
1216         return false;
1217     }
1218
1219     /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1220      * it is also not atomic in its behaviour.
1221      * Therefore we check 5 times before we really give up.
1222      * FIXME: failing isn't actually implemented yet. */
1223     for( i = 0; i < 5; i++ )
1224     {
1225         AudioStreamBasicDescription actual_format;
1226         mtime_t timeout = mdate() + 500000;
1227
1228         if( vlc_cond_timedwait( &w.cond, &w.lock, timeout ) )
1229         {
1230             msg_Dbg( p_aout, "reached timeout" );
1231         }
1232
1233         i_param_size = sizeof( AudioStreamBasicDescription );
1234         err = AudioStreamGetProperty( i_stream_id, 0,
1235                                       kAudioStreamPropertyPhysicalFormat,
1236                                       &i_param_size,
1237                                       &actual_format );
1238
1239         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", actual_format ) );
1240         if( actual_format.mSampleRate == change_format.mSampleRate &&
1241             actual_format.mFormatID == change_format.mFormatID &&
1242             actual_format.mFramesPerPacket == change_format.mFramesPerPacket )
1243         {
1244             /* The right format is now active */
1245             break;
1246         }
1247         /* We need to check again */
1248     }
1249
1250     /* Removing the property listener */
1251     err = AudioStreamRemovePropertyListener( i_stream_id, 0,
1252                                             kAudioStreamPropertyPhysicalFormat,
1253                                             StreamListener );
1254     if( err != noErr )
1255     {
1256         msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
1257         return false;
1258     }
1259
1260     /* Destroy the lock and condition */
1261     vlc_mutex_unlock( &w.lock );
1262     vlc_mutex_destroy( &w.lock );
1263     vlc_cond_destroy( &w.cond );
1264
1265     return true;
1266 }
1267
1268 /*****************************************************************************
1269  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1270  * us to provide some more audio data.
1271  * Don't print anything during normal playback, calling blocking function from
1272  * this callback is not allowed.
1273  *****************************************************************************/
1274 static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
1275                                       AudioUnitRenderActionFlags *ioActionFlags,
1276                                       const AudioTimeStamp *inTimeStamp,
1277                                       unsigned int inBusNumber,
1278                                       unsigned int inNumberFrames,
1279                                       AudioBufferList *ioData )
1280 {
1281     AudioTimeStamp  host_time;
1282     mtime_t         current_date = 0;
1283     uint32_t        i_mData_bytes = 0;
1284
1285     aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
1286     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1287
1288     VLC_UNUSED(ioActionFlags);
1289     VLC_UNUSED(inBusNumber);
1290     VLC_UNUSED(inNumberFrames);
1291
1292     host_time.mFlags = kAudioTimeStampHostTimeValid;
1293     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
1294
1295     /* Check for the difference between the Device clock and mdate */
1296     p_sys->clock_diff = - (mtime_t)
1297         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
1298     p_sys->clock_diff += mdate();
1299
1300     current_date = p_sys->clock_diff +
1301                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
1302                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1303
1304     if( ioData == NULL && ioData->mNumberBuffers < 1 )
1305     {
1306         msg_Err( p_aout, "no iodata or buffers");
1307         return 0;
1308     }
1309     if( ioData->mNumberBuffers > 1 )
1310         msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
1311
1312
1313     if( p_sys->i_total_bytes > 0 )
1314     {
1315         i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
1316         vlc_memcpy( ioData->mBuffers[0].mData,
1317                     &p_sys->p_remainder_buffer[p_sys->i_read_bytes],
1318                     i_mData_bytes );
1319         p_sys->i_read_bytes += i_mData_bytes;
1320         current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1321                         ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
1322
1323         if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
1324             p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
1325     }
1326
1327     while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
1328     {
1329         /* We don't have enough data yet */
1330         aout_buffer_t * p_buffer;
1331         p_buffer = aout_OutputNextBuffer( p_aout, current_date , false );
1332
1333         if( p_buffer != NULL )
1334         {
1335             uint32_t i_second_mData_bytes = __MIN( p_buffer->i_buffer, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1336
1337             vlc_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes,
1338                         p_buffer->p_buffer, i_second_mData_bytes );
1339             i_mData_bytes += i_second_mData_bytes;
1340
1341             if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
1342             {
1343                 p_sys->i_total_bytes = p_buffer->i_buffer - i_second_mData_bytes;
1344                 vlc_memcpy( p_sys->p_remainder_buffer,
1345                             &p_buffer->p_buffer[i_second_mData_bytes],
1346                             p_sys->i_total_bytes );
1347                 aout_BufferFree( p_buffer );
1348                 break;
1349             }
1350             else
1351             {
1352                 /* update current_date */
1353                 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1354                                 ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
1355             }
1356             aout_BufferFree( p_buffer );
1357         }
1358         else
1359         {
1360              vlc_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes,
1361                          0,ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1362              i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
1363         }
1364     }
1365     return( noErr );
1366 }
1367
1368 /*****************************************************************************
1369  * RenderCallbackSPDIF: callback for SPDIF audio output
1370  *****************************************************************************/
1371 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
1372                                     const AudioTimeStamp * inNow,
1373                                     const void * inInputData,
1374                                     const AudioTimeStamp * inInputTime,
1375                                     AudioBufferList * outOutputData,
1376                                     const AudioTimeStamp * inOutputTime,
1377                                     void * threadGlobals )
1378 {
1379     aout_buffer_t * p_buffer;
1380     mtime_t         current_date;
1381
1382     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
1383     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1384
1385     VLC_UNUSED(inDevice);
1386     VLC_UNUSED(inInputData);
1387     VLC_UNUSED(inInputTime);
1388
1389     /* Check for the difference between the Device clock and mdate */
1390     p_sys->clock_diff = - (mtime_t)
1391         AudioConvertHostTimeToNanos( inNow->mHostTime ) / 1000;
1392     p_sys->clock_diff += mdate();
1393
1394     current_date = p_sys->clock_diff +
1395                    AudioConvertHostTimeToNanos( inOutputTime->mHostTime ) / 1000;
1396                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1397
1398     p_buffer = aout_OutputNextBuffer( p_aout, current_date, true );
1399
1400 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1401     if( p_buffer != NULL )
1402     {
1403         if( (int)BUFFER.mDataByteSize != (int)p_buffer->i_buffer)
1404             msg_Warn( p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_buffer );
1405
1406         /* move data into output data buffer */
1407         vlc_memcpy( BUFFER.mData, p_buffer->p_buffer, p_buffer->i_buffer );
1408         aout_BufferFree( p_buffer );
1409     }
1410     else
1411     {
1412         vlc_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
1413     }
1414 #undef BUFFER
1415
1416     return( noErr );
1417 }
1418
1419 /*****************************************************************************
1420  * HardwareListener: Warns us of changes in the list of registered devices
1421  *****************************************************************************/
1422 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1423                                   void * inClientData )
1424 {
1425     OSStatus err = noErr;
1426     aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
1427
1428     switch( inPropertyID )
1429     {
1430         case kAudioHardwarePropertyDevices:
1431         {
1432             /* something changed in the list of devices */
1433             /* We trigger the audio-device's aout_ChannelsRestart callback */
1434             var_TriggerCallback( p_aout, "audio-device" );
1435             var_Destroy( p_aout, "audio-device" );
1436         }
1437         break;
1438     }
1439
1440     return( err );
1441 }
1442
1443 /*****************************************************************************
1444  * StreamListener
1445  *****************************************************************************/
1446 static OSStatus StreamListener( AudioStreamID inStream,
1447                                 UInt32 inChannel,
1448                                 AudioDevicePropertyID inPropertyID,
1449                                 void * inClientData )
1450 {
1451     OSStatus err = noErr;
1452     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1453
1454     VLC_UNUSED(inStream);
1455     VLC_UNUSED(inChannel);
1456
1457     switch( inPropertyID )
1458     {
1459         case kAudioStreamPropertyPhysicalFormat:
1460             vlc_mutex_lock( &w->lock );
1461             vlc_cond_signal( &w->cond );
1462             vlc_mutex_unlock( &w->lock );
1463             break;
1464
1465         default:
1466             break;
1467     }
1468     return( err );
1469 }
1470
1471 /*****************************************************************************
1472  * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1473  *****************************************************************************/
1474 static int AudioDeviceCallback( vlc_object_t *p_this, const char *psz_variable,
1475                      vlc_value_t old_val, vlc_value_t new_val, void *param )
1476 {
1477     aout_instance_t *p_aout = (aout_instance_t *)p_this;
1478     var_Set( p_aout->p_libvlc, "macosx-audio-device", new_val );
1479     msg_Dbg( p_aout, "Set Device: %#"PRIx64, new_val.i_int );
1480     return aout_ChannelsRestart( p_this, psz_variable, old_val, new_val, param );
1481 }
1482