Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

auhal.c

00001 /*****************************************************************************
00002  * auhal.c: AUHAL output plugin
00003  *****************************************************************************
00004  * Copyright (C) 2005 the VideoLAN team
00005  * $Id: auhal.c 13671 2005-12-10 19:26:03Z hartman $
00006  *
00007  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  * 
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 #include <string.h>
00028 #include <stdlib.h>
00029 
00030 #include <vlc/vlc.h>
00031 #include <vlc/aout.h>
00032 
00033 #include "aout_internal.h"
00034 
00035 #include <CoreAudio/CoreAudio.h>
00036 #include <CoreAudio/CoreAudioTypes.h>
00037 #include <AudioUnit/AudioUnitProperties.h>
00038 #include <AudioUnit/AudioUnitParameters.h>
00039 #include <AudioUnit/AudioOutputUnit.h>
00040 #include <AudioToolbox/AudioFormat.h>
00041 
00042 #define STREAM_FORMAT_MSG( pre, sfm ) \
00043     pre "[%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
00044     (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
00045     sfm.mFormatFlags, sfm.mBytesPerPacket, \
00046     sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
00047     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
00048 
00049 #define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
00050     pre ":\nsamplerate: [%ld]\nFormatID: [%4.4s]\nFormatFlags: [%ld]\nBypesPerPacket: [%ld]\nFramesPerPacket: [%ld]\nBytesPerFrame: [%ld]\nChannelsPerFrame: [%ld]\nBitsPerChannel[%ld]", \
00051     (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
00052     sfm.mFormatFlags, sfm.mBytesPerPacket, \
00053     sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
00054     sfm.mChannelsPerFrame, sfm.mBitsPerChannel
00055 
00056 #define BUFSIZE 0xffffff
00057 #define AOUT_VAR_SPDIF_FLAG 0xf00000
00058 
00059 /*****************************************************************************
00060  * aout_sys_t: private audio output method descriptor
00061  *****************************************************************************
00062  * This structure is part of the audio output thread descriptor.
00063  * It describes the CoreAudio specific properties of an output thread.
00064  *****************************************************************************/
00065 struct aout_sys_t
00066 {
00067     AudioDeviceID               i_default_dev;  /* Keeps DeviceID of defaultOutputDevice */
00068     AudioDeviceID               i_selected_dev; /* Keeps DeviceID of the selected device */
00069     UInt32                      i_devices;      /* Number of CoreAudio Devices */
00070     vlc_bool_t                  b_supports_digital;/* Does the currently selected device support digital mode? */
00071     vlc_bool_t                  b_digital;      /* Are we running in digital mode? */
00072     mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
00073     /* AUHAL specific */
00074     Component                   au_component;   /* The Audiocomponent we use */
00075     AudioUnit                   au_unit;        /* The AudioUnit we use */
00076     uint8_t                     p_remainder_buffer[BUFSIZE];
00077     uint32_t                    i_read_bytes;
00078     uint32_t                    i_total_bytes;
00079     /* CoreAudio SPDIF mode specific */
00080     pid_t                       i_hog_pid;      /* The keep the pid of our hog status */
00081     AudioStreamID               i_stream_id;    /* The StreamID that has a cac3 streamformat */
00082     int                         i_stream_index; /* The index of i_stream_id in an AudioBufferList */
00083     AudioStreamBasicDescription stream_format;  /* The format we changed the to */
00084     AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
00085     vlc_bool_t                  b_revert;       /* Wether we need to revert the stream format */
00086     vlc_bool_t                  b_changed_mixing;/* Wether we need to set the mixing mode back */
00087 };
00088 
00089 /*****************************************************************************
00090  * Local prototypes.
00091  *****************************************************************************/
00092 static int      Open                    ( vlc_object_t * );
00093 static int      OpenAnalog              ( aout_instance_t * );
00094 static int      OpenSPDIF               ( aout_instance_t * );
00095 static void     Close                   ( vlc_object_t * );
00096 
00097 static void     Play                    ( aout_instance_t * );
00098 static void     Probe                   ( aout_instance_t * );
00099 
00100 static int      AudioDeviceHasOutput    ( AudioDeviceID );
00101 static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
00102 static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
00103 
00104 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
00105                                           unsigned int, unsigned int, AudioBufferList *);
00106 static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
00107                                           AudioBufferList *, const AudioTimeStamp *, void * );
00108 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
00109 static OSStatus StreamListener          ( AudioStreamID, UInt32,
00110                                           AudioDevicePropertyID, void * );
00111 static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
00112                                           vlc_value_t, vlc_value_t, void * );
00113 
00114 
00115 /*****************************************************************************
00116  * Module descriptor
00117  *****************************************************************************/
00118 #define ADEV_TEXT N_("Audio Device")
00119 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
00120     "audio device, as listed in your 'Audio Device' menu. This device will " \
00121     "then be used by default for audio playback.")
00122 
00123 vlc_module_begin();
00124     set_shortname( "auhal" );
00125     set_description( _("HAL AudioUnit output") );
00126     set_capability( "audio output", 101 );
00127     set_category( CAT_AUDIO );
00128     set_subcategory( SUBCAT_AUDIO_AOUT );
00129     set_callbacks( Open, Close );
00130     add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
00131 vlc_module_end();
00132 
00133 /*****************************************************************************
00134  * Open: open macosx audio output
00135  *****************************************************************************/
00136 static int Open( vlc_object_t * p_this )
00137 {
00138     OSStatus                err = noErr;
00139     UInt32                  i_param_size = 0;
00140     struct aout_sys_t       *p_sys = NULL;
00141     vlc_bool_t              b_alive = VLC_FALSE;
00142     vlc_value_t             val;
00143     aout_instance_t         *p_aout = (aout_instance_t *)p_this;
00144 
00145     /* Allocate structure */
00146     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
00147     if( p_aout->output.p_sys == NULL )
00148     {
00149         msg_Err( p_aout, "out of memory" );
00150         return( VLC_ENOMEM );
00151     }
00152 
00153     p_sys = p_aout->output.p_sys;
00154     p_sys->i_default_dev = 0;
00155     p_sys->i_selected_dev = 0;
00156     p_sys->i_devices = 0;
00157     p_sys->b_supports_digital = VLC_FALSE;
00158     p_sys->b_digital = VLC_FALSE;
00159     p_sys->au_component = NULL;
00160     p_sys->au_unit = NULL;
00161     p_sys->clock_diff = (mtime_t) 0;
00162     p_sys->i_read_bytes = 0;
00163     p_sys->i_total_bytes = 0;
00164     p_sys->i_hog_pid = -1;
00165     p_sys->i_stream_id = 0;
00166     p_sys->i_stream_index = 0;
00167     p_sys->b_revert = VLC_FALSE;
00168     p_sys->b_changed_mixing = VLC_FALSE;
00169     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
00170 
00171     p_aout->output.pf_play = Play;
00172     
00173     aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
00174     
00175     /* Persistent device variable */
00176     if( var_Type( p_aout->p_vlc, "macosx-audio-device" ) == 0 )
00177     {
00178         msg_Dbg( p_aout, "create macosx-audio-device" );
00179         var_Create( p_aout->p_vlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00180     }
00181 
00182     /* Build a list of devices */
00183     if( var_Type( p_aout, "audio-device" ) == 0 )
00184     {
00185         Probe( p_aout );
00186     }
00187 
00188     /* What device do we want? */
00189     if( var_Get( p_aout, "audio-device", &val ) < 0 )
00190     {
00191         msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
00192         free( p_sys );
00193         return( VLC_ENOVAR );
00194     }
00195 
00196     p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG;
00197     p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;
00198 
00199     /* Check if the desired device is alive and usable */
00200     i_param_size = sizeof( b_alive );
00201     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
00202                                   kAudioDevicePropertyDeviceIsAlive,
00203                                   &i_param_size, &b_alive );
00204 
00205     if( err != noErr )
00206     {
00207         msg_Err( p_aout, "could not check whether device is alive: %4.4s",
00208                  (char *)&err );
00209         return VLC_EGENERIC;
00210     }
00211 
00212     if( b_alive == VLC_FALSE )
00213     {
00214         msg_Err( p_aout, "Selected audio device is not alive switching to default device" ); 
00215         p_sys->i_selected_dev = p_sys->i_default_dev;
00216     }
00217 
00218     i_param_size = sizeof( p_sys->i_hog_pid );
00219     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
00220                                   kAudioDevicePropertyHogMode,
00221                                   &i_param_size, &p_sys->i_hog_pid );
00222 
00223     if( err != noErr )
00224     {
00225         msg_Err( p_aout, "could not check whether device is hogged: %4.4s",
00226                  (char *)&err );
00227         return VLC_EGENERIC;
00228     }
00229 
00230     if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
00231     {
00232         msg_Err( p_aout, "Selected audio device is exclusively in use by another program" );
00233         var_Destroy( p_aout, "audio-device" );
00234         free( p_sys );
00235         return VLC_EGENERIC;
00236     }
00237 
00238     /* Check for Digital mode or Analog output mode */
00239     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
00240     {
00241         if( OpenSPDIF( p_aout ) )
00242             return VLC_SUCCESS;
00243     }
00244     else
00245     {
00246         if( OpenAnalog( p_aout ) )
00247             return VLC_SUCCESS;
00248     }
00249     
00250     /* If we reach this, the Open* failed */
00251     var_Destroy( p_aout, "audio-device" );
00252     free( p_sys );
00253     return VLC_EGENERIC;
00254 }
00255 
00256 /*****************************************************************************
00257  * Open: open and setup a HAL AudioUnit
00258  *****************************************************************************/
00259 static int OpenAnalog( aout_instance_t *p_aout )
00260 {
00261     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
00262     OSStatus                err = noErr;
00263     UInt32                  i_param_size = 0, i = 0;
00264     ComponentDescription    desc;
00265         
00266     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
00267     {
00268         msg_Dbg( p_aout, "we had requested a digital stream, but it's not possible for this device" );
00269     }
00270 
00271     /* If analog only start setting up AUHAL */
00272     /* Lets go find our Component */
00273     desc.componentType = kAudioUnitType_Output;
00274     desc.componentSubType = kAudioUnitSubType_HALOutput;
00275     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
00276     desc.componentFlags = 0;
00277     desc.componentFlagsMask = 0;
00278 
00279     p_sys->au_component = FindNextComponent( NULL, &desc );
00280     if( p_sys->au_component == NULL )
00281     {
00282         msg_Err( p_aout, "we cannot find our HAL component" );
00283         return VLC_FALSE;
00284     }
00285 
00286     err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
00287     if( err )
00288     {
00289         msg_Err( p_aout, "we cannot find our HAL component" );
00290         return VLC_FALSE;
00291     }
00292     
00293     /* Enable IO for the component */
00294     msg_Dbg( p_aout, "Device: %#x", (int)p_sys->i_selected_dev );
00295     
00296     /* Set the device */
00297     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
00298                          kAudioOutputUnitProperty_CurrentDevice,
00299                          kAudioUnitScope_Global,
00300                          0,
00301                          &p_sys->i_selected_dev,
00302                          sizeof( AudioDeviceID )));
00303                          
00304     /* Get the current format */
00305     AudioStreamBasicDescription DeviceFormat;
00306     
00307     i_param_size = sizeof(AudioStreamBasicDescription);
00308 
00309     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
00310                                    kAudioUnitProperty_StreamFormat,
00311                                    kAudioUnitScope_Input,
00312                                    0,
00313                                    &DeviceFormat,
00314                                    &i_param_size ));
00315                                    
00316     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: " , DeviceFormat ) );
00317 
00318     /* Get the channel layout */
00319     AudioChannelLayout *layout;
00320     verify_noerr( AudioUnitGetPropertyInfo( p_sys->au_unit,
00321                                    kAudioDevicePropertyPreferredChannelLayout,
00322                                    kAudioUnitScope_Output,
00323                                    0,
00324                                    &i_param_size,
00325                                    NULL ));
00326 
00327     layout = (AudioChannelLayout *)malloc( i_param_size);
00328 
00329     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
00330                                    kAudioDevicePropertyPreferredChannelLayout,
00331                                    kAudioUnitScope_Output,
00332                                    0,
00333                                    layout,
00334                                    &i_param_size ));
00335                                    
00336     /* Lets fill out the ChannelLayout */
00337     if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
00338     {
00339         msg_Dbg( p_aout, "bitmap defined channellayout" );
00340         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
00341                                 sizeof( UInt32), &layout->mChannelBitmap,
00342                                 &i_param_size,
00343                                 layout ));
00344     }
00345     else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
00346     {
00347         msg_Dbg( p_aout, "layouttags defined channellayout" );
00348         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
00349                                 sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
00350                                 &i_param_size,
00351                                 layout ));
00352     }
00353 
00354     msg_Dbg( p_aout, "Layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
00355     
00356     p_aout->output.output.i_physical_channels = 0;
00357     for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
00358     {
00359         msg_Dbg( p_aout, "This is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
00360 
00361         switch( layout->mChannelDescriptions[i].mChannelLabel )
00362         {
00363             case kAudioChannelLabel_Left:
00364                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
00365                 continue;
00366             case kAudioChannelLabel_Right:
00367                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
00368                 continue;
00369             case kAudioChannelLabel_Center:
00370                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
00371                 continue;
00372             case kAudioChannelLabel_LFEScreen:
00373                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
00374                 continue;
00375             case kAudioChannelLabel_LeftSurround:
00376                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
00377                 continue;
00378             case kAudioChannelLabel_RightSurround:
00379                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
00380                 continue;
00381             case kAudioChannelLabel_RearSurroundLeft:
00382                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
00383                 continue;
00384             case kAudioChannelLabel_RearSurroundRight:
00385                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
00386                 continue;
00387             case kAudioChannelLabel_CenterSurround:
00388                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
00389                 continue;
00390             default:
00391                 msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
00392                 if( i == 0 )
00393                 {
00394                     msg_Warn( p_aout, "Probably no channellayout is set. force based on channelcount" );
00395                     switch( layout->mNumberChannelDescriptions )
00396                     {
00397                         /* We make assumptions based on number of channels here.
00398                          * Unfortunatly Apple has provided no 100% method to retrieve the speaker configuration */
00399                         case 1:
00400                             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
00401                             break;
00402                         case 4:
00403                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
00404                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00405                             break;
00406                         case 6:
00407                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
00408                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
00409                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00410                             break;
00411                         case 7:
00412                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
00413                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
00414                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER;
00415                             break;
00416                         case 8:
00417                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
00418                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
00419                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
00420                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00421                             break;
00422                         case 2:
00423                         default:
00424                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
00425                     }
00426                 }
00427                 break;
00428         }
00429     }
00430     free( layout );
00431 
00432     msg_Dbg( p_aout, "defined %d physical channels for vlc core", aout_FormatNbChannels( &p_aout->output.output ) );
00433     msg_Dbg( p_aout, "%s", aout_FormatPrintChannels( &p_aout->output.output ));
00434     
00435     AudioChannelLayout new_layout;
00436     memset (&new_layout, 0, sizeof(new_layout));
00437     switch( aout_FormatNbChannels( &p_aout->output.output ) )
00438     {
00439         case 1:
00440             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
00441             break;
00442         case 2:
00443             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
00444             break;
00445         case 3:
00446             if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
00447             {
00448                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
00449             }
00450             else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
00451             {
00452                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
00453             }
00454             break;
00455         case 4:
00456             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
00457             {
00458                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
00459             }
00460             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
00461             {
00462                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
00463             }
00464             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
00465             {
00466                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
00467             }
00468             break;
00469         case 5:
00470             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
00471             {
00472                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
00473             }
00474             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
00475             {
00476                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
00477             }
00478             break;
00479         case 6:
00480             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
00481             break;
00482         case 7:
00483             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
00484             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
00485             break;
00486         case 8:
00487             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
00488             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
00489             break;
00490     }
00491 
00492     /* Set up the format to be used */
00493     DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
00494     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
00495 
00496     /* We use float 32. It's the best supported format by both VLC and Coreaudio */
00497     p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
00498     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
00499     DeviceFormat.mBitsPerChannel = 32;
00500     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
00501     
00502     /* Calculate framesizes and stuff */
00503     aout_FormatPrepare( &p_aout->output.output );
00504     DeviceFormat.mFramesPerPacket = 1;
00505     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
00506     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
00507  
00508     i_param_size = sizeof(AudioStreamBasicDescription);
00509     /* Set desired format (Use CAStreamBasicDescription )*/
00510     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
00511                                    kAudioUnitProperty_StreamFormat,
00512                                    kAudioUnitScope_Input,
00513                                    0,
00514                                    &DeviceFormat,
00515                                    i_param_size ));
00516                                    
00517     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
00518     
00519     /* Retrieve actual format??? */
00520     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
00521                                    kAudioUnitProperty_StreamFormat,
00522                                    kAudioUnitScope_Input,
00523                                    0,
00524                                    &DeviceFormat,
00525                                    &i_param_size ));
00526                                    
00527     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
00528 
00529     p_aout->output.i_nb_samples = 2048;
00530     aout_VolumeSoftInit( p_aout );
00531 
00532     /* Find the difference between device clock and mdate clock */
00533     p_sys->clock_diff = - (mtime_t)
00534         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
00535     p_sys->clock_diff += mdate();
00536 
00537     /* set the IOproc callback */
00538     AURenderCallbackStruct input;
00539     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
00540     input.inputProcRefCon = p_aout;
00541     
00542     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
00543                             kAudioUnitProperty_SetRenderCallback,
00544                             kAudioUnitScope_Input,
00545                             0, &input, sizeof( input ) ) );
00546 
00547     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
00548     input.inputProcRefCon = p_aout;
00549     
00550     /* Set the new_layout as the layout VLC feeds to the AU unit */
00551     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
00552                             kAudioUnitProperty_AudioChannelLayout,
00553                             kAudioUnitScope_Input,
00554                             0, &new_layout, sizeof(new_layout) ) );
00555     
00556     /* AU initiliaze */
00557     verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
00558 
00559     verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
00560     
00561     return VLC_TRUE;
00562 }
00563 
00564 /*****************************************************************************
00565  * Setup a encoded digital stream (SPDIF)
00566  *****************************************************************************/
00567 static int OpenSPDIF( aout_instance_t * p_aout )
00568 {
00569     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
00570     OSStatus                err = noErr;
00571     UInt32                  i_param_size = 0, b_mix = 0;
00572     Boolean                 b_writeable = VLC_FALSE;
00573     AudioStreamID           *p_streams = NULL;
00574     int                     i = 0, i_streams = 0;
00575 
00576     struct timeval now;
00577     struct timespec timeout;
00578     struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
00579 
00580     /* Start doing the SPDIF setup proces */
00581     p_sys->b_digital = VLC_TRUE;
00582     msg_Dbg( p_aout, "opening in SPDIF mode" );
00583 
00584     /* Hog the device */
00585     i_param_size = sizeof( p_sys->i_hog_pid );
00586     p_sys->i_hog_pid = getpid() ;
00587     
00588     err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
00589                                   kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
00590     
00591     if( err != noErr )
00592     {
00593         msg_Err( p_aout, "failed to set hogmode: : [%4.4s]", (char *)&err );
00594         return VLC_FALSE;
00595     }
00596 
00597     /* Set mixable to false if we are allowed to */
00598     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
00599                                     &i_param_size, &b_writeable );
00600 
00601     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
00602                                     &i_param_size, &b_mix );
00603                                     
00604     if( !err && b_writeable )
00605     {
00606         b_mix = 0;
00607         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
00608                             kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
00609         p_sys->b_changed_mixing = VLC_TRUE;
00610     }
00611     
00612     if( err != noErr )
00613     {
00614         msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
00615         return VLC_FALSE;
00616     }
00617 
00618     // Find stream_id of selected device with a cac3 stream
00619     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
00620                                       kAudioDevicePropertyStreams,
00621                                       &i_param_size, NULL );
00622     if( err != noErr )
00623     {
00624         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
00625         return VLC_FALSE;
00626     }
00627     
00628     i_streams = i_param_size / sizeof( AudioStreamID );
00629     p_streams = (AudioStreamID *)malloc( i_param_size );
00630     if( p_streams == NULL )
00631     {
00632         msg_Err( p_aout, "Out of memory" );
00633         return VLC_FALSE;
00634     }
00635     
00636     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
00637                                     kAudioDevicePropertyStreams,
00638                                     &i_param_size, p_streams );
00639     
00640     if( err != noErr )
00641     {
00642         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
00643         if( p_streams ) free( p_streams );
00644         return VLC_FALSE;
00645     }
00646 
00647     for( i = 0; i < i_streams; i++ )
00648     {
00649         // Find a stream with a cac3 stream
00650         AudioStreamBasicDescription *p_format_list = NULL;
00651         int                         i_formats = 0, j = 0;
00652         
00653         /* Retrieve all the stream formats supported by each output stream */
00654         err = AudioStreamGetPropertyInfo( p_streams[i], 0,
00655                                           kAudioStreamPropertyPhysicalFormats,
00656                                           &i_param_size, NULL );
00657         if( err != noErr )
00658         {
00659             msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
00660             continue;
00661         }
00662         
00663         i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
00664         p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
00665         if( p_format_list == NULL )
00666         {
00667             msg_Err( p_aout, "Could not malloc the memory" );
00668             continue;
00669         }
00670         
00671         err = AudioStreamGetProperty( p_streams[i], 0,
00672                                           kAudioStreamPropertyPhysicalFormats,
00673                                           &i_param_size, p_format_list );
00674         if( err != noErr )
00675         {
00676             msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
00677             if( p_format_list) free( p_format_list);
00678             continue;
00679         }
00680 
00681         for( j = 0; j < i_formats; j++ )
00682         {
00683             if( p_format_list[j].mFormatID == 'IAC3' ||
00684                   p_format_list[j].mFormatID == kAudioFormat60958AC3 )
00685             {
00686                 // found a cac3 format
00687                 p_sys->i_stream_id = p_streams[i];
00688                 p_sys->i_stream_index = i;
00689 
00690                 if( p_sys->b_revert == VLC_FALSE )
00691                 {
00692                     i_param_size = sizeof( p_sys->sfmt_revert );
00693                     err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
00694                                                   kAudioStreamPropertyPhysicalFormat,
00695                                                   &i_param_size, 
00696                                                   &p_sys->sfmt_revert );
00697                     if( err != noErr )
00698                     {
00699                         msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
00700                         continue; 
00701                     }
00702                     p_sys->b_revert = VLC_TRUE;
00703                 }
00704                 if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
00705                 {
00706                     p_sys->stream_format = p_format_list[j];
00707                 }
00708             }
00709         }
00710         if( p_format_list ) free( p_format_list );
00711     }
00712     
00713     if( p_streams ) free( p_streams );
00714 
00715     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );
00716     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->stream_format ) );
00717 
00718     /* Install the callback */
00719     err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
00720                                       kAudioStreamPropertyPhysicalFormat,
00721                                       StreamListener, (void *)&w );
00722     if( err != noErr )
00723     {
00724         msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
00725         return VLC_FALSE;
00726     }
00727 
00728     /* Condition because SetProperty is asynchronious */ 
00729     vlc_cond_init( p_aout, &w.cond );
00730     vlc_mutex_init( p_aout, &w.lock );
00731     vlc_mutex_lock( &w.lock );
00732     
00733     /* change the format */
00734     err = AudioStreamSetProperty( p_sys->i_stream_id, 0, 0,
00735                                   kAudioStreamPropertyPhysicalFormat,
00736                                   sizeof( AudioStreamBasicDescription ),
00737                                   &p_sys->stream_format ); 
00738     if( err != noErr )
00739     {
00740         msg_Err( p_aout, "could not set the stream format: [%4.4s]", (char *)&err );
00741         vlc_mutex_unlock( &w.lock );
00742         vlc_mutex_destroy( &w.lock );
00743         vlc_cond_destroy( &w.cond );
00744         return VLC_FALSE;
00745     }
00746 
00747     gettimeofday( &now, NULL );
00748     timeout.tv_sec = now.tv_sec;
00749     timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
00750 
00751     pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
00752     vlc_mutex_unlock( &w.lock );
00753 
00754     err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
00755                                         kAudioStreamPropertyPhysicalFormat,
00756                                         StreamListener );
00757     if( err != noErr )
00758     {
00759         msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
00760         vlc_mutex_destroy( &w.lock );
00761         vlc_cond_destroy( &w.cond );
00762         return VLC_FALSE;
00763     }
00764 
00765     vlc_mutex_destroy( &w.lock );
00766     vlc_cond_destroy( &w.cond );
00767     
00768     i_param_size = sizeof( AudioStreamBasicDescription );
00769     err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
00770                                   kAudioStreamPropertyPhysicalFormat,
00771                                   &i_param_size, 
00772                                   &p_sys->stream_format );
00773 
00774     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
00775 
00776     /* set the format flags */
00777     if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
00778         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
00779     else
00780         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
00781     p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
00782     p_aout->output.output.i_frame_length = A52_FRAME_NB;
00783     p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
00784     p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
00785 
00786     aout_VolumeNoneInit( p_aout );
00787 
00788     /* Add IOProc callback */
00789     err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
00790                                 (AudioDeviceIOProc)RenderCallbackSPDIF,
00791                                 (void *)p_aout );
00792     if( err != noErr )
00793     {
00794         msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
00795         return VLC_FALSE;
00796     }
00797 
00798     /* Check for the difference between the Device clock and mdate */
00799     p_sys->clock_diff = - (mtime_t)
00800         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
00801     p_sys->clock_diff += mdate();
00802  
00803     /* Start device */
00804     err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF ); 
00805     if( err != noErr )
00806     {
00807         msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );
00808 
00809         err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev, 
00810                                        (AudioDeviceIOProc)RenderCallbackSPDIF );
00811         if( err != noErr )
00812         {
00813             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
00814         }
00815         return VLC_FALSE;
00816     }
00817 
00818     return VLC_TRUE;
00819 }
00820 
00821 
00822 /*****************************************************************************
00823  * Close: Close HAL AudioUnit
00824  *****************************************************************************/
00825 static void Close( vlc_object_t * p_this )
00826 {
00827     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
00828     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
00829     OSStatus            err = noErr;
00830     UInt32              i_param_size = 0;
00831     
00832     if( p_sys->au_unit )
00833     {
00834         verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
00835         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
00836         verify_noerr( CloseComponent( p_sys->au_unit ) );
00837     }
00838     
00839     if( p_sys->b_digital )
00840     {
00841         /* Stop device */
00842         err = AudioDeviceStop( p_sys->i_selected_dev, 
00843                                (AudioDeviceIOProc)RenderCallbackSPDIF ); 
00844         if( err != noErr )
00845         {
00846             msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
00847         }
00848 
00849         /* Remove callback */
00850         err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
00851                                        (AudioDeviceIOProc)RenderCallbackSPDIF );
00852         if( err != noErr )
00853         {
00854             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
00855         }
00856         
00857         if( p_sys->b_revert )
00858         {
00859             struct timeval now;
00860             struct timespec timeout;
00861             struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
00862 
00863             /* Install the callback */
00864             err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
00865                                               kAudioStreamPropertyPhysicalFormat,
00866                                               StreamListener, (void *)&w );
00867             if( err != noErr )
00868             {
00869                 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
00870             }
00871 
00872             /* Condition because SetProperty is asynchronious */ 
00873             vlc_cond_init( p_aout, &w.cond );
00874             vlc_mutex_init( p_aout, &w.lock );
00875             vlc_mutex_lock( &w.lock );
00876 
00877             msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->sfmt_revert ) );
00878 
00879             err = AudioStreamSetProperty( p_sys->i_stream_id, NULL, 0,
00880                                             kAudioStreamPropertyPhysicalFormat,
00881                                             sizeof( AudioStreamBasicDescription ),
00882                                             &p_sys->sfmt_revert );
00883                                             
00884             if( err != noErr )
00885             {
00886                 msg_Err( p_aout, "Streamformat reverse failed: [%4.4s]", (char *)&err );
00887             }
00888             
00889             gettimeofday( &now, NULL );
00890             timeout.tv_sec = now.tv_sec;
00891             timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
00892 
00893             pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
00894             vlc_mutex_unlock( &w.lock );
00895 
00896             err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
00897                                                 kAudioStreamPropertyPhysicalFormat,
00898                                                 StreamListener );
00899             if( err != noErr )
00900             {
00901                 msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
00902             }
00903 
00904             vlc_mutex_destroy( &w.lock );
00905             vlc_cond_destroy( &w.cond );
00906             
00907             i_param_size = sizeof( AudioStreamBasicDescription );
00908             err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
00909                                           kAudioStreamPropertyPhysicalFormat,
00910                                           &i_param_size, 
00911                                           &p_sys->stream_format );
00912 
00913             msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
00914         }
00915         if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
00916         {
00917             int b_mix;
00918             Boolean b_writeable;
00919             /* Revert mixable to true if we are allowed to */
00920             err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
00921                                         &i_param_size, &b_writeable );
00922 
00923             err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
00924                                         &i_param_size, &b_mix );
00925                                         
00926             if( !err && b_writeable )
00927             {
00928                 msg_Dbg( p_aout, "mixable is: %d", b_mix );
00929                 b_mix = 1;
00930                 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
00931                                     kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
00932             }
00933 
00934             if( err != noErr )
00935             {
00936                 msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
00937             }
00938         }
00939     }
00940 
00941     err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
00942                                                HardwareListener );
00943                                                
00944     if( err != noErr )
00945     {
00946         msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
00947     }
00948     
00949     if( p_sys->i_hog_pid == getpid() )
00950     {
00951         p_sys->i_hog_pid = -1;
00952         i_param_size = sizeof( p_sys->i_hog_pid );
00953         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
00954                                          kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
00955         if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
00956     }
00957     
00958     if( p_sys ) free( p_sys );
00959 }
00960 
00961 /*****************************************************************************
00962  * Play: nothing to do
00963  *****************************************************************************/
00964 static void Play( aout_instance_t * p_aout )
00965 {
00966 }
00967 
00968 
00969 /*****************************************************************************
00970  * Probe
00971  *****************************************************************************/
00972 static void Probe( aout_instance_t * p_aout )
00973 {
00974     OSStatus            err = noErr;
00975     UInt32              i = 0, i_param_size = 0;
00976     AudioDeviceID       devid_def = 0;
00977     AudioDeviceID       *p_devices = NULL;
00978     vlc_value_t         val, text;
00979 
00980     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
00981 
00982     /* Get number of devices */
00983     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
00984                                         &i_param_size, NULL );
00985     if( err != noErr )
00986     {
00987         msg_Err( p_aout, "could not get number of devices: [%4.4s]", (char *)&err );
00988         goto error;
00989     }
00990 
00991     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
00992 
00993     if( p_sys->i_devices < 1 )
00994     {
00995         msg_Err( p_aout, "no devices found" );
00996         goto error;
00997     }
00998 
00999     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
01000 
01001     /* Allocate DeviceID array */
01002     p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
01003     if( p_devices == NULL )
01004     {
01005         msg_Err( p_aout, "out of memory" );
01006         goto error;
01007     }
01008 
01009     /* Populate DeviceID array */
01010     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
01011                                     &i_param_size, p_devices );
01012     if( err != noErr )
01013     {
01014         msg_Err( p_aout, "could not get the device ID's: [%4.4s]", (char *)&err );
01015         goto error;
01016     }
01017 
01018     /* Find the ID of the default Device */
01019     i_param_size = sizeof( AudioDeviceID );
01020     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
01021                                     &i_param_size, &devid_def );
01022     if( err != noErr )
01023     {
01024         msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
01025         goto error;
01026     }
01027     p_sys->i_default_dev = devid_def;
01028     
01029     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
01030     text.psz_string = _("Audio Device");
01031     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
01032     
01033     for( i = 0; i < p_sys->i_devices; i++ )
01034     {
01035         char *psz_name;
01036         i_param_size = 0;
01037 
01038         /* Retrieve the length of the device name */
01039         err = AudioDeviceGetPropertyInfo(
01040                     p_devices[i], 0, VLC_FALSE,
01041                     kAudioDevicePropertyDeviceName,
01042                     &i_param_size, NULL);
01043         if( err ) goto error;
01044 
01045         /* Retrieve the name of the device */
01046         psz_name = (char *)malloc( i_param_size );
01047         err = AudioDeviceGetProperty(
01048                     p_devices[i], 0, VLC_FALSE,
01049                     kAudioDevicePropertyDeviceName,
01050                     &i_param_size, psz_name);
01051         if( err ) goto error;
01052 
01053         msg_Dbg( p_aout, "DevID: %lu  DevName: %s", p_devices[i], psz_name );
01054 
01055         if( !AudioDeviceHasOutput( p_devices[i]) )
01056         {
01057             msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
01058             continue;
01059         }
01060 
01061         val.i_int = (int)p_devices[i];
01062         text.psz_string = strdup( psz_name );
01063         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
01064         if( p_sys->i_default_dev == p_devices[i] )
01065         {
01066             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
01067             var_Set( p_aout, "audio-device", val );
01068         }
01069 
01070         if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
01071         {
01072             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
01073             asprintf( &text.psz_string, "%s (Encoded Output)", psz_name );
01074             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
01075             if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
01076             {
01077                 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
01078                 var_Set( p_aout, "audio-device", val );
01079             }
01080         }
01081         
01082         free( psz_name);
01083     }
01084     
01085     var_Get( p_aout->p_vlc, "macosx-audio-device", &val );
01086     msg_Dbg( p_aout, "device value override1: %#x", val.i_int );
01087     if( val.i_int > 0 )
01088     {
01089         msg_Dbg( p_aout, "device value override2: %#x", val.i_int );
01090         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
01091         var_Set( p_aout, "audio-device", val );
01092     }
01093     
01094     var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );
01095 
01096     /* attach a Listener so that we are notified of a change in the Device setup */
01097     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
01098                                             HardwareListener, 
01099                                             (void *)p_aout );
01100     if( err )
01101         goto error;
01102 
01103     msg_Dbg( p_aout, "succesful finish of deviceslist" );
01104     if( p_devices ) free( p_devices );
01105     return;
01106 
01107 error:
01108     var_Destroy( p_aout, "audio-device" );
01109     if( p_devices ) free( p_devices );
01110     return;
01111 }
01112 
01113 /*****************************************************************************
01114  * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
01115  *****************************************************************************/
01116 static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
01117 {
01118     UInt32                      dataSize;
01119     Boolean                     isWritable;
01120         
01121     verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
01122     if (dataSize == 0) return FALSE;
01123     
01124     return TRUE;
01125 }
01126 
01127 /*****************************************************************************
01128  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
01129  *****************************************************************************/
01130 static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
01131 {
01132     OSStatus                    err = noErr;
01133     UInt32                      i_param_size = 0;
01134     AudioStreamID               *p_streams = NULL;
01135     int                         i = 0, i_streams = 0;
01136     vlc_bool_t                  b_return = VLC_FALSE;
01137     
01138     /* Retrieve all the output streams */
01139     err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
01140                                       kAudioDevicePropertyStreams,
01141                                       &i_param_size, NULL );
01142     if( err != noErr )
01143     {
01144         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
01145         return VLC_FALSE;
01146     }
01147     
01148     i_streams = i_param_size / sizeof( AudioStreamID );
01149     p_streams = (AudioStreamID *)malloc( i_param_size );
01150     if( p_streams == NULL )
01151     {
01152         msg_Err( p_aout, "Out of memory" );
01153         return VLC_ENOMEM;
01154     }
01155     
01156     err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
01157                                     kAudioDevicePropertyStreams,
01158                                     &i_param_size, p_streams );
01159     
01160     if( err != noErr )
01161     {
01162         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
01163         return VLC_FALSE;
01164     }
01165 
01166     for( i = 0; i < i_streams; i++ )
01167     {
01168         if( AudioStreamSupportsDigital( p_aout, p_streams[i] ) )
01169             b_return = VLC_TRUE;
01170     }
01171     
01172     if( p_streams ) free( p_streams );
01173     return b_return;
01174 }
01175 
01176 /*****************************************************************************
01177  * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
01178  *****************************************************************************/
01179 static int AudioStreamSupportsDigital( aout_instance_t *p_aout, AudioStreamID i_stream_id )
01180 {
01181     OSStatus                    err = noErr;
01182     UInt32                      i_param_size = 0;
01183     AudioStreamBasicDescription *p_format_list = NULL;
01184     int                         i = 0, i_formats = 0;
01185     vlc_bool_t                  b_return = VLC_FALSE;
01186     
01187     /* Retrieve all the stream formats supported by each output stream */
01188     err = AudioStreamGetPropertyInfo( i_stream_id, 0,
01189                                       kAudioStreamPropertyPhysicalFormats,
01190                                       &i_param_size, NULL );
01191     if( err != noErr )
01192     {
01193         msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
01194         return VLC_FALSE;
01195     }
01196     
01197     i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
01198     msg_Dbg( p_aout, "number of formats: %d", i_formats );
01199     p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
01200     if( p_format_list == NULL )
01201     {
01202         msg_Err( p_aout, "Could not malloc the memory" );
01203         return VLC_FALSE;
01204     }
01205     
01206     err = AudioStreamGetProperty( i_stream_id, 0,
01207                                       kAudioStreamPropertyPhysicalFormats,
01208                                       &i_param_size, p_format_list );
01209     if( err != noErr )
01210     {
01211         msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
01212         free( p_format_list);
01213         p_format_list = NULL;
01214         return VLC_FALSE;
01215     }
01216 
01217     for( i = 0; i < i_formats; i++ )
01218     {
01219         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
01220         
01221         if( p_format_list[i].mFormatID == 'IAC3' ||
01222                   p_format_list[i].mFormatID == kAudioFormat60958AC3 )
01223         {
01224             b_return = VLC_TRUE;
01225         }
01226     }
01227     
01228     if( p_format_list ) free( p_format_list );
01229     return b_return;
01230 }
01231 
01232 /*****************************************************************************
01233  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
01234  * us to provide some more audio data.
01235  * Don't print anything during normal playback, calling blocking function from
01236  * this callback is not allowed.
01237  *****************************************************************************/
01238 static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
01239                                       AudioUnitRenderActionFlags *ioActionFlags,
01240                                       const AudioTimeStamp *inTimeStamp,
01241                                       unsigned int inBusNummer,
01242                                       unsigned int inNumberFrames,
01243                                       AudioBufferList *ioData )
01244 {
01245     AudioTimeStamp  host_time;
01246     mtime_t         current_date = 0;
01247     uint32_t        i_mData_bytes = 0;    
01248 
01249     aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
01250     struct aout_sys_t * p_sys = p_aout->output.p_sys;
01251 
01252     host_time.mFlags = kAudioTimeStampHostTimeValid;
01253     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
01254 
01255     /* Check for the difference between the Device clock and mdate */
01256     p_sys->clock_diff = - (mtime_t)
01257         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
01258     p_sys->clock_diff += mdate();
01259 
01260     current_date = p_sys->clock_diff +
01261                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
01262                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
01263 
01264     if( ioData == NULL && ioData->mNumberBuffers < 1 )
01265     {
01266         msg_Err( p_aout, "no iodata or buffers");
01267         return 0;
01268     }
01269     if( ioData->mNumberBuffers > 1 )
01270         msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
01271 
01272 
01273     if( p_sys->i_total_bytes > 0 )
01274     {
01275         i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
01276         p_aout->p_vlc->pf_memcpy( ioData->mBuffers[0].mData, &p_sys->p_remainder_buffer[p_sys->i_read_bytes], i_mData_bytes );
01277         p_sys->i_read_bytes += i_mData_bytes;
01278         current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
01279                         ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
01280         
01281         if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
01282             p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
01283     }
01284     
01285     while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
01286     {
01287         /* We don't have enough data yet */
01288         aout_buffer_t * p_buffer;
01289         p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
01290         
01291         if( p_buffer != NULL )
01292         {
01293             uint32_t i_second_mData_bytes = __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
01294             
01295             p_aout->p_vlc->pf_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes, p_buffer->p_buffer, i_second_mData_bytes );
01296             i_mData_bytes += i_second_mData_bytes;
01297 
01298             if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
01299             {
01300                 p_sys->i_total_bytes = p_buffer->i_nb_bytes - i_second_mData_bytes;
01301                 p_aout->p_vlc->pf_memcpy( p_sys->p_remainder_buffer, &p_buffer->p_buffer[i_second_mData_bytes], p_sys->i_total_bytes );
01302             }
01303             else
01304             {
01305                 // update current_date
01306                 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
01307                                 ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
01308             }
01309             aout_BufferFree( p_buffer );
01310         }
01311         else
01312         {
01313              p_aout->p_vlc->pf_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes, 0, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
01314              i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
01315         }
01316     }
01317     return( noErr );     
01318 }
01319 
01320 /*****************************************************************************
01321  * RenderCallbackSPDIF: callback for SPDIF audio output
01322  *****************************************************************************/
01323 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
01324                                     const AudioTimeStamp * inNow, 
01325                                     const void * inInputData,
01326                                     const AudioTimeStamp * inInputTime, 
01327                                     AudioBufferList * outOutputData,
01328                                     const AudioTimeStamp * inOutputTime, 
01329                                     void * threadGlobals )
01330 {
01331     aout_buffer_t * p_buffer;
01332     mtime_t         current_date;
01333 
01334     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
01335     struct aout_sys_t * p_sys = p_aout->output.p_sys;
01336 
01337     /* Check for the difference between the Device clock and mdate */
01338     p_sys->clock_diff = - (mtime_t)
01339         AudioConvertHostTimeToNanos( inNow->mHostTime ) / 1000; 
01340     p_sys->clock_diff += mdate();
01341 
01342     current_date = p_sys->clock_diff +
01343                    AudioConvertHostTimeToNanos( inOutputTime->mHostTime ) / 1000;
01344                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
01345 
01346     p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_TRUE );
01347 
01348 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
01349     if( p_buffer != NULL )
01350     {
01351         if( (int)BUFFER.mDataByteSize != (int)p_buffer->i_nb_bytes)
01352             msg_Warn( p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_nb_bytes );
01353         
01354         /* move data into output data buffer */
01355         p_aout->p_vlc->pf_memcpy( BUFFER.mData,
01356                                   p_buffer->p_buffer, p_buffer->i_nb_bytes );
01357         aout_BufferFree( p_buffer );
01358     }
01359     else
01360     {
01361         p_aout->p_vlc->pf_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
01362     }
01363 #undef BUFFER
01364 
01365     return( noErr );     
01366 }
01367 
01368 /*****************************************************************************
01369  * HardwareListener: Warns us of changes in the list of registered devices
01370  *****************************************************************************/
01371 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
01372                                   void * inClientData )
01373 {
01374     OSStatus err = noErr;
01375     aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
01376 
01377     switch( inPropertyID )
01378     {
01379         case kAudioHardwarePropertyDevices:
01380         {
01381             /* something changed in the list of devices */
01382             /* We trigger the audio-device's aout_ChannelsRestart callback */
01383             var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
01384             var_Destroy( p_aout, "audio-device" );
01385         }
01386         break;
01387     }
01388 
01389     return( err );
01390 }
01391 
01392 /*****************************************************************************
01393  * StreamListener 
01394  *****************************************************************************/
01395 static OSStatus StreamListener( AudioStreamID inStream,
01396                                 UInt32 inChannel,
01397                                 AudioDevicePropertyID inPropertyID,
01398                                 void * inClientData )
01399 {
01400     OSStatus err = noErr;
01401     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
01402 
01403     switch( inPropertyID )
01404     {
01405         case kAudioStreamPropertyPhysicalFormat:
01406             vlc_mutex_lock( &w->lock );
01407             vlc_cond_signal( &w->cond );
01408             vlc_mutex_unlock( &w->lock ); 
01409             break;
01410 
01411         default:
01412             break;
01413     }
01414     return( err );
01415 }
01416 
01417 /*****************************************************************************
01418  * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
01419  *****************************************************************************/
01420 static int AudioDeviceCallback( vlc_object_t *p_this, const char *psz_variable,
01421                      vlc_value_t old_val, vlc_value_t new_val, void *param )
01422 {
01423     aout_instance_t *p_aout = (aout_instance_t *)p_this;
01424     var_Set( p_aout->p_vlc, "macosx-audio-device", new_val );
01425     msg_Dbg( p_aout, "Set Device: %#x", new_val.i_int );
01426     return aout_ChannelsRestart( p_this, psz_variable, old_val, new_val, param );
01427 }
01428 

Generated on Tue Dec 20 10:14:28 2005 for vlc-0.8.4a by  doxygen 1.4.2