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