auhal.c 58.5 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
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
32
#include <unistd.h>

33
#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
34
35
#include <vlc_interface.h>
#include <vlc_aout.h>
36
37
38
39
40
41
42
43

#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 ) \
44
45
46
47
48
49
50
    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 ) \
51
52
53
54
55
56
    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

57
#define BUFSIZE 0xffffff
58
#define AOUT_VAR_SPDIF_FLAG 0xf00000
59

60
61
62
63
64
/*
 * 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
65
 * - fix 6.1 and 7.1
66
67
 */

68
69
70
71
72
73
74
75
76
77
78
/*****************************************************************************
 * 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 */
79
80
    bool                  b_supports_digital;/* Does the currently selected device support digital mode? */
    bool                  b_digital;      /* Are we running in digital mode? */
81
    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
hartman's avatar
hartman committed
82

83
    /* AUHAL specific */
84
85
    Component                   au_component;   /* The Audiocomponent we use */
    AudioUnit                   au_unit;        /* The AudioUnit we use */
86
    uint8_t                     p_remainder_buffer[BUFSIZE];
87
88
    uint32_t                    i_read_bytes;
    uint32_t                    i_total_bytes;
hartman's avatar
hartman committed
89

90
91
92
93
    /* 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
94
    AudioStreamBasicDescription stream_format;  /* The format we changed the stream to */
95
    AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
96
97
    bool                  b_revert;       /* Wether we need to revert the stream format */
    bool                  b_changed_mixing;/* Wether we need to set the mixing mode back */
98
99
100
101
102
103
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int      Open                    ( vlc_object_t * );
104
105
static int      OpenAnalog              ( aout_instance_t * );
static int      OpenSPDIF               ( aout_instance_t * );
106
107
static void     Close                   ( vlc_object_t * );

108
109
static void     Play                    ( aout_instance_t * );
static void     Probe                   ( aout_instance_t * );
110

111
112
113
static int      AudioDeviceHasOutput    ( AudioDeviceID );
static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
hartman's avatar
hartman committed
114
static int      AudioStreamChangeFormat ( aout_instance_t *, AudioStreamID, AudioStreamBasicDescription );
115
116
117

static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                          unsigned int, unsigned int, AudioBufferList *);
118
119
static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
                                          AudioBufferList *, const AudioTimeStamp *, void * );
120
static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
121
122
123
124
125
static OSStatus StreamListener          ( AudioStreamID, UInt32,
                                          AudioDevicePropertyID, void * );
static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
                                          vlc_value_t, vlc_value_t, void * );

126
127
128
129
130
131
132
133
134
135
136
137

/*****************************************************************************
 * 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") );
138
    set_capability( "audio output", 101 );
139
140
141
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
    set_callbacks( Open, Close );
142
    add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, false );
143
144
145
vlc_module_end();

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

156
157
    /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
     * property size */
158
    int                     b_alive = false; 
159

160
    /* Allocate structure */
161
162
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
163
164
165
166
167
    {
        msg_Err( p_aout, "out of memory" );
        return( VLC_ENOMEM );
    }

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;
172
173
    p_sys->b_supports_digital = false;
    p_sys->b_digital = false;
174
175
176
177
178
    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;
179
180
    p_sys->i_hog_pid = -1;
    p_sys->i_stream_id = 0;
181
    p_sys->i_stream_index = -1;
182
183
    p_sys->b_revert = false;
    p_sys->b_changed_mixing = false;
184
    memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
185
186

    p_aout->output.pf_play = Play;
187
 
188
    aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
189
 
190
    /* Persistent device variable */
191
    if( var_Type( p_aout->p_libvlc, "macosx-audio-device" ) == 0 )
192
    {
193
        var_Create( p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
194
195
    }

196
197
198
    /* Build a list of devices */
    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
199
        Probe( p_aout );
200
    }
201

202
203
204
    /* What device do we want? */
    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
205
        msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
hartman's avatar
hartman committed
206
        goto error;
207
208
    }

hartman's avatar
hartman committed
209
    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
210
    p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? true : false;
211
212

    /* Check if the desired device is alive and usable */
hartman's avatar
hartman committed
213
    /* TODO: add a callback to the device to alert us if the device dies */
214
215
216
217
218
219
220
    i_param_size = sizeof( b_alive );
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                  kAudioDevicePropertyDeviceIsAlive,
                                  &i_param_size, &b_alive );

    if( err != noErr )
    {
221
        /* Be tolerant, only give a warning here */
222
        msg_Warn( p_aout, "could not check whether device [0x%x] is alive: %4.4s", (unsigned int)p_sys->i_selected_dev, (char *)&err );
223
        b_alive = false;
224
225
    }

226
    if( b_alive == false )
227
    {
228
        msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
229
        p_sys->i_selected_dev = p_sys->i_default_dev;
230
    }
231

232
    i_param_size = sizeof( p_sys->i_hog_pid );
233
234
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                  kAudioDevicePropertyHogMode,
235
                                  &i_param_size, &p_sys->i_hog_pid );
236
237
238

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

245
    if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
246
    {
hartman's avatar
hartman committed
247
        msg_Err( p_aout, "Selected audio device is exclusively in use by another program." );
248
        intf_UserFatal( p_aout, false, _("Audio output failed"),
249
250
                        _("The selected audio output device is exclusively in "
                          "use by another program.") );
hartman's avatar
hartman committed
251
        goto error;
252
253
254
    }

    /* Check for Digital mode or Analog output mode */
255
256
    if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
    {
257
258
259
260
261
262
263
        if( OpenSPDIF( p_aout ) )
            return VLC_SUCCESS;
    }
    else
    {
        if( OpenAnalog( p_aout ) )
            return VLC_SUCCESS;
264
    }
hartman's avatar
hartman committed
265
266
267

error:
    /* If we reach this, this aout has failed */
268
    var_Destroy( p_aout, "audio-device" );
ivoire's avatar
ivoire committed
269
    free( p_sys );
270
271
272
    return VLC_EGENERIC;
}

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

    /* 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
298
        msg_Warn( p_aout, "we cannot find our HAL component" );
299
        return false;
300
301
302
    }

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

326
    err = AudioUnitGetProperty( p_sys->au_unit,
327
328
329
330
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
331
                                   &i_param_size );
332
 
333
    if( err != noErr ) return false;
334
    else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
335

hartman's avatar
hartman committed
336
    /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
337
    err = AudioUnitGetPropertyInfo( p_sys->au_unit,
338
339
340
341
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   &i_param_size,
342
                                   NULL );
343

344
    if( err == noErr )
345
    {
346
347
348
349
350
351
352
353
        layout = (AudioChannelLayout *)malloc( i_param_size);

        verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                       kAudioDevicePropertyPreferredChannelLayout,
                                       kAudioUnitScope_Output,
                                       0,
                                       layout,
                                       &i_param_size ));
354
 
355
356
        /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
        if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
357
        {
358
359
360
361
362
363
364
365
366
367
368
369
370
            /* 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 ));
371
        }
372
373

        msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
374
 
375
376
377
        /* 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;
378
 
379
380
381
382
        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;
383
        }
384
        else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
hartman's avatar
hartman committed
385
        {
386
387
            /* 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
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
418
419
420
421
422
423
424
425
        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:
426
                        msg_Warn( p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
427
428
429
430
431
432
                }
            }
            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." );
433
                intf_UserFatal( p_aout, false, _("Audio device is not configured"),
434
                                _("You should configure your speaker layout with "
435
                                  "the \"Audio Midi Setup\" utility in /Applications/"
436
                                  "Utilities. Stereo mode is being used now.") );
437
438
            }
        }
ivoire's avatar
ivoire committed
439
        free( layout );
440
441
442
443
444
    }
    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;
445
446
    }

447
    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
hartman's avatar
hartman committed
448
    msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
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
486
487
488
489
490
491
492
493
    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
494
495
496
497
498
499
500
501
            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
            }
502
503
504
505
506
507
508
509
510
511
            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;
    }
512
513
514
515
516
517
518
519
520
521

    /* 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 );
522
 
523
    /* Calculate framesizes and stuff */
524
525
526
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
527
 
hartman's avatar
hartman committed
528
    /* Set the desired format */
529
530
531
532
533
534
535
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
536
 
537
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
538
 
hartman's avatar
hartman committed
539
    /* Retrieve actual format */
540
541
542
543
544
545
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
546
 
547
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
548

hartman's avatar
hartman committed
549
550
    /* Do the last VLC aout setups */
    aout_FormatPrepare( &p_aout->output.output );
551
    p_aout->output.i_nb_samples = 2048;
552
553
554
555
556
    aout_VolumeSoftInit( p_aout );

    /* set the IOproc callback */
    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
557
 
558
559
560
561
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Input,
                            0, &input, sizeof( input ) ) );
562
563
564

    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
565
 
hartman's avatar
hartman committed
566
    /* Set the new_layout as the layout VLC will use to feed the AU unit */
567
568
569
570
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_AudioChannelLayout,
                            kAudioUnitScope_Input,
                            0, &new_layout, sizeof(new_layout) ) );
571
 
hartman's avatar
hartman committed
572
573
    if( new_layout.mNumberChannelDescriptions > 0 )
        free( new_layout.mChannelDescriptions );
574
 
575
576
577
    /* AU initiliaze */
    verify_noerr( AudioUnitInitialize(p_sys->au_unit) );

hartman's avatar
hartman committed
578
579
    /* Find the difference between device clock and mdate clock */
    p_sys->clock_diff = - (mtime_t)
580
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
hartman's avatar
hartman committed
581
582
583
    p_sys->clock_diff += mdate();

    /* Start the AU */
584
    verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
585
 
586
    return true;
587
588
}

589
590
591
/*****************************************************************************
 * Setup a encoded digital stream (SPDIF)
 *****************************************************************************/
592
static int OpenSPDIF( aout_instance_t * p_aout )
593
{
594
595
596
    struct aout_sys_t       *p_sys = p_aout->output.p_sys;
    OSStatus                err = noErr;
    UInt32                  i_param_size = 0, b_mix = 0;
597
    Boolean                 b_writeable = false;
598
599
    AudioStreamID           *p_streams = NULL;
    int                     i = 0, i_streams = 0;
600
601

    /* Start doing the SPDIF setup proces */
602
    p_sys->b_digital = true;
603
604
605
606

    /* Hog the device */
    i_param_size = sizeof( p_sys->i_hog_pid );
    p_sys->i_hog_pid = getpid() ;
607
 
608
609
    err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                  kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
610
 
611
612
    if( err != noErr )
    {
hartman's avatar
hartman committed
613
        msg_Err( p_aout, "failed to set hogmode: [%4.4s]", (char *)&err );
614
        return false;
615
    }
616

617
618
619
620
621
622
    /* 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 );
623
 
624
625
626
627
628
    if( !err && b_writeable )
    {
        b_mix = 0;
        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                            kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
629
        p_sys->b_changed_mixing = true;
630
    }
631
 
632
633
634
    if( err != noErr )
    {
        msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
635
        return false;
636
637
    }

hartman's avatar
hartman committed
638
    /* Get a list of all the streams on this device */
639
640
641
642
643
644
    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 );
645
        return false;
646
    }
647
 
648
649
650
651
    i_streams = i_param_size / sizeof( AudioStreamID );
    p_streams = (AudioStreamID *)malloc( i_param_size );
    if( p_streams == NULL )
    {
652
        msg_Err( p_aout, "out of memory" );
653
        return false;
654
    }
655
 
656
657
658
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                    kAudioDevicePropertyStreams,
                                    &i_param_size, p_streams );
659
 
660
661
662
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
ivoire's avatar
ivoire committed
663
        free( p_streams );
664
        return false;
665
666
    }

667
    for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
668
    {
hartman's avatar
hartman committed
669
        /* Find a stream with a cac3 stream */
670
671
        AudioStreamBasicDescription *p_format_list = NULL;
        int                         i_formats = 0, j = 0;
672
        bool                  b_digital = false;
673
 
674
675
676
677
678
679
680
681
682
        /* 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;
        }
683
 
684
685
686
687
        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
688
            msg_Err( p_aout, "could not malloc the memory" );
689
690
            continue;
        }
691
 
692
693
694
695
696
697
        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 );
ivoire's avatar
ivoire committed
698
            free( p_format_list );
699
700
701
            continue;
        }

hartman's avatar
hartman committed
702
        /* Check if one of the supported formats is a digital format */
703
704
705
706
707
        for( j = 0; j < i_formats; j++ )
        {
            if( p_format_list[j].mFormatID == 'IAC3' ||
                  p_format_list[j].mFormatID == kAudioFormat60958AC3 )
            {
708
                b_digital = true;
709
710
711
                break;
            }
        }
712
 
713
714
        if( b_digital )
        {
hartman's avatar
hartman committed
715
716
717
718
719
            /* 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;

720
721
722
            p_sys->i_stream_id = p_streams[i];
            p_sys->i_stream_index = i;

723
            if( p_sys->b_revert == false )
724
            {
hartman's avatar
hartman committed
725
                /* Retrieve the original format of this stream first if not done so already */
726
727
728
                i_param_size = sizeof( p_sys->sfmt_revert );
                err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
                                              kAudioStreamPropertyPhysicalFormat,
729
                                              &i_param_size,
730
731
                                              &p_sys->sfmt_revert );
                if( err != noErr )
732
                {
733
                    msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
734
                    continue;
735
                }
736
                p_sys->b_revert = true;
737
            }
hartman's avatar
hartman committed
738

739
740
741
742
            for( j = 0; j < i_formats; j++ )
            {
                if( p_format_list[j].mFormatID == 'IAC3' ||
                      p_format_list[j].mFormatID == kAudioFormat60958AC3 )
743
                {
744
745
                    if( p_format_list[j].mSampleRate == p_aout->output.output.i_rate )
                    {
hartman's avatar
hartman committed
746
                        i_requested_rate_format = j;
747
748
749
750
                        break;
                    }
                    else if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
                    {
hartman's avatar
hartman committed
751
                        i_current_rate_format = j;
752
753
754
                    }
                    else
                    {
hartman's avatar
hartman committed
755
756
                        if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
                            i_backup_rate_format = j;
757
                    }
758
                }
759
 
760
            }
761
 
hartman's avatar
hartman committed
762
763
764
765
766
            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) */
767
        }
ivoire's avatar
ivoire committed
768
        free( p_format_list );
769
    }
ivoire's avatar
ivoire committed
770
    free( p_streams );
771
772
773

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

hartman's avatar
hartman committed
774
    if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
775
        return false;
776

hartman's avatar
hartman committed
777
    /* Set the format flags */
778
779
780
781
782
783
784
785
    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
786
    aout_FormatPrepare( &p_aout->output.output );
787
788
789
    aout_VolumeNoneInit( p_aout );

    /* Add IOProc callback */
790
791
792
793
    err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
                               (AudioDeviceIOProc)RenderCallbackSPDIF,
                               (void *)p_aout );

794
795
796
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
797
        return false;
798
799
800
801
    }

    /* Check for the difference between the Device clock and mdate */
    p_sys->clock_diff = - (mtime_t)
802
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
803
804
805
    p_sys->clock_diff += mdate();
 
    /* Start device */
806
    err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF );
807
808
809
810
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );

811
812
        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
                                     (AudioDeviceIOProc)RenderCallbackSPDIF );
813
814
815
816
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
        }
817
        return false;
818
819
    }

820
    return true;
821
822
823
}


824
825
826
827
828
829
830
/*****************************************************************************
 * 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;
831
832
    OSStatus            err = noErr;
    UInt32              i_param_size = 0;
833
 
834
835
836
837
838
839
    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 ) );
    }
840
 
841
842
843
    if( p_sys->b_digital )
    {
        /* Stop device */
844
845
        err = AudioDeviceStop( p_sys->i_selected_dev,
                               (AudioDeviceIOProc)RenderCallbackSPDIF );
846
847
848
849
850
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
        }

hartman's avatar
hartman committed
851
        /* Remove IOProc callback */
852
853
        err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
                                      (AudioDeviceIOProc)RenderCallbackSPDIF );
854
855
856
857
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
        }
858
 
859
860
        if( p_sys->b_revert )
        {
hartman's avatar
hartman committed
861
            AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->sfmt_revert );
862
        }
hartman's avatar
hartman committed
863

864
865
866
867
868
869
870
871
872
873
        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 );
874
 
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
            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 );
892
 
893
894
895
896
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
    }
897
 
898
899
900
901
902
903
904
905
    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 );
    }
906
 
ivoire's avatar
ivoire committed
907
    free( p_sys );
908
909
910
911
912
913
914
915
916
917
918
}

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


/*****************************************************************************
hartman's avatar
hartman committed
919
 * Probe: Check which devices the OS has, and add them to our audio-device menu
920
 *****************************************************************************/
921
static void Probe( aout_instance_t * p_aout )
922
923
{
    OSStatus            err = noErr;
924
925
    UInt32              i = 0, i_param_size = 0;
    AudioDeviceID       devid_def = 0;
926
927
928
929
930
931
932
933
934
935
    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
936
        msg_Err( p_aout, "Could not get number of devices: [%4.4s]", (char *)&err );
937
938
939
940
941
942
943
        goto error;
    }

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

    if( p_sys->i_devices < 1 )
    {
hartman's avatar
hartman committed
944
        msg_Err( p_aout, "No audio output devices were found." );
945
946
947
948
949
950
        goto error;
    }

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

    /* Allocate DeviceID array */
951
    p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
952
953
954
955
956
957
958
959
    if( p_devices == NULL )
    {
        msg_Err( p_aout, "out of memory" );
        goto error;
    }

    /* Populate DeviceID array */
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
960
                                    &i_param_size, p_devices );
961
962
    if( err != noErr )
    {
hartman's avatar
hartman committed
963
        msg_Err( p_aout, "could not get the device IDs: [%4.4s]", (char *)&err );
964
965
966
967
968
969
        goto error;
    }

    /* Find the ID of the default Device */
    i_param_size = sizeof( AudioDeviceID );
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
970
                                    &i_param_size, &devid_def );
971
972
973
974
975
976
    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;
977
 
978
979
980
    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 );
981
 
982
983
    for( i = 0; i < p_sys->i_devices; i++ )
    {
984
985
        char *psz_name;
        i_param_size = 0;
hartman's avatar
hartman committed
986

987
988
        /* Retrieve the length of the device name */
        err = AudioDeviceGetPropertyInfo(
989
                    p_devices[i], 0, false,
990
                    kAudioDevicePropertyDeviceName,
991
992
                    &i_param_size, NULL);
        if( err ) goto error;
993

994
995
        /* Retrieve the name of the device */
        psz_name = (char *)malloc( i_param_size );
996
        err = AudioDeviceGetProperty(
997
                    p_devices[i], 0, false,
998
999
1000
                    kAudioDevicePropertyDeviceName,
                    &i_param_size, psz_name);
        if( err ) goto error;
1001

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

hartman's avatar
hartman committed
1004
        if( !AudioDeviceHasOutput( p_devices[i]) )
hartman's avatar
hartman committed
1005
1006
1007
1008
1009
        {
            msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
            continue;
        }

hartman's avatar
hartman committed
1010
        /* Add the menu entries */
1011
1012
        val.i_int = (int)p_devices[i];
        text.psz_string = strdup( psz_name );
1013
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1014
        if( p_sys->i_default_dev == p_devices[i] )
1015
        {
hartman's avatar
hartman committed
1016
            /* The default device is the selected device normally */
1017
1018
1019
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            var_Set( p_aout, "audio-device", val );
        }
1020

1021
        if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
1022
1023
        {
            val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
hartman's avatar
hartman committed
1024
            asprintf( &text.psz_string, _("%s (Encoded Output)"), psz_name );
1025
1026
1027
            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" ) )
            {