auhal.c 59.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
13
14
15
16
17
18
19
20
 *
 * 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
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <string.h>
#include <stdlib.h>
29
#include <unistd.h>
30
31

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

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

55
#define BUFSIZE 0xffffff
56
#define AOUT_VAR_SPDIF_FLAG 0xf00000
57

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

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 */
    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? */
79
    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
hartman's avatar
hartman committed
80

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

88
89
90
91
    /* 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
92
    AudioStreamBasicDescription stream_format;  /* The format we changed the stream to */
93
94
95
    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 */
96
97
98
99
100
101
};

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

106
107
static void     Play                    ( aout_instance_t * );
static void     Probe                   ( aout_instance_t * );
108

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

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

124
125
126
127
128
129
130
131
132
133
134
135

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

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

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

163
164
165
166
167
168
169
170
171
172
173
    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;
174
175
    p_sys->i_hog_pid = -1;
    p_sys->i_stream_id = 0;
176
    p_sys->i_stream_index = -1;
177
178
    p_sys->b_revert = VLC_FALSE;
    p_sys->b_changed_mixing = VLC_FALSE;
179
    memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
180
181
182

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

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

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

hartman's avatar
hartman committed
204
    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
205
206
207
    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
208
    /* TODO: add a callback to the device to alert us if the device dies */
209
210
211
212
213
214
215
    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
216
217
        msg_Err( p_aout, "could not check whether device is alive: %4.4s", (char *)&err );
        goto error;
218
219
220
    }

    if( b_alive == VLC_FALSE )
221
    {
hartman's avatar
hartman committed
222
        msg_Warn( p_aout, "selected audio device is not alive, switching to default device" ); 
223
        p_sys->i_selected_dev = p_sys->i_default_dev;
224
    }
225

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

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

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

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

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

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

    /* 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
292
        msg_Warn( p_aout, "we cannot find our HAL component" );
293
        return VLC_FALSE;
294
295
296
    }

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

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

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

338
    if( err == noErr )
339
    {
340
341
342
343
344
345
346
347
348
349
350
        layout = (AudioChannelLayout *)malloc( i_param_size);

        verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                       kAudioDevicePropertyPreferredChannelLayout,
                                       kAudioUnitScope_Output,
                                       0,
                                       layout,
                                       &i_param_size ));
                                       
        /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
        if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
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
            /* 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 ));
        } 

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

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

    /* 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 */
518
519
520
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
521
 
hartman's avatar
hartman committed
522
    /* Set the desired format */
523
524
525
526
527
528
529
530
531
532
    i_param_size = sizeof(AudioStreamBasicDescription);
    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 ) );
    
hartman's avatar
hartman committed
533
    /* Retrieve actual format */
534
535
536
537
538
539
540
541
    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 ) );
542

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

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

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

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

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

583
584
585
/*****************************************************************************
 * Setup a encoded digital stream (SPDIF)
 *****************************************************************************/
586
static int OpenSPDIF( aout_instance_t * p_aout )
587
{
588
589
590
591
592
593
    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;
594
595
596

    /* Start doing the SPDIF setup proces */
    p_sys->b_digital = VLC_TRUE;
597
598
599
600
601
602
603

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

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
    /* 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 );
                                    
    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;
    }
    
    if( err != noErr )
    {
        msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
        return VLC_FALSE;
    }

hartman's avatar
hartman committed
632
    /* Get a list of all the streams on this device */
633
634
635
636
637
638
639
640
641
642
643
644
645
    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;
    }
    
    i_streams = i_param_size / sizeof( AudioStreamID );
    p_streams = (AudioStreamID *)malloc( i_param_size );
    if( p_streams == NULL )
    {
646
        msg_Err( p_aout, "out of memory" );
647
648
649
650
651
652
653
654
655
656
657
658
659
660
        return VLC_FALSE;
    }
    
    err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
                                    kAudioDevicePropertyStreams,
                                    &i_param_size, p_streams );
    
    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;
    }

661
    for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
662
    {
hartman's avatar
hartman committed
663
        /* Find a stream with a cac3 stream */
664
665
        AudioStreamBasicDescription *p_format_list = NULL;
        int                         i_formats = 0, j = 0;
666
        vlc_bool_t                  b_digital = VLC_FALSE;
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
        
        /* 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;
        }
        
        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
682
            msg_Err( p_aout, "could not malloc the memory" );
683
684
685
686
687
688
689
690
691
692
693
694
695
            continue;
        }
        
        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
696
        /* Check if one of the supported formats is a digital format */
697
698
699
700
701
        for( j = 0; j < i_formats; j++ )
        {
            if( p_format_list[j].mFormatID == 'IAC3' ||
                  p_format_list[j].mFormatID == kAudioFormat60958AC3 )
            {
702
703
704
705
706
707
708
                b_digital = VLC_TRUE;
                break;
            }
        }
        
        if( b_digital )
        {
hartman's avatar
hartman committed
709
710
711
712
713
            /* 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;

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

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

hartman's avatar
hartman committed
771
    /* Set the format flags */
772
773
774
775
776
777
778
779
    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
780
    aout_FormatPrepare( &p_aout->output.output );
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
    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)
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
    p_sys->clock_diff += mdate();
 
    /* Start device */
    err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF ); 
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );

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

    return VLC_TRUE;
814
815
816
}


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

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

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

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


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

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

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

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

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

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

    /* Find the ID of the default Device */
    i_param_size = sizeof( AudioDeviceID );
    err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
963
                                    &i_param_size, &devid_def );
964
965
966
967
968
969
970
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;
    
    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++ )
    {
977
978
        char *psz_name;
        i_param_size = 0;
hartman's avatar
hartman committed
979

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

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

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

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