]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/auhal.c
* Don't upmix the channels if we only have Stereo or Mono audio. Let the device setup...
[vlc] / modules / audio_output / auhal.c
index 515ae7200146bd970e0c945212680f3660702e75..2bf861b5e3596eab9da901315a9fa428a7152d10 100644 (file)
@@ -1,8 +1,8 @@
 /*****************************************************************************
  * auhal.c: AUHAL output plugin
  *****************************************************************************
- * Copyright (C) 2005 VideoLAN
- * $Id: coreaudio.c 10101 2005-03-02 16:47:31Z robux4 $
+ * Copyright (C) 2005 the VideoLAN team
+ * $Id$
  *
  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
  *
@@ -26,6 +26,7 @@
  *****************************************************************************/
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <vlc/vlc.h>
 #include <vlc/aout.h>
 #include <AudioToolbox/AudioFormat.h>
 
 #define STREAM_FORMAT_MSG( pre, sfm ) \
+    pre "[%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
+    (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
+    sfm.mFormatFlags, sfm.mBytesPerPacket, \
+    sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
+    sfm.mChannelsPerFrame, sfm.mBitsPerChannel
+
+#define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
     pre ":\nsamplerate: [%ld]\nFormatID: [%4.4s]\nFormatFlags: [%ld]\nBypesPerPacket: [%ld]\nFramesPerPacket: [%ld]\nBytesPerFrame: [%ld]\nChannelsPerFrame: [%ld]\nBitsPerChannel[%ld]", \
     (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
     sfm.mFormatFlags, sfm.mBytesPerPacket, \
     sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
 
+#define BUFSIZE 0xffffff
+#define AOUT_VAR_SPDIF_FLAG 0xf00000
+
+/*
+ * TODO:
+ * - clean up the debug info
+ * - clean up C99'isms
+ * - be better at changing stream setup or devices setup changes while playing.
+ */
 
 /*****************************************************************************
  * aout_sys_t: private audio output method descriptor
@@ -60,26 +77,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? */
+    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 */
-    mtime_t                     clock_diff;
+    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 * );
+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     Play                    ( aout_instance_t * );
+static void     Probe                   ( aout_instance_t * );
 
-static int      Probe                   ( aout_instance_t * );
-static int      DeviceDigitalMode       ( aout_instance_t *, AudioDeviceID );
-static int      DigitalInit             ( aout_instance_t * );
+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 RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
+                                          AudioBufferList *, const AudioTimeStamp *, 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
@@ -96,81 +135,148 @@ vlc_module_begin();
     set_category( CAT_AUDIO );
     set_subcategory( SUBCAT_AUDIO_AOUT );
     set_callbacks( Open, Close );
-    //add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
+    add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
 vlc_module_end();
 
 /*****************************************************************************
- * Open: open a HAL AudioUnit
+ * Open: open macosx audio output
  *****************************************************************************/
 static int Open( vlc_object_t * p_this )
 {
     OSStatus                err = noErr;
-    ComponentDescription    desc;
-    UInt32                  i_param_size,i;
-    struct aout_sys_t       *p_sys;
+    UInt32                  i_param_size = 0;
+    struct aout_sys_t       *p_sys = NULL;
+    vlc_bool_t              b_alive = VLC_FALSE;
     vlc_value_t             val;
     aout_instance_t         *p_aout = (aout_instance_t *)p_this;
 
     /* Allocate structure */
-    p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
-    if( p_sys == NULL )
+    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
+    if( p_aout->output.p_sys == NULL )
     {
         msg_Err( p_aout, "out of memory" );
         return( VLC_ENOMEM );
     }
 
-    memset( p_sys, 0, sizeof( struct aout_sys_t ) );
-
-    p_sys->b_digital = VLC_FALSE; /* We assume we are not digital */
+    p_sys = p_aout->output.p_sys;
+    p_sys->i_default_dev = 0;
+    p_sys->i_selected_dev = 0;
+    p_sys->i_devices = 0;
+    p_sys->b_supports_digital = VLC_FALSE;
+    p_sys->b_digital = VLC_FALSE;
+    p_sys->au_component = NULL;
+    p_sys->au_unit = NULL;
+    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.p_sys = p_sys;
     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 )
     {
         Probe( p_aout );
-        /*if( Probe( p_aout ) != VLC_SUCCESS );
-        {
-            msg_Err( p_aout, "Probe failed" );
-            free( p_sys );
-            return VLC_EGENERIC;
-        }*/
     }
-    
+
     /* What device do we want? */
     if( var_Get( p_aout, "audio-device", &val ) < 0 )
     {
-        msg_Err( p_aout, "audio-device var does not exist" );
+        msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
         free( p_sys );
         return( VLC_ENOVAR );
     }
-    p_sys->i_selected_dev = val.i_int;
 
-    /* what is vlc format? if digital, take digital route else AUHAL route */
-    DeviceDigitalMode( p_aout, p_sys->i_selected_dev );
-    /*if( DeviceDigitalMode( p_aout, p_sys->i_selected_dev ) != VLC_SUCCESS );
+    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG;
+    p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;
+
+    /* Check if the desired device is alive and usable */
+    i_param_size = sizeof( b_alive );
+    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
+                                  kAudioDevicePropertyDeviceIsAlive,
+                                  &i_param_size, &b_alive );
+
+    if( err != noErr )
     {
-        msg_Err( p_aout, "DeviceDigitalMode failed" );
+        msg_Err( p_aout, "could not check whether device is alive: %4.4s",
+                 (char *)&err );
+        return VLC_EGENERIC;
+    }
+
+    if( b_alive == VLC_FALSE )
+    {
+        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( p_sys->i_hog_pid );
+    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
+                                  kAudioDevicePropertyHogMode,
+                                  &i_param_size, &p_sys->i_hog_pid );
+
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "could not check whether device is hogged: %4.4s",
+                 (char *)&err );
+        return VLC_EGENERIC;
+    }
+
+    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" );
         free( p_sys );
         return VLC_EGENERIC;
     }
-    */
+
+    /* Check for Digital mode or Analog output mode */
     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
     {
-        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" );
+        if( OpenSPDIF( p_aout ) )
+            return VLC_SUCCESS;
+    }
+    else
+    {
+        if( OpenAnalog( p_aout ) )
+            return VLC_SUCCESS;
     }
-    else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
+    
+    /* If we reach this, the Open* failed */
+    var_Destroy( p_aout, "audio-device" );
+    free( p_sys );
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * 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;
+    UInt32                  i_param_size = 0, i = 0;
+    ComponentDescription    desc;
+        
+    if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
     {
         msg_Dbg( p_aout, "we had requested a digital stream, but it's not possible for this device" );
     }
-    /* If analog only start setting up AUHAL */
 
+    /* If analog only start setting up AUHAL */
     /* Lets go find our Component */
     desc.componentType = kAudioUnitType_Output;
     desc.componentSubType = kAudioUnitSubType_HALOutput;
@@ -182,20 +288,18 @@ static int Open( vlc_object_t * p_this )
     if( p_sys->au_component == NULL )
     {
         msg_Err( p_aout, "we cannot find our HAL component" );
-        free( p_sys );
-        return VLC_EGENERIC;
+        return VLC_FALSE;
     }
 
     err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
     if( err )
     {
-        
         msg_Err( p_aout, "we cannot find our HAL component" );
-        free( p_sys );
-        return VLC_EGENERIC;
+        return VLC_FALSE;
     }
     
     /* 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,
@@ -203,7 +307,7 @@ static int Open( vlc_object_t * p_this )
                          kAudioUnitScope_Global,
                          0,
                          &p_sys->i_selected_dev,
-                         sizeof(p_sys->i_selected_dev)));
+                         sizeof( AudioDeviceID )));
                          
     /* Get the current format */
     AudioStreamBasicDescription DeviceFormat;
@@ -217,7 +321,7 @@ static int Open( vlc_object_t * p_this )
                                    &DeviceFormat,
                                    &i_param_size ));
                                    
-    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is " , DeviceFormat ) );
+    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: " , DeviceFormat ) );
 
     /* Get the channel layout */
     AudioChannelLayout *layout;
@@ -258,50 +362,103 @@ static int Open( vlc_object_t * p_this )
     msg_Dbg( p_aout, "Layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
     
     p_aout->output.output.i_physical_channels = 0;
-    for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
+    int i_original = p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK;
+    
+    if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
     {
-        msg_Dbg( p_aout, "This is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
-
-        switch( layout->mChannelDescriptions[i].mChannelLabel )
+        // We only need Mono or cannot output more
+        p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
+    }
+    else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
+    {
+        // We only need Stereo or cannot output more
+        p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
+        p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
+    }
+    else
+    {
+        // We want more then stereo and we can do that
+        for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
         {
-            case kAudioChannelLabel_Left:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
-                continue;
-            case kAudioChannelLabel_Right:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
-                continue;
-            case kAudioChannelLabel_Center:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
-                continue;
-            case kAudioChannelLabel_LFEScreen:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
-                continue;
-            case kAudioChannelLabel_LeftSurround:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
-                continue;
-            case kAudioChannelLabel_RightSurround:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
-                continue;
-            case kAudioChannelLabel_LeftCenter:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
-                continue;
-            case kAudioChannelLabel_RightCenter:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
-                continue;
-            case kAudioChannelLabel_CenterSurround:
-                p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
-                continue;
-            default:
-                msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
-                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
-                break;
+            msg_Dbg( p_aout, "This is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
+
+            switch( layout->mChannelDescriptions[i].mChannelLabel )
+            {
+                case kAudioChannelLabel_Left:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
+                    continue;
+                case kAudioChannelLabel_Right:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
+                    continue;
+                case kAudioChannelLabel_Center:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
+                    continue;
+                case kAudioChannelLabel_LFEScreen:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
+                    continue;
+                case kAudioChannelLabel_LeftSurround:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
+                    continue;
+                case kAudioChannelLabel_RightSurround:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
+                    continue;
+                case kAudioChannelLabel_RearSurroundLeft:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
+                    continue;
+                case kAudioChannelLabel_RearSurroundRight:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
+                    continue;
+                case kAudioChannelLabel_CenterSurround:
+                    p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
+                    continue;
+                default:
+                    msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
+                    if( i == 0 )
+                    {
+                        msg_Warn( p_aout, "Probably no channellayout is set. force based on channelcount" );
+                        switch( layout->mNumberChannelDescriptions )
+                        {
+                            /* We make assumptions based on number of channels here.
+                             * Unfortunatly Apple has provided no 100% method to retrieve the speaker configuration */
+                            case 1:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
+                                break;
+                            case 4:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                                                                            AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+                                break;
+                            case 6:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                                                                            AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
+                                                                            AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+                                break;
+                            case 7:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                                                                            AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
+                                                                            AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
+                                                                            AOUT_CHAN_REARCENTER;
+                                break;
+                            case 8:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                                                                            AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
+                                                                            AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
+                                                                            AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
+                                break;
+                            case 2:
+                            default:
+                                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
+                        }
+                    }
+                    break;
+            }
         }
     }
-    free( layout );
+    if( layout ) free( layout );
 
-    msg_Dbg( p_aout, "defined %d physical channels for vlc core", aout_FormatNbChannels( &p_aout->output.output ) );
+    msg_Dbg( p_aout, "we want to output these channels: %#x", p_aout->output.output.i_original_channels);
+    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
     msg_Dbg( p_aout, "%s", aout_FormatPrintChannels( &p_aout->output.output ));
-    
+
     AudioChannelLayout new_layout;
     memset (&new_layout, 0, sizeof(new_layout));
     switch( aout_FormatNbChannels( &p_aout->output.output ) )
@@ -396,9 +553,10 @@ static int Open( vlc_object_t * p_this )
                                    
     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
 
+    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();
@@ -426,9 +584,268 @@ static int Open( vlc_object_t * p_this )
     verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
 
     verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
-    return( VLC_SUCCESS );
+    
+    return VLC_TRUE;
+}
+
+/*****************************************************************************
+ * Setup a encoded digital stream (SPDIF)
+ *****************************************************************************/
+static int OpenSPDIF( aout_instance_t * p_aout )
+{
+    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 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;
+    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 );
+    
+    if( err != noErr )
+    {
+        msg_Err( p_aout, "failed to set hogmode: : [%4.4s]", (char *)&err );
+        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;
 }
 
+
 /*****************************************************************************
  * Close: Close HAL AudioUnit
  *****************************************************************************/
@@ -436,6 +853,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;
+    OSStatus            err = noErr;
+    UInt32              i_param_size = 0;
     
     if( p_sys->au_unit )
     {
@@ -443,7 +862,127 @@ static void Close( vlc_object_t * p_this )
         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 );
 }
 
 /*****************************************************************************
@@ -457,11 +996,11 @@ static void Play( aout_instance_t * p_aout )
 /*****************************************************************************
  * Probe
  *****************************************************************************/
-static int Probe( aout_instance_t * p_aout )
+static void Probe( aout_instance_t * p_aout )
 {
     OSStatus            err = noErr;
-    UInt32              i, i_param_size;
-    AudioDeviceID       devid_def;
+    UInt32              i = 0, i_param_size = 0;
+    AudioDeviceID       devid_def = 0;
     AudioDeviceID       *p_devices = NULL;
     vlc_value_t         val, text;
 
@@ -487,7 +1026,7 @@ static int Probe( aout_instance_t * p_aout )
     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
 
     /* Allocate DeviceID array */
-    p_devices = (AudioDeviceID *)malloc( i_param_size );
+    p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
     if( p_devices == NULL )
     {
         msg_Err( p_aout, "out of memory" );
@@ -496,7 +1035,7 @@ static int Probe( aout_instance_t * p_aout )
 
     /* Populate DeviceID array */
     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
-                                    &i_param_size, (void *)p_devices );
+                                    &i_param_size, p_devices );
     if( err != noErr )
     {
         msg_Err( p_aout, "could not get the device ID's: [%4.4s]", (char *)&err );
@@ -506,7 +1045,7 @@ static int Probe( aout_instance_t * p_aout )
     /* Find the ID of the default Device */
     i_param_size = sizeof( AudioDeviceID );
     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
-                                    &i_param_size, (void *)&devid_def );
+                                    &i_param_size, &devid_def );
     if( err != noErr )
     {
         msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
@@ -520,111 +1059,201 @@ static int Probe( aout_instance_t * p_aout )
     
     for( i = 0; i < p_sys->i_devices; i++ )
     {
-        char psz_devuid[1024];
-        char psz_name[1024];
-        CFStringRef devUID;
-            
-        i_param_size = sizeof psz_name;
-        err = AudioDeviceGetProperty(
+        char *psz_name;
+        i_param_size = 0;
+
+        /* Retrieve the length of the device name */
+        err = AudioDeviceGetPropertyInfo(
                     p_devices[i], 0, VLC_FALSE,
                     kAudioDevicePropertyDeviceName,
-                    &i_param_size, psz_name);
-        if( err )
-            goto error;
+                    &i_param_size, NULL);
+        if( err ) goto error;
 
-        i_param_size = sizeof(CFStringRef);    
+        /* Retrieve the name of the device */
+        psz_name = (char *)malloc( i_param_size );
         err = AudioDeviceGetProperty(
                     p_devices[i], 0, VLC_FALSE,
-                    kAudioDevicePropertyDeviceUID,
-                    &i_param_size, &devUID);
-        if( err )
-            goto error;
+                    kAudioDevicePropertyDeviceName,
+                    &i_param_size, psz_name);
+        if( err ) goto error;
 
-        CFStringGetCString( devUID, psz_devuid, sizeof psz_devuid, CFStringGetSystemEncoding() );
-        msg_Dbg( p_aout, "DevID: %lu  DevName: %s  DevUID: %s", p_devices[i], psz_name, psz_devuid );
-        CFRelease( devUID );
+        msg_Dbg( p_aout, "DevID: %#lx  DevName: %s", p_devices[i], psz_name );
 
-        val.i_int = (int) p_devices[i];
-        text.psz_string = psz_name;
+        if( !AudioDeviceHasOutput( p_devices[i]) )
+        {
+            msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
+            continue;
+        }
+
+        val.i_int = (int)p_devices[i];
+        text.psz_string = strdup( psz_name );
         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
-        if( devid_def == p_devices[i] )
+        if( p_sys->i_default_dev == p_devices[i] )
         {
             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
             var_Set( p_aout, "audio-device", val );
         }
+
+        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 );
+            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
+            if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
+            {
+                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
+                var_Set( p_aout, "audio-device", val );
+            }
+        }
+        
+        free( psz_name);
+    }
+    
+    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", aout_ChannelsRestart, NULL );
     
+    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,
+    err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
                                             HardwareListener, 
                                             (void *)p_aout );
     if( err )
-        goto error;*/
-    
+        goto error;
+
     msg_Dbg( p_aout, "succesful finish of deviceslist" );
     if( p_devices ) free( p_devices );
-    return (VLC_SUCCESS);
+    return;
 
 error:
     var_Destroy( p_aout, "audio-device" );
     if( p_devices ) free( p_devices );
-    return VLC_EGENERIC;
+    return;
 }
 
 /*****************************************************************************
- * DeviceDigitalMode: Check i_dev_id for digital stream support.
+ * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
  *****************************************************************************/
-static int DeviceDigitalMode( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
+static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
 {
-    OSStatus                    err = noErr;
-    UInt32                      i_param_size;
-    AudioStreamBasicDescription *p_format_list;
-    int                         i, i_formats;
-    struct aout_sys_t           *p_sys = p_aout->output.p_sys;
+    UInt32                     dataSize;
+    Boolean                    isWritable;
+       
+    verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
+    if (dataSize == 0) return FALSE;
     
-    p_sys->b_supports_digital = VLC_FALSE;
+    return TRUE;
+}
+
+/*****************************************************************************
+ * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
+ *****************************************************************************/
+static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
+{
+    OSStatus                    err = noErr;
+    UInt32                      i_param_size = 0;
+    AudioStreamID               *p_streams = NULL;
+    int                         i = 0, i_streams = 0;
+    vlc_bool_t                  b_return = VLC_FALSE;
     
+    /* Retrieve all the output streams */
     err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
-                                      kAudioDevicePropertyStreamFormats,
+                                      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_ENOMEM;
+    }
+    
+    err = AudioDeviceGetProperty( i_dev_id, 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 );
+        return VLC_FALSE;
+    }
+
+    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 streamsformats: [%4.4s]", (char *)&err );
-        return( VLC_EGENERIC );
+        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 )
     {
-        return( VLC_ENOMEM );
+        msg_Err( p_aout, "Could not malloc the memory" );
+        return VLC_FALSE;
     }
     
-    err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
-                                      kAudioDevicePropertyStreamFormats,
-                                      &i_param_size, (void *)p_format_list );
+    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 formats: [%4.4s]", (char *)&err );
-        return( VLC_EGENERIC );
+        msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
+        free( p_format_list);
+        p_format_list = NULL;
+        return VLC_FALSE;
     }
 
     for( i = 0; i < i_formats; i++ )
     {
-        msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
+        msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
         
         if( p_format_list[i].mFormatID == 'IAC3' ||
                   p_format_list[i].mFormatID == kAudioFormat60958AC3 )
         {
-            p_sys->b_supports_digital = VLC_TRUE;
-            msg_Dbg( p_aout, "this device supports a digital stream" );
-            break;
+            b_return = VLC_TRUE;
         }
     }
     
-    free( (void *)p_format_list );
-    return VLC_SUCCESS;
+    if( p_format_list ) free( p_format_list );
+    return b_return;
 }
 
 /*****************************************************************************
@@ -640,9 +1269,9 @@ static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
                                       unsigned int inNumberFrames,
                                       AudioBufferList *ioData )
 {
-    aout_buffer_t * p_buffer;
     AudioTimeStamp  host_time;
     mtime_t         current_date = 0;
+    uint32_t        i_mData_bytes = 0;    
 
     aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
     struct aout_sys_t * p_sys = p_aout->output.p_sys;
@@ -650,14 +1279,14 @@ static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
     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();
 
     current_date = p_sys->clock_diff +
                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
-
-    p_aout->output.i_nb_samples = inNumberFrames;
+                   //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
 
     if( ioData == NULL && ioData->mNumberBuffers < 1 )
     {
@@ -667,48 +1296,101 @@ static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
     if( ioData->mNumberBuffers > 1 )
         msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
 
+
+    if( p_sys->i_total_bytes > 0 )
+    {
+        i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
+        p_aout->p_vlc->pf_memcpy( ioData->mBuffers[0].mData, &p_sys->p_remainder_buffer[p_sys->i_read_bytes], i_mData_bytes );
+        p_sys->i_read_bytes += i_mData_bytes;
+        current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
+                        ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
+        
+        if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
+            p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
+    }
     
-    p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
-    if( p_buffer != NULL )
+    while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
     {
-        p_aout->p_vlc->pf_memcpy(ioData->mBuffers[0].mData, p_buffer->p_buffer, __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize ) );
+        /* We don't have enough data yet */
+        aout_buffer_t * p_buffer;
+        p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
+        
+        if( p_buffer != NULL )
+        {
+            uint32_t i_second_mData_bytes = __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
+            
+            p_aout->p_vlc->pf_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes, p_buffer->p_buffer, i_second_mData_bytes );
+            i_mData_bytes += i_second_mData_bytes;
 
-        if( p_buffer->i_nb_bytes != ioData->mBuffers[0].mDataByteSize )
+            if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
+            {
+                p_sys->i_total_bytes = p_buffer->i_nb_bytes - i_second_mData_bytes;
+                p_aout->p_vlc->pf_memcpy( p_sys->p_remainder_buffer, &p_buffer->p_buffer[i_second_mData_bytes], p_sys->i_total_bytes );
+            }
+            else
+            {
+                // update current_date
+                current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
+                                ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
+            }
+            aout_BufferFree( p_buffer );
+        }
+        else
         {
-            /* FIXME */
-            //msg_Err( p_aout, "byte sizes don't match %d:%d\nframes: %d, nb_samples: %d", p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize, inNumberFrames, p_aout->output.i_nb_samples);
+             p_aout->p_vlc->pf_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes, 0, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
+             i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
         }
-        aout_BufferFree( p_buffer );
-    }
-    else
-    {
-         p_aout->p_vlc->pf_memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
     }
     return( noErr );     
 }
 
-
 /*****************************************************************************
- * Setup a digital stream
+ * RenderCallbackSPDIF: callback for SPDIF audio output
  *****************************************************************************/
-static int DigitalInit( aout_instance_t * p_aout )
+static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
+                                    const AudioTimeStamp * inNow, 
+                                    const void * inInputData,
+                                    const AudioTimeStamp * inInputTime, 
+                                    AudioBufferList * outOutputData,
+                                    const AudioTimeStamp * inOutputTime, 
+                                    void * threadGlobals )
 {
-    OSStatus            err = noErr;
-    UInt32              i, i_param_size;
-    AudioDeviceID       devid_def;
-    AudioDeviceID       *p_devices = NULL;
-    vlc_value_t         val, text;
+    aout_buffer_t * p_buffer;
+    mtime_t         current_date;
 
-    struct aout_sys_t   *p_sys = p_aout->output.p_sys;
+    aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
+    struct aout_sys_t * p_sys = p_aout->output.p_sys;
 
-    
-    
-    return (VLC_SUCCESS);
+    /* 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();
 
-error:
-    return VLC_EGENERIC;
-}
+    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 );     
+}
 
 /*****************************************************************************
  * HardwareListener: Warns us of changes in the list of registered devices
@@ -717,9 +1399,7 @@ static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
                                   void * inClientData )
 {
     OSStatus err = noErr;
-
     aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
-    /* struct aout_sys_t   *p_sys = p_aout->output.p_sys; */
 
     switch( inPropertyID )
     {
@@ -728,9 +1408,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 );
+            var_Destroy( p_aout, "audio-device" );
         }
         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 );
+}
+