auhal.c 58.9 KB
Newer Older
1
/*****************************************************************************
hartman's avatar
hartman committed
2
 * auhal.c: AUHAL and Coreaudio output plugin
3
 *****************************************************************************
4
 * Copyright (C) 2005 the VideoLAN team
hartman's avatar
hartman committed
5
 * $Id$
6
7
8
9
10
11
12
 *
 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27
#include <unistd.h>
28
29

#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
30
31
#include <vlc_interface.h>
#include <vlc_aout.h>
32
33
34
35
36
37
38
39

#include <CoreAudio/CoreAudio.h>
#include <AudioUnit/AudioUnitProperties.h>
#include <AudioUnit/AudioUnitParameters.h>
#include <AudioUnit/AudioOutputUnit.h>
#include <AudioToolbox/AudioFormat.h>

#define STREAM_FORMAT_MSG( pre, sfm ) \
40
41
42
43
44
45
46
    pre "[%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
    (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
    sfm.mFormatFlags, sfm.mBytesPerPacket, \
    sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
    sfm.mChannelsPerFrame, sfm.mBitsPerChannel

#define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
47
48
49
50
51
52
    pre ":\nsamplerate: [%ld]\nFormatID: [%4.4s]\nFormatFlags: [%ld]\nBypesPerPacket: [%ld]\nFramesPerPacket: [%ld]\nBytesPerFrame: [%ld]\nChannelsPerFrame: [%ld]\nBitsPerChannel[%ld]", \
    (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
    sfm.mFormatFlags, sfm.mBytesPerPacket, \
    sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
    sfm.mChannelsPerFrame, sfm.mBitsPerChannel

53
#define BUFSIZE 0xffffff
54
#define AOUT_VAR_SPDIF_FLAG 0xf00000
55

56
57
58
59
60
/*
 * TODO:
 * - clean up the debug info
 * - clean up C99'isms
 * - be better at changing stream setup or devices setup changes while playing.
hartman's avatar
hartman committed
61
 * - fix 6.1 and 7.1
62
63
 */

64
65
66
67
68
69
70
71
72
73
74
75
76
/*****************************************************************************
 * aout_sys_t: private audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the CoreAudio specific properties of an output thread.
 *****************************************************************************/
struct aout_sys_t
{
    AudioDeviceID               i_default_dev;  /* Keeps DeviceID of defaultOutputDevice */
    AudioDeviceID               i_selected_dev; /* Keeps DeviceID of the selected device */
    UInt32                      i_devices;      /* Number of CoreAudio Devices */
    vlc_bool_t                  b_supports_digital;/* Does the currently selected device support digital mode? */
    vlc_bool_t                  b_digital;      /* Are we running in digital mode? */
77
    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
hartman's avatar
hartman committed
78

79
    /* AUHAL specific */
80
81
    Component                   au_component;   /* The Audiocomponent we use */
    AudioUnit                   au_unit;        /* The AudioUnit we use */
82
    uint8_t                     p_remainder_buffer[BUFSIZE];
83
84
    uint32_t                    i_read_bytes;
    uint32_t                    i_total_bytes;
hartman's avatar
hartman committed
85

86
87
88
89
    /* CoreAudio SPDIF mode specific */
    pid_t                       i_hog_pid;      /* The keep the pid of our hog status */
    AudioStreamID               i_stream_id;    /* The StreamID that has a cac3 streamformat */
    int                         i_stream_index; /* The index of i_stream_id in an AudioBufferList */
hartman's avatar
hartman committed
90
    AudioStreamBasicDescription stream_format;  /* The format we changed the stream to */
91
92
93
    AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
    vlc_bool_t                  b_revert;       /* Wether we need to revert the stream format */
    vlc_bool_t                  b_changed_mixing;/* Wether we need to set the mixing mode back */
94
95
96
97
98
99
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int      Open                    ( vlc_object_t * );
100
101
static int      OpenAnalog              ( aout_instance_t * );
static int      OpenSPDIF               ( aout_instance_t * );
102
103
static void     Close                   ( vlc_object_t * );

104
105
static void     Play                    ( aout_instance_t * );
static void     Probe                   ( aout_instance_t * );
106

107
108
109
static int      AudioDeviceHasOutput    ( AudioDeviceID );
static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
hartman's avatar
hartman committed
110
static int      AudioStreamChangeFormat ( aout_instance_t *, AudioStreamID, AudioStreamBasicDescription );
111
112
113

static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                          unsigned int, unsigned int, AudioBufferList *);
114
115
static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
                                          AudioBufferList *, const AudioTimeStamp *, void * );
116
static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
117
118
119
120
121
static OSStatus StreamListener          ( AudioStreamID, UInt32,
                                          AudioDevicePropertyID, void * );
static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
                                          vlc_value_t, vlc_value_t, void * );

122
123
124
125
126
127
128
129
130
131
132
133

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define ADEV_TEXT N_("Audio Device")
#define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
    "audio device, as listed in your 'Audio Device' menu. This device will " \
    "then be used by default for audio playback.")

vlc_module_begin();
    set_shortname( "auhal" );
    set_description( _("HAL AudioUnit output") );
134
    set_capability( "audio output", 101 );
135
136
137
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
    set_callbacks( Open, Close );
138
    add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE );
139
140
141
vlc_module_end();

/*****************************************************************************
142
 * Open: open macosx audio output
143
144
145
146
 *****************************************************************************/
static int Open( vlc_object_t * p_this )
{
    OSStatus                err = noErr;
147
    UInt32                  i_param_size = 0;
148
149
    struct aout_sys_t       *p_sys = NULL;
    vlc_bool_t              b_alive = VLC_FALSE;
150
151
152
153
    vlc_value_t             val;
    aout_instance_t         *p_aout = (aout_instance_t *)p_this;

    /* Allocate structure */
154
155
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
156
157
158
159
160
    {
        msg_Err( p_aout, "out of memory" );
        return( VLC_ENOMEM );
    }

161
162
163
164
165
166
167
168
169
170
171
    p_sys = p_aout->output.p_sys;
    p_sys->i_default_dev = 0;
    p_sys->i_selected_dev = 0;
    p_sys->i_devices = 0;
    p_sys->b_supports_digital = VLC_FALSE;
    p_sys->b_digital = VLC_FALSE;
    p_sys->au_component = NULL;
    p_sys->au_unit = NULL;
    p_sys->clock_diff = (mtime_t) 0;
    p_sys->i_read_bytes = 0;
    p_sys->i_total_bytes = 0;
172
173
    p_sys->i_hog_pid = -1;
    p_sys->i_stream_id = 0;
174
    p_sys->i_stream_index = -1;
175
176
    p_sys->b_revert = VLC_FALSE;
    p_sys->b_changed_mixing = VLC_FALSE;
177
    memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
178
179

    p_aout->output.pf_play = Play;
180
 
181
    aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
182
 
183
    /* Persistent device variable */
184
    if( var_Type( p_aout->p_libvlc, "macosx-audio-device" ) == 0 )
185
    {
186
        var_Create( p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
187
188
    }

189
190
191
    /* Build a list of devices */
    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
192
        Probe( p_aout );
193
    }
194

195
196
197
    /* What device do we want? */
    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
198
        msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
hartman's avatar
hartman committed
199
        goto error;
200
201
    }

hartman's avatar
hartman committed
202
    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
203
204
205
    p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;

    /* Check if the desired device is alive and usable */
hartman's avatar
hartman committed
206
    /* TODO: add a callback to the device to alert us if the device dies */
207
208
209
210
211
212
213
    i_param_size = sizeof( b_alive );
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                  kAudioDevicePropertyDeviceIsAlive,
                                  &i_param_size, &b_alive );

    if( err != noErr )
    {
hartman's avatar
hartman committed
214
215
        msg_Err( p_aout, "could not check whether device is alive: %4.4s", (char *)&err );
        goto error;
216
217
218
    }

    if( b_alive == VLC_FALSE )
219
    {
220
        msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
221
        p_sys->i_selected_dev = p_sys->i_default_dev;
222
    }
223

224
    i_param_size = sizeof( p_sys->i_hog_pid );
225
226
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                  kAudioDevicePropertyHogMode,
227
                                  &i_param_size, &p_sys->i_hog_pid );
228
229
230

    if( err != noErr )
    {
231
        /* This is not a fatal error. Some drivers simply don't support this property */
hartman's avatar
hartman committed
232
        msg_Warn( p_aout, "could not check whether device is hogged: %4.4s",
233
                 (char *)&err );
234
        p_sys->i_hog_pid = -1;
235
236
    }

237
    if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
238
    {
hartman's avatar
hartman committed
239
        msg_Err( p_aout, "Selected audio device is exclusively in use by another program." );
240
        intf_UserFatal( p_aout, VLC_FALSE, _("Audio output failed"),
241
242
                        _("The selected audio output device is exclusively in "
                          "use by another program.") );
hartman's avatar
hartman committed
243
        goto error;
244
245
246
    }

    /* Check for Digital mode or Analog output mode */
247
248
    if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
    {
249
250
251
252
253
254
255
        if( OpenSPDIF( p_aout ) )
            return VLC_SUCCESS;
    }
    else
    {
        if( OpenAnalog( p_aout ) )
            return VLC_SUCCESS;
256
    }
hartman's avatar
hartman committed
257
258
259

error:
    /* If we reach this, this aout has failed */
260
    var_Destroy( p_aout, "audio-device" );
hartman's avatar
hartman committed
261
    if( p_sys ) free( p_sys );
262
263
264
    return VLC_EGENERIC;
}

265
/*****************************************************************************
hartman's avatar
hartman committed
266
 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
267
268
 *****************************************************************************/
static int OpenAnalog( aout_instance_t *p_aout )
269
{
hartman's avatar
hartman committed
270
271
272
    struct aout_sys_t           *p_sys = p_aout->output.p_sys;
    OSStatus                    err = noErr;
    UInt32                      i_param_size = 0, i = 0;
273
    int                         i_original;
hartman's avatar
hartman committed
274
275
276
277
278
    ComponentDescription        desc;
    AudioStreamBasicDescription DeviceFormat;
    AudioChannelLayout          *layout;
    AudioChannelLayout          new_layout;
    AURenderCallbackStruct      input;
279
280
281
282
283
284
285
286
287
288
289

    /* Lets go find our Component */
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_HALOutput;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    p_sys->au_component = FindNextComponent( NULL, &desc );
    if( p_sys->au_component == NULL )
    {
hartman's avatar
hartman committed
290
        msg_Warn( p_aout, "we cannot find our HAL component" );
291
        return VLC_FALSE;
292
293
294
    }

    err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
295
    if( err != noErr )
296
    {
hartman's avatar
hartman committed
297
        msg_Warn( p_aout, "we cannot open our HAL component" );
298
        return VLC_FALSE;
299
    }
300
 
hartman's avatar
hartman committed
301
    /* Set the device we will use for this output unit */
302
    err = AudioUnitSetProperty( p_sys->au_unit,
303
304
305
306
                         kAudioOutputUnitProperty_CurrentDevice,
                         kAudioUnitScope_Global,
                         0,
                         &p_sys->i_selected_dev,
307
                         sizeof( AudioDeviceID ));
308
 
309
310
311
312
313
    if( err != noErr )
    {
        msg_Warn( p_aout, "we cannot select the audio device" );
        return VLC_FALSE;
    }
314
 
315
316
317
    /* Get the current format */
    i_param_size = sizeof(AudioStreamBasicDescription);

318
    err = AudioUnitGetProperty( p_sys->au_unit,
319
320
321
322
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
323
                                   &i_param_size );
324
 
325
326
    if( err != noErr ) return VLC_FALSE;
    else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
327

hartman's avatar
hartman committed
328
    /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
329
    err = AudioUnitGetPropertyInfo( p_sys->au_unit,
330
331
332
333
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   &i_param_size,
334
                                   NULL );
335

336
    if( err == noErr )
337
    {
338
339
340
341
342
343
344
345
        layout = (AudioChannelLayout *)malloc( i_param_size);

        verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                       kAudioDevicePropertyPreferredChannelLayout,
                                       kAudioUnitScope_Output,
                                       0,
                                       layout,
                                       &i_param_size ));
346
 
347
348
        /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
        if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
349
        {
350
351
352
353
354
355
356
357
358
359
360
361
362
            /* bitmap defined channellayout */
            verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
                                    sizeof( UInt32), &layout->mChannelBitmap,
                                    &i_param_size,
                                    layout ));
        }
        else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
        {
            /* layouttags defined channellayout */
            verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
                                    sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
                                    &i_param_size,
                                    layout ));
363
        }
364
365

        msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
366
 
367
368
369
        /* Initialize the VLC core channel count */
        p_aout->output.output.i_physical_channels = 0;
        i_original = p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK;
370
 
371
372
373
374
        if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
        {
            /* We only need Mono or cannot output more than 1 channel */
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
375
        }
376
        else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
hartman's avatar
hartman committed
377
        {
378
379
            /* We only need Stereo or cannot output more than 2 channels */
            p_aout->output.output.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
hartman's avatar
hartman committed
380
        }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
        else
        {
            /* We want more than stereo and we can do that */
            for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
            {
                msg_Dbg( p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );

                switch( layout->mChannelDescriptions[i].mChannelLabel )
                {
                    case kAudioChannelLabel_Left:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
                        continue;
                    case kAudioChannelLabel_Right:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
                        continue;
                    case kAudioChannelLabel_Center:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
                        continue;
                    case kAudioChannelLabel_LFEScreen:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
                        continue;
                    case kAudioChannelLabel_LeftSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
                        continue;
                    case kAudioChannelLabel_RightSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
                        continue;
                    case kAudioChannelLabel_RearSurroundLeft:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
                        continue;
                    case kAudioChannelLabel_RearSurroundRight:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
                        continue;
                    case kAudioChannelLabel_CenterSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
                        continue;
                    default:
418
                        msg_Warn( p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
419
420
421
422
423
424
                }
            }
            if( p_aout->output.output.i_physical_channels == 0 )
            {
                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
                msg_Err( p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
425
                intf_UserFatal( p_aout, VLC_FALSE, _("Audio device is not configured"),
426
                                _("You should configure your speaker layout with "
427
                                  "the \"Audio Midi Setup\" utility in /Applications/"
428
                                  "Utilities. Stereo mode is being used now.") );
429
430
431
432
433
434
435
436
            }
        }
        if( layout ) free( layout );
    }
    else
    {
        msg_Warn( p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
437
438
    }

439
    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
hartman's avatar
hartman committed
440
    msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
441

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    memset (&new_layout, 0, sizeof(new_layout));
    switch( aout_FormatNbChannels( &p_aout->output.output ) )
    {
        case 1:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
            break;
        case 2:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
            break;
        case 3:
            if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
            }
            else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
            }
            break;
        case 4:
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
            }
            break;
        case 5:
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
            }
            break;
        case 6:
hartman's avatar
hartman committed
486
487
488
489
490
491
492
493
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
            }
            else
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
            }
494
495
496
497
498
499
500
501
502
503
            break;
        case 7:
            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
            break;
        case 8:
            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
            break;
    }
504
505
506
507
508
509
510
511
512
513

    /* Set up the format to be used */
    DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
    DeviceFormat.mFormatID = kAudioFormatLinearPCM;

    /* We use float 32. It's the best supported format by both VLC and Coreaudio */
    p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
    DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
    DeviceFormat.mBitsPerChannel = 32;
    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
514
 
515
    /* Calculate framesizes and stuff */
516
517
518
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
519
 
hartman's avatar
hartman committed
520
    /* Set the desired format */
521
522
523
524
525
526
527
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
528
 
529
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
530
 
hartman's avatar
hartman committed
531
    /* Retrieve actual format */
532
533
534
535
536
537
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
538
 
539
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
540

hartman's avatar
hartman committed
541
542
    /* Do the last VLC aout setups */
    aout_FormatPrepare( &p_aout->output.output );
543
    p_aout->output.i_nb_samples = 2048;
544
545
546
547
548
    aout_VolumeSoftInit( p_aout );

    /* set the IOproc callback */
    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
549
 
550
551
552
553
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Input,
                            0, &input, sizeof( input ) ) );
554
555
556

    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
557
 
hartman's avatar
hartman committed
558
    /* Set the new_layout as the layout VLC will use to feed the AU unit */
559
560
561
562
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_AudioChannelLayout,
                            kAudioUnitScope_Input,
                            0, &new_layout, sizeof(new_layout) ) );
563
 
hartman's avatar
hartman committed
564
565
    if( new_layout.mNumberChannelDescriptions > 0 )
        free( new_layout.mChannelDescriptions );
566
 
567
568
569
    /* AU initiliaze */
    verify_noerr( AudioUnitInitialize(p_sys->au_unit) );

hartman's avatar
hartman committed
570
571
    /* Find the difference between device clock and mdate clock */
    p_sys->clock_diff = - (mtime_t)
572
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
hartman's avatar
hartman committed
573
574
575
    p_sys->clock_diff += mdate();

    /* Start the AU */
576
    verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
577
 
578
    return VLC_TRUE;
579
580
}

581
582
583
/*****************************************************************************
 * Setup a encoded digital stream (SPDIF)
 *****************************************************************************/
584
static int OpenSPDIF( aout_instance_t * p_aout )
585
{
586
587
588
589
590
591
    struct aout_sys_t       *p_sys = p_aout->output.p_sys;
    OSStatus                err = noErr;
    UInt32                  i_param_size = 0, b_mix = 0;
    Boolean                 b_writeable = VLC_FALSE;
    AudioStreamID           *p_streams = NULL;
    int                     i = 0, i_streams = 0;
592
593
594

    /* Start doing the SPDIF setup proces */
    p_sys->b_digital = VLC_TRUE;
595
596
597
598

    /* Hog the device */
    i_param_size = sizeof( p_sys->i_hog_pid );
    p_sys->i_hog_pid = getpid() ;
599
 
600
601
    err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                  kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
602
 
603
604
    if( err != noErr )
    {
hartman's avatar
hartman committed
605
        msg_Err( p_aout, "failed to set hogmode: [%4.4s]", (char *)&err );
606
607
        return VLC_FALSE;
    }
608

609
610
611
612
613
614
    /* Set mixable to false if we are allowed to */
    err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                    &i_param_size, &b_writeable );

    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                    &i_param_size, &b_mix );
615
 
616
617
618
619
620
621
622
    if( !err && b_writeable )
    {
        b_mix = 0;
        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                            kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
        p_sys->b_changed_mixing = VLC_TRUE;
    }
623
 
624
625
626
627
628
629
    if( err != noErr )
    {
        msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
        return VLC_FALSE;
    }

hartman's avatar
hartman committed
630
    /* Get a list of all the streams on this device */
631
632
633
634
635
636
637
638
    err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
                                      kAudioDevicePropertyStreams,
                                      &i_param_size, NULL );
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
        return VLC_FALSE;
    }
639
 
640
641
642
643
    i_streams = i_param_size / sizeof( AudioStreamID );
    p_streams = (AudioStreamID *)malloc( i_param_size );
    if( p_streams == NULL )
    {
644
        msg_Err( p_aout, "out of memory" );
645
646
        return VLC_FALSE;
    }
647
 
648
649
650
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                    kAudioDevicePropertyStreams,
                                    &i_param_size, p_streams );
651
 
652
653
654
655
656
657
658
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
        if( p_streams ) free( p_streams );
        return VLC_FALSE;
    }

659
    for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
660
    {
hartman's avatar
hartman committed
661
        /* Find a stream with a cac3 stream */
662
663
        AudioStreamBasicDescription *p_format_list = NULL;
        int                         i_formats = 0, j = 0;
664
        vlc_bool_t                  b_digital = VLC_FALSE;
665
 
666
667
668
669
670
671
672
673
674
        /* Retrieve all the stream formats supported by each output stream */
        err = AudioStreamGetPropertyInfo( p_streams[i], 0,
                                          kAudioStreamPropertyPhysicalFormats,
                                          &i_param_size, NULL );
        if( err != noErr )
        {
            msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
            continue;
        }
675
 
676
677
678
679
        i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
        p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
        if( p_format_list == NULL )
        {
hartman's avatar
hartman committed
680
            msg_Err( p_aout, "could not malloc the memory" );
681
682
            continue;
        }
683
 
684
685
686
687
688
689
690
691
692
693
        err = AudioStreamGetProperty( p_streams[i], 0,
                                          kAudioStreamPropertyPhysicalFormats,
                                          &i_param_size, p_format_list );
        if( err != noErr )
        {
            msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
            if( p_format_list) free( p_format_list);
            continue;
        }

hartman's avatar
hartman committed
694
        /* Check if one of the supported formats is a digital format */
695
696
697
698
699
        for( j = 0; j < i_formats; j++ )
        {
            if( p_format_list[j].mFormatID == 'IAC3' ||
                  p_format_list[j].mFormatID == kAudioFormat60958AC3 )
            {
700
701
702
703
                b_digital = VLC_TRUE;
                break;
            }
        }
704
 
705
706
        if( b_digital )
        {
hartman's avatar
hartman committed
707
708
709
710
711
            /* if this stream supports a digital (cac3) format, then go set it. */
            int i_requested_rate_format = -1;
            int i_current_rate_format = -1;
            int i_backup_rate_format = -1;

712
713
714
715
716
            p_sys->i_stream_id = p_streams[i];
            p_sys->i_stream_index = i;

            if( p_sys->b_revert == VLC_FALSE )
            {
hartman's avatar
hartman committed
717
                /* Retrieve the original format of this stream first if not done so already */
718
719
720
                i_param_size = sizeof( p_sys->sfmt_revert );
                err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
                                              kAudioStreamPropertyPhysicalFormat,
721
                                              &i_param_size,
722
723
                                              &p_sys->sfmt_revert );
                if( err != noErr )
724
                {
725
                    msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
726
                    continue;
727
                }
728
729
                p_sys->b_revert = VLC_TRUE;
            }
hartman's avatar
hartman committed
730

731
732
733
734
            for( j = 0; j < i_formats; j++ )
            {
                if( p_format_list[j].mFormatID == 'IAC3' ||
                      p_format_list[j].mFormatID == kAudioFormat60958AC3 )
735
                {
736
737
                    if( p_format_list[j].mSampleRate == p_aout->output.output.i_rate )
                    {
hartman's avatar
hartman committed
738
                        i_requested_rate_format = j;
739
740
741
742
                        break;
                    }
                    else if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
                    {
hartman's avatar
hartman committed
743
                        i_current_rate_format = j;
744
745
746
                    }
                    else
                    {
hartman's avatar
hartman committed
747
748
                        if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
                            i_backup_rate_format = j;
749
                    }
750
                }
751
 
752
            }
753
 
hartman's avatar
hartman committed
754
755
756
757
758
            if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
                p_sys->stream_format = p_format_list[i_requested_rate_format];
            else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
                p_sys->stream_format = p_format_list[i_current_rate_format];
            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) */
759
760
761
762
763
764
765
        }
        if( p_format_list ) free( p_format_list );
    }
    if( p_streams ) free( p_streams );

    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );

hartman's avatar
hartman committed
766
    if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
767
768
        return VLC_FALSE;

hartman's avatar
hartman committed
769
    /* Set the format flags */
770
771
772
773
774
775
776
777
    if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
    else
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
    p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
    p_aout->output.output.i_frame_length = A52_FRAME_NB;
    p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
    p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
hartman's avatar
hartman committed
778
    aout_FormatPrepare( &p_aout->output.output );
779
780
781
782
783
784
785
786
787
788
789
790
791
792
    aout_VolumeNoneInit( p_aout );

    /* Add IOProc callback */
    err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
                                (AudioDeviceIOProc)RenderCallbackSPDIF,
                                (void *)p_aout );
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
        return VLC_FALSE;
    }

    /* Check for the difference between the Device clock and mdate */
    p_sys->clock_diff = - (mtime_t)
793
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
794
795
796
    p_sys->clock_diff += mdate();
 
    /* Start device */
797
    err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF );
798
799
800
801
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );

802
        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
803
804
805
806
807
808
809
810
811
                                       (AudioDeviceIOProc)RenderCallbackSPDIF );
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
        }
        return VLC_FALSE;
    }

    return VLC_TRUE;
812
813
814
}


815
816
817
818
819
820
821
/*****************************************************************************
 * Close: Close HAL AudioUnit
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    aout_instance_t     *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t   *p_sys = p_aout->output.p_sys;
822
823
    OSStatus            err = noErr;
    UInt32              i_param_size = 0;
824
 
825
826
827
828
829
830
    if( p_sys->au_unit )
    {
        verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
        verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
        verify_noerr( CloseComponent( p_sys->au_unit ) );
    }
831
 
832
833
834
    if( p_sys->b_digital )
    {
        /* Stop device */
835
836
        err = AudioDeviceStop( p_sys->i_selected_dev,
                               (AudioDeviceIOProc)RenderCallbackSPDIF );
837
838
839
840
841
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
        }

hartman's avatar
hartman committed
842
        /* Remove IOProc callback */
843
844
845
846
847
848
        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
                                       (AudioDeviceIOProc)RenderCallbackSPDIF );
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
        }
849
 
850
851
        if( p_sys->b_revert )
        {
hartman's avatar
hartman committed
852
            AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->sfmt_revert );
853
        }
hartman's avatar
hartman committed
854

855
856
857
858
859
860
861
862
863
864
        if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
        {
            int b_mix;
            Boolean b_writeable;
            /* Revert mixable to true if we are allowed to */
            err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                        &i_param_size, &b_writeable );

            err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                        &i_param_size, &b_mix );
865
 
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
            if( !err && b_writeable )
            {
                msg_Dbg( p_aout, "mixable is: %d", b_mix );
                b_mix = 1;
                err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                    kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
            }

            if( err != noErr )
            {
                msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
            }
        }
    }

    err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
                                               HardwareListener );
883
 
884
885
886
887
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
    }
888
 
889
890
891
892
893
894
895
896
    if( p_sys->i_hog_pid == getpid() )
    {
        p_sys->i_hog_pid = -1;
        i_param_size = sizeof( p_sys->i_hog_pid );
        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                         kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
        if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
    }
897
 
898
    if( p_sys ) free( p_sys );
899
900
901
902
903
904
905
906
907
908
909
}

/*****************************************************************************
 * Play: nothing to do
 *****************************************************************************/
static void Play( aout_instance_t * p_aout )
{
}


/*****************************************************************************
hartman's avatar
hartman committed
910
 * Probe: Check which devices the OS has, and add them to our audio-device menu
911
 *****************************************************************************/
912
static void Probe( aout_instance_t * p_aout )
913
914
{
    OSStatus            err = noErr;
915
916
    UInt32              i = 0, i_param_size = 0;
    AudioDeviceID       devid_def = 0;
917
918
919
920
921
922
923
924
925
926
    AudioDeviceID       *p_devices = NULL;
    vlc_value_t         val, text;

    struct aout_sys_t   *p_sys = p_aout->output.p_sys;

    /* Get number of devices */
    err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
                                        &i_param_size, NULL );
    if( err != noErr )
    {
hartman's avatar
hartman committed
927
        msg_Err( p_aout, "Could not get number of devices: [%4.4s]", (char *)&err );
928
929
930
931
932
933
934
        goto error;
    }

    p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );

    if( p_sys->i_devices < 1 )
    {
hartman's avatar
hartman committed
935
        msg_Err( p_aout, "No audio output devices were found." );
936
937
938
939
940
941
        goto error;
    }

    msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );

    /* Allocate DeviceID array */
942
    p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
943
944
945
946
947
948
949
950
    if( p_devices == NULL )
    {
        msg_Err( p_aout, "out of memory" );
        goto error;
    }

    /* Populate DeviceID array */
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
951
                                    &i_param_size, p_devices );
952
953
    if( err != noErr )
    {
hartman's avatar
hartman committed
954
        msg_Err( p_aout, "could not get the device IDs: [%4.4s]", (char *)&err );
955
956
957
958
959
960
        goto error;
    }

    /* Find the ID of the default Device */
    i_param_size = sizeof( AudioDeviceID );
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
961
                                    &i_param_size, &devid_def );
962
963
964
965
966
967
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
        goto error;
    }
    p_sys->i_default_dev = devid_def;
968
 
969
970
971
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
    text.psz_string = _("Audio Device");
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
972
 
973
974
    for( i = 0; i < p_sys->i_devices; i++ )
    {
975
976
        char *psz_name;
        i_param_size = 0;
hartman's avatar
hartman committed
977

978
979
        /* Retrieve the length of the device name */
        err = AudioDeviceGetPropertyInfo(
980
981
                    p_devices[i], 0, VLC_FALSE,
                    kAudioDevicePropertyDeviceName,
982
983
                    &i_param_size, NULL);
        if( err ) goto error;
984

985
986
        /* Retrieve the name of the device */
        psz_name = (char *)malloc( i_param_size );
987
988
        err = AudioDeviceGetProperty(
                    p_devices[i], 0, VLC_FALSE,
989
990
991
                    kAudioDevicePropertyDeviceName,
                    &i_param_size, psz_name);
        if( err ) goto error;
992

hartman's avatar
hartman committed
993
        msg_Dbg( p_aout, "DevID: %#lx DevName: %s", p_devices[i], psz_name );
994

hartman's avatar
hartman committed
995
        if( !AudioDeviceHasOutput( p_devices[i]) )
hartman's avatar
hartman committed
996
997
998
999
1000
        {
            msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
            continue;
        }

hartman's avatar
hartman committed
1001
        /* Add the menu entries */
1002
1003
        val.i_int = (int)p_devices[i];
        text.psz_string = strdup( psz_name );
1004
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1005
        if( p_sys->i_default_dev == p_devices[i] )
1006
        {
hartman's avatar
hartman committed
1007
            /* The default device is the selected device normally */
1008
1009
1010
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            var_Set( p_aout, "audio-device", val );
        }
1011

1012
        if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
1013
1014
        {
            val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
hartman's avatar
hartman committed
1015
            asprintf( &text.psz_string, _("%s (Encoded Output)"), psz_name );
1016
1017
1018
            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
            if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
            {
1019
                /* We selected to prefer SPDIF output if available
hartman's avatar
hartman committed
1020
                 * then this "dummy" entry should be selected */
1021
1022
1023
1024
                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
                var_Set( p_aout, "audio-device", val );
            }
        }
1025
 
1026
        free( psz_name);
1027
    }
1028
 
hartman's avatar
hartman committed
1029
    /* If a device is already "preselected", then use this device */
1030
    var_Get( p_aout->p_libvlc, "macosx-audio-device", &val );
1031
1032
1033
1034
1035
    if( val.i_int > 0 )
    {
        var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
        var_Set( p_aout, "audio-device", val );
    }
1036
 
hartman's avatar
hartman committed
1037
    /* If we change the device we want to use, we should renegotiate the audio chain */
1038
1039
    var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );

hartman's avatar
hartman committed
1040
    /* Attach a Listener so that we are notified of a change in the Device setup */
1041
    err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
1042
                                            HardwareListener,
1043
1044
                                            (void *)p_aout );
    if( err )
1045
1046
        goto error;

1047
    if( p_devices ) free( p_devices );
1048
    return;
1049
1050
1051
1052

error:
    var_Destroy( p_aout, "audio-device" );
    if( p_devices ) free( p_devices );
1053
    return;
1054
1055
}

1056
1057
1058
1059
1060
/*****************************************************************************
 * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
 *****************************************************************************/
static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
{
1061
1062
1063
    UInt32            dataSize;
    Boolean            isWritable;
    
1064
1065
    verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
    if (dataSize == 0) return FALSE;
1066
 
1067
1068
1069
    return TRUE;
}

1070
/*****************************************************************************
1071
 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1072
 *****************************************************************************/
1073
static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
1074
1075
{
    OSStatus                    err = noErr;
1076
1077
    UInt32                      i_param_size = 0;
    AudioStreamID               *p_streams = NULL;
1078
    int                         i = 0, i_streams = 0;
1079
    vlc_bool_t                  b_return = VLC_FALSE;
1080
 
1081
    /* Retrieve all the output streams */
1082
    err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
1083
                                      kAudioDevicePropertyStreams,
1084
1085
1086
                                      &i_param_size, NULL );
    if( err != noErr )
    {
1087
1088
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
        return VLC_FALSE;
1089
    }
1090
 
1