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