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

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

29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

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
79
80
/*****************************************************************************
 * 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? */
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
96
97
    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 */
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, VLC_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
153
    struct aout_sys_t       *p_sys = NULL;
    vlc_bool_t              b_alive = VLC_FALSE;
154
155
156
157
    vlc_value_t             val;
    aout_instance_t         *p_aout = (aout_instance_t *)p_this;

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

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

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

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

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

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

    if( b_alive == VLC_FALSE )
223
    {
224
        msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
225
        p_sys->i_selected_dev = p_sys->i_default_dev;
226
    }
227

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

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

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

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

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

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

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

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

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

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

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

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

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

443
    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
hartman's avatar
hartman committed
444
    msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
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
488
489
    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
490
491
492
493
494
495
496
497
            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
            }
498
499
500
501
502
503
504
505
506
507
            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;
    }
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 );
518
 
519
    /* Calculate framesizes and stuff */
520
521
522
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
523
 
hartman's avatar
hartman committed
524
    /* Set the desired format */
525
526
527
528
529
530
531
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
532
 
533
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
534
 
hartman's avatar
hartman committed
535
    /* Retrieve actual format */
536
537
538
539
540
541
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
542
 
543
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
544

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hartman's avatar
hartman committed
770
    if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
771
772
        return VLC_FALSE;

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

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

    return VLC_TRUE;
816
817
818
}


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

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

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

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


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

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

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

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

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

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

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

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

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

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

hartman's avatar
hartman committed
999
        if( !AudioDeviceHasOutput( p_devices[i]) )
hartman's avatar
hartman committed
1000
        {