auhal.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:59k
源码类别:

midi

开发平台:

Unix_Linux

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