]> git.sesse.net Git - vlc/commitdiff
Mac OS X audio module is now unified for analog and digital output. todo: test on...
authorDerk-Jan Hartman <hartman@videolan.org>
Wed, 7 Dec 2005 01:45:10 +0000 (01:45 +0000)
committerDerk-Jan Hartman <hartman@videolan.org>
Wed, 7 Dec 2005 01:45:10 +0000 (01:45 +0000)
modules/audio_output/auhal.c

index a5cabdbabc13722da348f5438d6da6028ab6db39..b747a6b76b94f302e21b94b4297bc48d15865f70 100644 (file)
@@ -54,7 +54,7 @@
     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
 
 #define BUFSIZE 0xffffff
     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
 
 #define BUFSIZE 0xffffff
-#define AOUT_VAR_SPDIF_FLAG 0xf0000000
+#define AOUT_VAR_SPDIF_FLAG 0xf00000
 
 /*****************************************************************************
  * aout_sys_t: private audio output method descriptor
 
 /*****************************************************************************
  * aout_sys_t: private audio output method descriptor
@@ -69,31 +69,48 @@ struct aout_sys_t
     UInt32                      i_devices;      /* Number of CoreAudio Devices */
     vlc_bool_t                  b_supports_digital;/* Does the currently selected device support digital mode? */
     vlc_bool_t                  b_digital;      /* Are we running in digital mode? */
     UInt32                      i_devices;      /* Number of CoreAudio Devices */
     vlc_bool_t                  b_supports_digital;/* Does the currently selected device support digital mode? */
     vlc_bool_t                  b_digital;      /* Are we running in digital mode? */
+    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
+    /* AUHAL specific */
     Component                   au_component;   /* The Audiocomponent we use */
     AudioUnit                   au_unit;        /* The AudioUnit we use */
     Component                   au_component;   /* The Audiocomponent we use */
     AudioUnit                   au_unit;        /* The AudioUnit we use */
-    mtime_t                     clock_diff;
     uint8_t                     p_remainder_buffer[BUFSIZE];
     uint32_t                    i_read_bytes;
     uint32_t                    i_total_bytes;
     uint8_t                     p_remainder_buffer[BUFSIZE];
     uint32_t                    i_read_bytes;
     uint32_t                    i_total_bytes;
+    /* CoreAudio SPDIF mode specific */
+    pid_t                       i_hog_pid;      /* The keep the pid of our hog status */
+    AudioStreamID               i_stream_id;    /* The StreamID that has a cac3 streamformat */
+    int                         i_stream_index; /* The index of i_stream_id in an AudioBufferList */
+    AudioStreamBasicDescription stream_format;  /* The format we changed the to */
+    AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
+    vlc_bool_t                  b_revert;       /* Wether we need to revert the stream format */
+    vlc_bool_t                  b_changed_mixing;/* Wether we need to set the mixing mode back */
 };
 
 /*****************************************************************************
  * Local prototypes.
  *****************************************************************************/
 static int      Open                    ( vlc_object_t * );
 };
 
 /*****************************************************************************
  * Local prototypes.
  *****************************************************************************/
 static int      Open                    ( vlc_object_t * );
-int             OpenAnalog              ( aout_instance_t * );
-int             OpenSPDIF               ( aout_instance_t * );
+static int      OpenAnalog              ( aout_instance_t * );
+static int      OpenSPDIF               ( aout_instance_t * );
 static void     Close                   ( vlc_object_t * );
 
 static void     Play                    ( aout_instance_t * );
 static void     Probe                   ( aout_instance_t * );
 
 static void     Close                   ( vlc_object_t * );
 
 static void     Play                    ( aout_instance_t * );
 static void     Probe                   ( aout_instance_t * );
 
-int             AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
-int             AudioDeviceHasOutput    ( AudioDeviceID );
+static int      AudioDeviceHasOutput    ( AudioDeviceID );
+static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
+static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
 
 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                           unsigned int, unsigned int, AudioBufferList *);
 
 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                           unsigned int, unsigned int, AudioBufferList *);
+static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
+                                          AudioBufferList *, const AudioTimeStamp *, void * );
 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
+static OSStatus StreamListener          ( AudioStreamID, UInt32,
+                                          AudioDevicePropertyID, void * );
+static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
+                                          vlc_value_t, vlc_value_t, void * );
+
 
 /*****************************************************************************
  * Module descriptor
 
 /*****************************************************************************
  * Module descriptor
@@ -110,16 +127,16 @@ vlc_module_begin();
     set_category( CAT_AUDIO );
     set_subcategory( SUBCAT_AUDIO_AOUT );
     set_callbacks( Open, Close );
     set_category( CAT_AUDIO );
     set_subcategory( SUBCAT_AUDIO_AOUT );
     set_callbacks( Open, Close );
-    //add_integer( "macosx-audio-device", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
+    add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
 vlc_module_end();
 
 /*****************************************************************************
 vlc_module_end();
 
 /*****************************************************************************
- * Open: open a HAL AudioUnit
+ * Open: open macosx audio output
  *****************************************************************************/
 static int Open( vlc_object_t * p_this )
 {
     OSStatus                err = noErr;
  *****************************************************************************/
 static int Open( vlc_object_t * p_this )
 {
     OSStatus                err = noErr;
-    UInt32                  i_param_size = 0, i_hog = 0;
+    UInt32                  i_param_size = 0;
     struct aout_sys_t       *p_sys = NULL;
     vlc_bool_t              b_alive = VLC_FALSE;
     vlc_value_t             val;
     struct aout_sys_t       *p_sys = NULL;
     vlc_bool_t              b_alive = VLC_FALSE;
     vlc_value_t             val;
@@ -144,12 +161,24 @@ static int Open( vlc_object_t * p_this )
     p_sys->clock_diff = (mtime_t) 0;
     p_sys->i_read_bytes = 0;
     p_sys->i_total_bytes = 0;
     p_sys->clock_diff = (mtime_t) 0;
     p_sys->i_read_bytes = 0;
     p_sys->i_total_bytes = 0;
+    p_sys->i_hog_pid = -1;
+    p_sys->i_stream_id = 0;
+    p_sys->i_stream_index = 0;
+    p_sys->b_revert = VLC_FALSE;
+    p_sys->b_changed_mixing = VLC_FALSE;
     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
 
     p_aout->output.pf_play = Play;
     
     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
 
     p_aout->output.pf_play = Play;
     
-    aout_FormatPrint( p_aout, "VLC is looking for:\n", (audio_sample_format_t *)&p_aout->output.output );
+    aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
     
     
+    /* Persistent device variable */
+    if( var_Type( p_aout->p_vlc, "macosx-audio-device" ) == 0 )
+    {
+        msg_Dbg( p_aout, "create macosx-audio-device" );
+        var_Create( p_aout->p_vlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    }
+
     /* Build a list of devices */
     if( var_Type( p_aout, "audio-device" ) == 0 )
     {
     /* Build a list of devices */
     if( var_Type( p_aout, "audio-device" ) == 0 )
     {
@@ -182,16 +211,14 @@ static int Open( vlc_object_t * p_this )
 
     if( b_alive == VLC_FALSE )
     {
 
     if( b_alive == VLC_FALSE )
     {
-        msg_Err( p_aout, "Selected audio device is not alive" ); 
-        var_Destroy( p_aout, "audio-device" );
-        free( p_sys );
-        return VLC_EGENERIC;
+        msg_Err( p_aout, "Selected audio device is not alive switching to default device" ); 
+        p_sys->i_selected_dev = p_sys->i_default_dev;
     }
 
     }
 
-    i_param_size = sizeof( i_hog );
+    i_param_size = sizeof( p_sys->i_hog_pid );
     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                   kAudioDevicePropertyHogMode,
     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                   kAudioDevicePropertyHogMode,
-                                  &i_param_size, &i_hog );
+                                  &i_param_size, &p_sys->i_hog_pid );
 
     if( err != noErr )
     {
 
     if( err != noErr )
     {
@@ -200,7 +227,7 @@ static int Open( vlc_object_t * p_this )
         return VLC_EGENERIC;
     }
 
         return VLC_EGENERIC;
     }
 
-    if( i_hog != -1 )
+    if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
     {
         msg_Err( p_aout, "Selected audio device is exclusively in use by another program" );
         var_Destroy( p_aout, "audio-device" );
     {
         msg_Err( p_aout, "Selected audio device is exclusively in use by another program" );
         var_Destroy( p_aout, "audio-device" );
@@ -226,7 +253,10 @@ static int Open( vlc_object_t * p_this )
     return VLC_EGENERIC;
 }
 
     return VLC_EGENERIC;
 }
 
-int OpenAnalog( aout_instance_t *p_aout )
+/*****************************************************************************
+ * Open: open and setup a HAL AudioUnit
+ *****************************************************************************/
+static int OpenAnalog( aout_instance_t *p_aout )
 {
     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
     OSStatus                err = noErr;
 {
     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
     OSStatus                err = noErr;
@@ -261,6 +291,7 @@ int OpenAnalog( aout_instance_t *p_aout )
     }
     
     /* Enable IO for the component */
     }
     
     /* Enable IO for the component */
+    msg_Dbg( p_aout, "Device: %#x", (int)p_sys->i_selected_dev );
     
     /* Set the device */
     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
     
     /* Set the device */
     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
@@ -268,7 +299,7 @@ int OpenAnalog( aout_instance_t *p_aout )
                          kAudioUnitScope_Global,
                          0,
                          &p_sys->i_selected_dev,
                          kAudioUnitScope_Global,
                          0,
                          &p_sys->i_selected_dev,
-                         sizeof(p_sys->i_selected_dev)));
+                         sizeof( AudioDeviceID )));
                          
     /* Get the current format */
     AudioStreamBasicDescription DeviceFormat;
                          
     /* Get the current format */
     AudioStreamBasicDescription DeviceFormat;
@@ -498,13 +529,10 @@ int OpenAnalog( aout_instance_t *p_aout )
     p_aout->output.i_nb_samples = 2048;
     aout_VolumeSoftInit( p_aout );
 
     p_aout->output.i_nb_samples = 2048;
     aout_VolumeSoftInit( p_aout );
 
-    /* Let's pray for the following operation to be atomic... */
+    /* Find the difference between device clock and mdate clock */
     p_sys->clock_diff = - (mtime_t)
         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
     p_sys->clock_diff += mdate();
     p_sys->clock_diff = - (mtime_t)
         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
     p_sys->clock_diff += mdate();
-    
-    p_sys->i_read_bytes = 0;
-    p_sys->i_total_bytes = 0;
 
     /* set the IOproc callback */
     AURenderCallbackStruct input;
 
     /* set the IOproc callback */
     AURenderCallbackStruct input;
@@ -536,25 +564,258 @@ int OpenAnalog( aout_instance_t *p_aout )
 /*****************************************************************************
  * Setup a encoded digital stream (SPDIF)
  *****************************************************************************/
 /*****************************************************************************
  * Setup a encoded digital stream (SPDIF)
  *****************************************************************************/
-int OpenSPDIF( aout_instance_t * p_aout )
+static int OpenSPDIF( aout_instance_t * p_aout )
 {
 {
-    OSStatus            err = noErr;
-    UInt32              i, i_param_size;
-    AudioDeviceID       devid_def;
-    AudioDeviceID       *p_devices = NULL;
-    vlc_value_t         val, text;
+    struct aout_sys_t       *p_sys = p_aout->output.p_sys;
+    OSStatus                err = noErr;
+    UInt32                  i_param_size = 0, b_mix = 0;
+    Boolean                 b_writeable = VLC_FALSE;
+    AudioStreamID           *p_streams = NULL;
+    int                     i = 0, i_streams = 0;
 
 
-    struct aout_sys_t   *p_sys = p_aout->output.p_sys;
+    struct timeval now;
+    struct timespec timeout;
+    struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
 
     /* Start doing the SPDIF setup proces */
     p_sys->b_digital = VLC_TRUE;
 
     /* Start doing the SPDIF setup proces */
     p_sys->b_digital = VLC_TRUE;
-    p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
-    msg_Dbg( p_aout, "we found a digital stream, and we WANT a digital stream" );
+    msg_Dbg( p_aout, "opening in SPDIF mode" );
+
+    /* Hog the device */
+    i_param_size = sizeof( p_sys->i_hog_pid );
+    p_sys->i_hog_pid = getpid() ;
+    
+    err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
+                                  kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
     
     
-    return VLC_FALSE;
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "failed to set hogmode: : [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
 
 
-error:
-    return VLC_FALSE;
+    /* Set mixable to false if we are allowed to */
+    err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
+                                    &i_param_size, &b_writeable );
+
+    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
+                                    &i_param_size, &b_mix );
+                                    
+    if( !err && b_writeable )
+    {
+        b_mix = 0;
+        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
+                            kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
+        p_sys->b_changed_mixing = VLC_TRUE;
+    }
+    
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
+
+    // Find stream_id of selected device with a cac3 stream
+    err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
+                                      kAudioDevicePropertyStreams,
+                                      &i_param_size, NULL );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
+    
+    i_streams = i_param_size / sizeof( AudioStreamID );
+    p_streams = (AudioStreamID *)malloc( i_param_size );
+    if( p_streams == NULL )
+    {
+        msg_Err( p_aout, "Out of memory" );
+        return VLC_FALSE;
+    }
+    
+    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
+                                    kAudioDevicePropertyStreams,
+                                    &i_param_size, p_streams );
+    
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
+        if( p_streams ) free( p_streams );
+        return VLC_FALSE;
+    }
+
+    for( i = 0; i < i_streams; i++ )
+    {
+        // Find a stream with a cac3 stream
+        AudioStreamBasicDescription *p_format_list = NULL;
+        int                         i_formats = 0, j = 0;
+        
+        /* Retrieve all the stream formats supported by each output stream */
+        err = AudioStreamGetPropertyInfo( p_streams[i], 0,
+                                          kAudioStreamPropertyPhysicalFormats,
+                                          &i_param_size, NULL );
+        if( err != noErr )
+        {
+            msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
+            continue;
+        }
+        
+        i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
+        p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
+        if( p_format_list == NULL )
+        {
+            msg_Err( p_aout, "Could not malloc the memory" );
+            continue;
+        }
+        
+        err = AudioStreamGetProperty( p_streams[i], 0,
+                                          kAudioStreamPropertyPhysicalFormats,
+                                          &i_param_size, p_format_list );
+        if( err != noErr )
+        {
+            msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
+            if( p_format_list) free( p_format_list);
+            continue;
+        }
+
+        for( j = 0; j < i_formats; j++ )
+        {
+            if( p_format_list[j].mFormatID == 'IAC3' ||
+                  p_format_list[j].mFormatID == kAudioFormat60958AC3 )
+            {
+                // found a cac3 format
+                p_sys->i_stream_id = p_streams[i];
+                p_sys->i_stream_index = i;
+
+                if( p_sys->b_revert == VLC_FALSE )
+                {
+                    i_param_size = sizeof( p_sys->sfmt_revert );
+                    err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
+                                                  kAudioStreamPropertyPhysicalFormat,
+                                                  &i_param_size, 
+                                                  &p_sys->sfmt_revert );
+                    if( err != noErr )
+                    {
+                        msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
+                        continue; 
+                    }
+                    p_sys->b_revert = VLC_TRUE;
+                }
+                if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
+                {
+                    p_sys->stream_format = p_format_list[j];
+                }
+            }
+        }
+        if( p_format_list ) free( p_format_list );
+    }
+    
+    if( p_streams ) free( p_streams );
+
+    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );
+    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->stream_format ) );
+
+    /* Install the callback */
+    err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
+                                      kAudioStreamPropertyPhysicalFormat,
+                                      StreamListener, (void *)&w );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
+
+    /* Condition because SetProperty is asynchronious */ 
+    vlc_cond_init( p_aout, &w.cond );
+    vlc_mutex_init( p_aout, &w.lock );
+    vlc_mutex_lock( &w.lock );
+    
+    /* change the format */
+    err = AudioStreamSetProperty( p_sys->i_stream_id, 0, 0,
+                                  kAudioStreamPropertyPhysicalFormat,
+                                  sizeof( AudioStreamBasicDescription ),
+                                  &p_sys->stream_format ); 
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not set the stream format: [%4.4s]", (char *)&err );
+        vlc_mutex_unlock( &w.lock );
+        vlc_mutex_destroy( &w.lock );
+        vlc_cond_destroy( &w.cond );
+        return VLC_FALSE;
+    }
+
+    gettimeofday( &now, NULL );
+    timeout.tv_sec = now.tv_sec;
+    timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
+
+    pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
+    vlc_mutex_unlock( &w.lock );
+
+    err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
+                                        kAudioStreamPropertyPhysicalFormat,
+                                        StreamListener );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
+        vlc_mutex_destroy( &w.lock );
+        vlc_cond_destroy( &w.cond );
+        return VLC_FALSE;
+    }
+
+    vlc_mutex_destroy( &w.lock );
+    vlc_cond_destroy( &w.cond );
+    
+    i_param_size = sizeof( AudioStreamBasicDescription );
+    err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
+                                  kAudioStreamPropertyPhysicalFormat,
+                                  &i_param_size, 
+                                  &p_sys->stream_format );
+
+    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
+
+    /* set the format flags */
+    if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
+        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
+    else
+        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
+    p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
+    p_aout->output.output.i_frame_length = A52_FRAME_NB;
+    p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
+    p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
+
+    aout_VolumeNoneInit( p_aout );
+
+    /* Add IOProc callback */
+    err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
+                                (AudioDeviceIOProc)RenderCallbackSPDIF,
+                                (void *)p_aout );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
+
+    /* Check for the difference between the Device clock and mdate */
+    p_sys->clock_diff = - (mtime_t)
+        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
+    p_sys->clock_diff += mdate();
+    /* Start device */
+    err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF ); 
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );
+
+        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev, 
+                                       (AudioDeviceIOProc)RenderCallbackSPDIF );
+        if( err != noErr )
+        {
+            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
+        }
+        return VLC_FALSE;
+    }
+
+    return VLC_TRUE;
 }
 
 
 }
 
 
@@ -565,6 +826,8 @@ static void Close( vlc_object_t * p_this )
 {
     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
 {
     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
+    OSStatus            err = noErr;
+    UInt32              i_param_size = 0;
     
     if( p_sys->au_unit )
     {
     
     if( p_sys->au_unit )
     {
@@ -572,7 +835,127 @@ static void Close( vlc_object_t * p_this )
         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
         verify_noerr( CloseComponent( p_sys->au_unit ) );
     }
         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
         verify_noerr( CloseComponent( p_sys->au_unit ) );
     }
-    free( p_sys );
+    
+    if( p_sys->b_digital )
+    {
+        /* Stop device */
+        err = AudioDeviceStop( p_sys->i_selected_dev, 
+                               (AudioDeviceIOProc)RenderCallbackSPDIF ); 
+        if( err != noErr )
+        {
+            msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
+        }
+
+        /* Remove callback */
+        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
+                                       (AudioDeviceIOProc)RenderCallbackSPDIF );
+        if( err != noErr )
+        {
+            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
+        }
+        
+        if( p_sys->b_revert )
+        {
+            struct timeval now;
+            struct timespec timeout;
+            struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
+
+            /* Install the callback */
+            err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
+                                              kAudioStreamPropertyPhysicalFormat,
+                                              StreamListener, (void *)&w );
+            if( err != noErr )
+            {
+                msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
+            }
+
+            /* Condition because SetProperty is asynchronious */ 
+            vlc_cond_init( p_aout, &w.cond );
+            vlc_mutex_init( p_aout, &w.lock );
+            vlc_mutex_lock( &w.lock );
+
+            msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->sfmt_revert ) );
+
+            err = AudioStreamSetProperty( p_sys->i_stream_id, NULL, 0,
+                                            kAudioStreamPropertyPhysicalFormat,
+                                            sizeof( AudioStreamBasicDescription ),
+                                            &p_sys->sfmt_revert );
+                                            
+            if( err != noErr )
+            {
+                msg_Err( p_aout, "Streamformat reverse failed: [%4.4s]", (char *)&err );
+            }
+            
+            gettimeofday( &now, NULL );
+            timeout.tv_sec = now.tv_sec;
+            timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
+
+            pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
+            vlc_mutex_unlock( &w.lock );
+
+            err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
+                                                kAudioStreamPropertyPhysicalFormat,
+                                                StreamListener );
+            if( err != noErr )
+            {
+                msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
+            }
+
+            vlc_mutex_destroy( &w.lock );
+            vlc_cond_destroy( &w.cond );
+            
+            i_param_size = sizeof( AudioStreamBasicDescription );
+            err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
+                                          kAudioStreamPropertyPhysicalFormat,
+                                          &i_param_size, 
+                                          &p_sys->stream_format );
+
+            msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
+        }
+        if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
+        {
+            int b_mix;
+            Boolean b_writeable;
+            /* Revert mixable to true if we are allowed to */
+            err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
+                                        &i_param_size, &b_writeable );
+
+            err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
+                                        &i_param_size, &b_mix );
+                                        
+            if( !err && b_writeable )
+            {
+                msg_Dbg( p_aout, "mixable is: %d", b_mix );
+                b_mix = 1;
+                err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
+                                    kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
+            }
+
+            if( err != noErr )
+            {
+                msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
+            }
+        }
+    }
+
+    err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
+                                               HardwareListener );
+                                               
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
+    }
+    
+    if( p_sys->i_hog_pid == getpid() )
+    {
+        p_sys->i_hog_pid = -1;
+        i_param_size = sizeof( p_sys->i_hog_pid );
+        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
+                                         kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
+        if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
+    }
+    
+    if( p_sys ) free( p_sys );
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -684,7 +1067,7 @@ static void Probe( aout_instance_t * p_aout )
             var_Set( p_aout, "audio-device", val );
         }
 
             var_Set( p_aout, "audio-device", val );
         }
 
-        if( AudioDeviceSupportsDigital( p_aout, p_devices[i]) )
+        if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
         {
             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
             asprintf( &text.psz_string, "%s (Encoded Output)", psz_name );
         {
             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
             asprintf( &text.psz_string, "%s (Encoded Output)", psz_name );
@@ -698,8 +1081,18 @@ static void Probe( aout_instance_t * p_aout )
         
         free( psz_name);
     }
         
         free( psz_name);
     }
-    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
     
     
+    var_Get( p_aout->p_vlc, "macosx-audio-device", &val );
+    msg_Dbg( p_aout, "device value override1: %#x", val.i_int );
+    if( val.i_int > 0 )
+    {
+        msg_Dbg( p_aout, "device value override2: %#x", val.i_int );
+        var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
+        var_Set( p_aout, "audio-device", val );
+    }
+    
+    var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );
+
     /* attach a Listener so that we are notified of a change in the Device setup */
     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
                                             HardwareListener, 
     /* attach a Listener so that we are notified of a change in the Device setup */
     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
                                             HardwareListener, 
@@ -717,17 +1110,29 @@ error:
     return;
 }
 
     return;
 }
 
+/*****************************************************************************
+ * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
+ *****************************************************************************/
+static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
+{
+    UInt32                     dataSize;
+    Boolean                    isWritable;
+       
+    verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
+    if (dataSize == 0) return FALSE;
+    
+    return TRUE;
+}
+
 /*****************************************************************************
  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
  *****************************************************************************/
 /*****************************************************************************
  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
  *****************************************************************************/
-int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
+static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
 {
     OSStatus                    err = noErr;
     UInt32                      i_param_size = 0;
 {
     OSStatus                    err = noErr;
     UInt32                      i_param_size = 0;
-    AudioStreamBasicDescription *p_format_list = NULL;
     AudioStreamID               *p_streams = NULL;
     AudioStreamID               *p_streams = NULL;
-    int                         i = 0, j = 0; 
-    int                         i_formats = 0, i_streams = 0;
+    int                         i = 0, i_streams = 0;
     vlc_bool_t                  b_return = VLC_FALSE;
     
     /* Retrieve all the output streams */
     vlc_bool_t                  b_return = VLC_FALSE;
     
     /* Retrieve all the output streams */
@@ -760,54 +1165,67 @@ int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id
 
     for( i = 0; i < i_streams; i++ )
     {
 
     for( i = 0; i < i_streams; i++ )
     {
+        if( AudioStreamSupportsDigital( p_aout, p_streams[i] ) )
+            b_return = VLC_TRUE;
+    }
+    
+    if( p_streams ) free( p_streams );
+    return b_return;
+}
+
+/*****************************************************************************
+ * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
+ *****************************************************************************/
+static int AudioStreamSupportsDigital( aout_instance_t *p_aout, AudioStreamID i_stream_id )
+{
+    OSStatus                    err = noErr;
+    UInt32                      i_param_size = 0;
+    AudioStreamBasicDescription *p_format_list = NULL;
+    int                         i = 0, i_formats = 0;
+    vlc_bool_t                  b_return = VLC_FALSE;
+    
+    /* Retrieve all the stream formats supported by each output stream */
+    err = AudioStreamGetPropertyInfo( i_stream_id, 0,
+                                      kAudioStreamPropertyPhysicalFormats,
+                                      &i_param_size, NULL );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
+        return VLC_FALSE;
+    }
+    
+    i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
+    msg_Dbg( p_aout, "number of formats: %d", i_formats );
+    p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
+    if( p_format_list == NULL )
+    {
+        msg_Err( p_aout, "Could not malloc the memory" );
+        return VLC_FALSE;
+    }
+    
+    err = AudioStreamGetProperty( i_stream_id, 0,
+                                      kAudioStreamPropertyPhysicalFormats,
+                                      &i_param_size, p_format_list );
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
+        free( p_format_list);
         p_format_list = NULL;
         p_format_list = NULL;
-        i_formats = 0;
-        msg_Dbg( p_aout, "retrieving formats for stream: %#lx", p_streams[i] );
+        return VLC_FALSE;
+    }
 
 
-        /* Retrieve all the stream formats supported by each output stream */
-        err = AudioStreamGetPropertyInfo( p_streams[i], 0,
-                                          kAudioDevicePropertyStreamFormats,
-                                          &i_param_size, NULL );
-        if( err != noErr )
-        {
-            msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
-            break;
-        }
-        
-        i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
-        p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
-        if( p_format_list == NULL )
-        {
-            break;
-        }
+    for( i = 0; i < i_formats; i++ )
+    {
+        msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
         
         
-        err = AudioStreamGetProperty( p_streams[i], 0,
-                                          kAudioDevicePropertyStreamFormats,
-                                          &i_param_size, p_format_list );
-        if( err != noErr )
-        {
-            msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
-            free( p_format_list);
-            break;
-        }
-
-        for( j = 0; j < i_formats; j++ )
+        if( p_format_list[i].mFormatID == 'IAC3' ||
+                  p_format_list[i].mFormatID == kAudioFormat60958AC3 )
         {
         {
-            msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[j] ) );
-            
-            if( p_format_list[j].mFormatID == 'IAC3' ||
-                      p_format_list[j].mFormatID == kAudioFormat60958AC3 )
-            {
-                b_return = VLC_TRUE;
-                msg_Dbg( p_aout, "this device supports a digital stream" );
-                break;
-            }
+            b_return = VLC_TRUE;
         }
         }
-        
-        if( p_format_list ) free( p_format_list );
     }
     
     }
     
-    if( p_streams ) free( p_streams );
+    if( p_format_list ) free( p_format_list );
     return b_return;
 }
 
     return b_return;
 }
 
@@ -834,6 +1252,7 @@ static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
     host_time.mFlags = kAudioTimeStampHostTimeValid;
     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
 
     host_time.mFlags = kAudioTimeStampHostTimeValid;
     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
 
+    /* Check for the difference between the Device clock and mdate */
     p_sys->clock_diff = - (mtime_t)
         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
     p_sys->clock_diff += mdate();
     p_sys->clock_diff = - (mtime_t)
         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
     p_sys->clock_diff += mdate();
@@ -898,15 +1317,52 @@ static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
     return( noErr );     
 }
 
     return( noErr );     
 }
 
-int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
+/*****************************************************************************
+ * RenderCallbackSPDIF: callback for SPDIF audio output
+ *****************************************************************************/
+static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
+                                    const AudioTimeStamp * inNow, 
+                                    const void * inInputData,
+                                    const AudioTimeStamp * inInputTime, 
+                                    AudioBufferList * outOutputData,
+                                    const AudioTimeStamp * inOutputTime, 
+                                    void * threadGlobals )
 {
 {
-    UInt32                     dataSize;
-    Boolean                    isWritable;
-       
-    verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
-    if (dataSize == 0) return FALSE;
-    
-    return TRUE;
+    aout_buffer_t * p_buffer;
+    mtime_t         current_date;
+
+    aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
+    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+
+    /* Check for the difference between the Device clock and mdate */
+    p_sys->clock_diff = - (mtime_t)
+        AudioConvertHostTimeToNanos( inNow->mHostTime ) / 1000; 
+    p_sys->clock_diff += mdate();
+
+    current_date = p_sys->clock_diff +
+                   AudioConvertHostTimeToNanos( inOutputTime->mHostTime ) / 1000;
+                   //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
+
+    p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_TRUE );
+
+#define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
+    if( p_buffer != NULL )
+    {
+        if( (int)BUFFER.mDataByteSize != (int)p_buffer->i_nb_bytes)
+            msg_Warn( p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_nb_bytes );
+        
+        /* move data into output data buffer */
+        p_aout->p_vlc->pf_memcpy( BUFFER.mData,
+                                  p_buffer->p_buffer, p_buffer->i_nb_bytes );
+        aout_BufferFree( p_buffer );
+    }
+    else
+    {
+        p_aout->p_vlc->pf_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
+    }
+#undef BUFFER
+
+    return( noErr );     
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -925,9 +1381,48 @@ static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
             /* something changed in the list of devices */
             /* We trigger the audio-device's aout_ChannelsRestart callback */
             var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
             /* something changed in the list of devices */
             /* We trigger the audio-device's aout_ChannelsRestart callback */
             var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
+            var_Destroy( p_aout, "audio-device" );
         }
         break;
     }
 
     return( err );
 }
         }
         break;
     }
 
     return( err );
 }
+
+/*****************************************************************************
+ * StreamListener 
+ *****************************************************************************/
+static OSStatus StreamListener( AudioStreamID inStream,
+                                UInt32 inChannel,
+                                AudioDevicePropertyID inPropertyID,
+                                void * inClientData )
+{
+    OSStatus err = noErr;
+    struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
+
+    switch( inPropertyID )
+    {
+        case kAudioStreamPropertyPhysicalFormat:
+            vlc_mutex_lock( &w->lock );
+            vlc_cond_signal( &w->cond );
+            vlc_mutex_unlock( &w->lock ); 
+            break;
+
+        default:
+            break;
+    }
+    return( err );
+}
+
+/*****************************************************************************
+ * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
+ *****************************************************************************/
+static int AudioDeviceCallback( vlc_object_t *p_this, const char *psz_variable,
+                     vlc_value_t old_val, vlc_value_t new_val, void *param )
+{
+    aout_instance_t *p_aout = (aout_instance_t *)p_this;
+    var_Set( p_aout->p_vlc, "macosx-audio-device", new_val );
+    msg_Dbg( p_aout, "Set Device: %#x", new_val.i_int );
+    return aout_ChannelsRestart( p_this, psz_variable, old_val, new_val, param );
+}
+