auhal.c 30.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*****************************************************************************
 * auhal.c: AUHAL output plugin
 *****************************************************************************
 * Copyright (C) 2005 VideoLAN
 * $Id: coreaudio.c 10101 2005-03-02 16:47:31Z robux4 $
 *
 * 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.
 * 
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <string.h>
#include <stdlib.h>

#include <vlc/vlc.h>
#include <vlc/aout.h>

#include "aout_internal.h"

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

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


/*****************************************************************************
 * 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? */
    Component                   au_component;   /* The Audiocomponent we use */
    AudioUnit                   au_unit;        /* The AudioUnit we use */
    mtime_t                     clock_diff;
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int      Open                    ( vlc_object_t * );
static void     Close                   ( vlc_object_t * );

static void     Play                    ( aout_instance_t *);

76
static int      Probe                   ( aout_instance_t * );
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
static int      DeviceDigitalMode       ( aout_instance_t *, AudioDeviceID );
static int      DigitalInit             ( aout_instance_t * );

static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                          unsigned int, unsigned int, AudioBufferList *);
static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);

/*****************************************************************************
 * 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") );
95
    set_capability( "audio output", 101 );
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
    set_callbacks( Open, Close );
    //add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
vlc_module_end();

/*****************************************************************************
 * Open: open a HAL AudioUnit
 *****************************************************************************/
static int Open( vlc_object_t * p_this )
{
    OSStatus                err = noErr;
    ComponentDescription    desc;
    UInt32                  i_param_size,i;
    struct aout_sys_t       *p_sys;
    vlc_value_t             val;
    aout_instance_t         *p_aout = (aout_instance_t *)p_this;

    /* Allocate structure */
    p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
    if( p_sys == NULL )
    {
        msg_Err( p_aout, "out of memory" );
        return( VLC_ENOMEM );
    }

    memset( p_sys, 0, sizeof( struct aout_sys_t ) );

    p_sys->b_digital = VLC_FALSE; /* We assume we are not digital */

    p_aout->output.p_sys = p_sys;
    p_aout->output.pf_play = Play;
    
    aout_FormatPrint( p_aout, "VLC is looking for:\n", (audio_sample_format_t *)&p_aout->output.output );
    
    /* Build a list of devices */
    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
134
135
        Probe( p_aout );
        /*if( Probe( p_aout ) != VLC_SUCCESS );
136
        {
137
            msg_Err( p_aout, "Probe failed" );
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
            free( p_sys );
            return VLC_EGENERIC;
        }*/
    }
    
    /* What device do we want? */
    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
        msg_Err( p_aout, "audio-device var does not exist" );
        free( p_sys );
        return( VLC_ENOVAR );
    }
    p_sys->i_selected_dev = val.i_int;

    /* what is vlc format? if digital, take digital route else AUHAL route */
    DeviceDigitalMode( p_aout, p_sys->i_selected_dev );
    /*if( DeviceDigitalMode( p_aout, p_sys->i_selected_dev ) != VLC_SUCCESS );
    {
        msg_Err( p_aout, "DeviceDigitalMode failed" );
        free( p_sys );
        return VLC_EGENERIC;
    }
    */
    if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
    {
        p_sys->b_digital = VLC_TRUE;
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
        msg_Dbg( p_aout, "we found a digital stream, and we WANT a digital stream" );
    }
    else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
    {
        msg_Dbg( p_aout, "we had requested a digital stream, but it's not possible for this device" );
    }
 
    /* If analog only start setting up AUHAL */

    /* 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 )
    {
        msg_Err( p_aout, "we cannot find our HAL component" );
        free( p_sys );
        return VLC_EGENERIC;
    }

    err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
    if( err )
    {
        
        msg_Err( p_aout, "we cannot find our HAL component" );
        free( p_sys );
        return VLC_EGENERIC;
    }
    
    /* Enable IO for the component */
    
    /* Set the device */
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                         kAudioOutputUnitProperty_CurrentDevice,
                         kAudioUnitScope_Global,
                         0,
                         &p_sys->i_selected_dev,
                         sizeof(p_sys->i_selected_dev)));
                         
    /* Get the current format */
    AudioStreamBasicDescription DeviceFormat;
    
    i_param_size = sizeof(AudioStreamBasicDescription);

    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
                                   
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is " , DeviceFormat ) );

    /* Get the channel layout */
    AudioChannelLayout *layout;
    verify_noerr( AudioUnitGetPropertyInfo( p_sys->au_unit,
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   &i_param_size,
                                   NULL ));

    layout = (AudioChannelLayout *)malloc( i_param_size);

    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   layout,
                                   &i_param_size ));
                                   
    /* Lets fill out the ChannelLayout */
    if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
    {
        msg_Dbg( p_aout, "bitmap defined channellayout" );
        verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
                                sizeof( UInt32), &layout->mChannelBitmap,
                                &i_param_size,
                                layout ));
    }
    else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
    {
        msg_Dbg( p_aout, "layouttags defined channellayout" );
        verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
                                sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
                                &i_param_size,
                                layout ));
    }
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    msg_Dbg( p_aout, "Layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
    
    p_aout->output.output.i_physical_channels = 0;
    for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
    {
        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_LeftCenter:
                p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
                continue;
            case kAudioChannelLabel_RightCenter:
                p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
                continue;
            case kAudioChannelLabel_CenterSurround:
                p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
                continue;
            default:
                msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
                switch( layout->mNumberChannelDescriptions )
                {
                    /* We make assumptions based on number of channels here.
                     * Unfortunatly Apple has provided no 100% method to retrieve the speaker configuration */
                    case 1:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
                        break;
                    case 4:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                                                                    AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
                        break;
                    case 6:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                                                                    AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
                                                                    AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
                        break;
                    case 7:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                                                                    AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
                                                                    AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER;
                        break;
                    case 8:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                                                                    AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
                                                                    AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
                                                                    AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
                        break;
                    case 2:
                    default:
                        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
                }
327
328
329
330
331
332
333
                break;
        }
    }
    free( layout );

    msg_Dbg( p_aout, "defined %d physical channels for vlc core", aout_FormatNbChannels( &p_aout->output.output ) );
    msg_Dbg( p_aout, "%s", aout_FormatPrintChannels( &p_aout->output.output ));
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
    
    AudioChannelLayout new_layout;
    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:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
            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;
    }
391
392
393
394
395
396
397
398
399
400
401
402
403

    /* 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 );
    
    /* Calculate framesizes and stuff */
    aout_FormatPrepare( &p_aout->output.output );
404
405
406
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
 
    i_param_size = sizeof(AudioStreamBasicDescription);
    /* Set desired format (Use CAStreamBasicDescription )*/
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
                                   
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
    
    /* Retrieve actual format??? */
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
                                   
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
428

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    aout_VolumeSoftInit( p_aout );

    /* Let's pray for the following operation to be atomic... */
    p_sys->clock_diff = - (mtime_t)
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
    p_sys->clock_diff += mdate();

    /* set the IOproc callback */
    AURenderCallbackStruct input;
    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
    
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Input,
                            0, &input, sizeof( input ) ) );
445
446
447

    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
448
    
449
450
451
452
453
454
    /* Set the new_layout as the layout VLC feeds to the AU unit */
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_AudioChannelLayout,
                            kAudioUnitScope_Input,
                            0, &new_layout, sizeof(new_layout) ) );
    
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
486
487
    /* AU initiliaze */
    verify_noerr( AudioUnitInitialize(p_sys->au_unit) );

    verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
    return( VLC_SUCCESS );
}

/*****************************************************************************
 * 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;
    
    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 ) );
    }
    free( p_sys );
}

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


/*****************************************************************************
488
 * Probe
489
 *****************************************************************************/
490
static int Probe( aout_instance_t * p_aout )
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
{
    OSStatus            err = noErr;
    UInt32              i, i_param_size;
    AudioDeviceID       devid_def;
    AudioDeviceID       *p_devices = NULL;
    vlc_value_t         val, text;

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

    /* Get number of devices */
    err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
                                        &i_param_size, NULL );
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of devices: [%4.4s]", (char *)&err );
        goto error;
    }

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

    if( p_sys->i_devices < 1 )
    {
        msg_Err( p_aout, "no devices found" );
        goto error;
    }

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

    /* Allocate DeviceID array */
    p_devices = (AudioDeviceID *)malloc( i_param_size );
    if( p_devices == NULL )
    {
        msg_Err( p_aout, "out of memory" );
        goto error;
    }

    /* Populate DeviceID array */
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
                                    &i_param_size, (void *)p_devices );
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get the device ID's: [%4.4s]", (char *)&err );
        goto error;
    }

    /* Find the ID of the default Device */
    i_param_size = sizeof( AudioDeviceID );
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
                                    &i_param_size, (void *)&devid_def );
    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;
    
    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 );
    
    for( i = 0; i < p_sys->i_devices; i++ )
    {
        char psz_devuid[1024];
        char psz_name[1024];
        CFStringRef devUID;
            
        i_param_size = sizeof psz_name;
        err = AudioDeviceGetProperty(
                    p_devices[i], 0, VLC_FALSE,
                    kAudioDevicePropertyDeviceName,
                    &i_param_size, psz_name);
        if( err )
            goto error;

        i_param_size = sizeof(CFStringRef);    
        err = AudioDeviceGetProperty(
                    p_devices[i], 0, VLC_FALSE,
                    kAudioDevicePropertyDeviceUID,
                    &i_param_size, &devUID);
        if( err )
            goto error;

        CFStringGetCString( devUID, psz_devuid, sizeof psz_devuid, CFStringGetSystemEncoding() );
        msg_Dbg( p_aout, "DevID: %lu  DevName: %s  DevUID: %s", p_devices[i], psz_name, psz_devuid );
        CFRelease( devUID );

        val.i_int = (int) p_devices[i];
        text.psz_string = psz_name;
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
        if( devid_def == p_devices[i] )
        {
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            var_Set( p_aout, "audio-device", val );
        }
    }
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
    
    /* attach a Listener so that we are notified of a change in the Device setup */
    /*err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
                                            HardwareListener, 
                                            (void *)p_aout );
    if( err )
        goto error;*/
    
    msg_Dbg( p_aout, "succesful finish of deviceslist" );
    if( p_devices ) free( p_devices );
    return (VLC_SUCCESS);

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

/*****************************************************************************
 * DeviceDigitalMode: Check i_dev_id for digital stream support.
 *****************************************************************************/
static int DeviceDigitalMode( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
{
    OSStatus                    err = noErr;
    UInt32                      i_param_size;
    AudioStreamBasicDescription *p_format_list;
    int                         i, i_formats;
    struct aout_sys_t           *p_sys = p_aout->output.p_sys;
    
    p_sys->b_supports_digital = VLC_FALSE;
    
    err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
                                      kAudioDevicePropertyStreamFormats,
                                      &i_param_size, NULL );
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streamsformats: [%4.4s]", (char *)&err );
        return( VLC_EGENERIC );
    }
    
    i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
    p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
    if( p_format_list == NULL )
    {
        return( VLC_ENOMEM );
    }
    
    err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
                                      kAudioDevicePropertyStreamFormats,
                                      &i_param_size, (void *)p_format_list );
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get the list of formats: [%4.4s]", (char *)&err );
        return( VLC_EGENERIC );
    }

    for( i = 0; i < i_formats; i++ )
    {
        msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
        
        if( p_format_list[i].mFormatID == 'IAC3' ||
                  p_format_list[i].mFormatID == kAudioFormat60958AC3 )
        {
            p_sys->b_supports_digital = VLC_TRUE;
            msg_Dbg( p_aout, "this device supports a digital stream" );
            break;
        }
    }
    
    free( (void *)p_format_list );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
 * us to provide some more audio data.
663
664
 * Don't print anything during normal playback, calling blocking function from
 * this callback is not allowed.
665
666
667
668
669
670
671
672
673
674
 *****************************************************************************/
static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
                                      AudioUnitRenderActionFlags *ioActionFlags,
                                      const AudioTimeStamp *inTimeStamp,
                                      unsigned int inBusNummer,
                                      unsigned int inNumberFrames,
                                      AudioBufferList *ioData )
{
    aout_buffer_t * p_buffer;
    AudioTimeStamp  host_time;
675
    mtime_t         current_date = 0;
676
677
678
679
680
681
682
683
684
685
686

    aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
    struct aout_sys_t * p_sys = p_aout->output.p_sys;

    host_time.mFlags = kAudioTimeStampHostTimeValid;
    AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );

    p_sys->clock_diff = - (mtime_t)
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
    p_sys->clock_diff += mdate();

687
688
    current_date = p_sys->clock_diff +
                   AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
689

690
691
    p_aout->output.i_nb_samples = inNumberFrames;

692
693
694
695
696
697
698
    if( ioData == NULL && ioData->mNumberBuffers < 1 )
    {
        msg_Err( p_aout, "no iodata or buffers");
        return 0;
    }
    if( ioData->mNumberBuffers > 1 )
        msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
699

700
701
    
    p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
702
703
    if( p_buffer != NULL )
    {
704
705
706
        p_aout->p_vlc->pf_memcpy(ioData->mBuffers[0].mData, p_buffer->p_buffer, __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize ) );

        if( p_buffer->i_nb_bytes != ioData->mBuffers[0].mDataByteSize )
707
        {
708
709
            /* FIXME */
            //msg_Err( p_aout, "byte sizes don't match %d:%d\nframes: %d, nb_samples: %d", p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize, inNumberFrames, p_aout->output.i_nb_samples);
710
711
        }
        aout_BufferFree( p_buffer );
712
713
714
    }
    else
    {
715
         p_aout->p_vlc->pf_memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    }
    return( noErr );     
}


/*****************************************************************************
 * Setup a digital stream
 *****************************************************************************/
static int DigitalInit( aout_instance_t * p_aout )
{
    OSStatus            err = noErr;
    UInt32              i, i_param_size;
    AudioDeviceID       devid_def;
    AudioDeviceID       *p_devices = NULL;
    vlc_value_t         val, text;

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

    
    
    return (VLC_SUCCESS);

error:
    return VLC_EGENERIC;
}


/*****************************************************************************
 * HardwareListener: Warns us of changes in the list of registered devices
 *****************************************************************************/
static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
                                  void * inClientData )
{
    OSStatus err = noErr;

    aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
    /* struct aout_sys_t   *p_sys = p_aout->output.p_sys; */

    switch( inPropertyID )
    {
        case kAudioHardwarePropertyDevices:
        {
            /* something changed in the list of devices */
            /* We trigger the audio-device's aout_ChannelsRestart callback */
            var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
        }
        break;
    }

    return( err );
}