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