auhal.c 59.8 KB
Newer Older
1
/*****************************************************************************
hartman's avatar
hartman committed
2
 * auhal.c: AUHAL and Coreaudio output plugin
3
 *****************************************************************************
4
 * Copyright (C) 2005, 2011 the VideoLAN team
hartman's avatar
hartman committed
5
 * $Id$
6 7
 *
 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8
 *          Felix Paul Kühne <fkuehne at videolan dot org>
9 10 11 12 13
 *
 * 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.
14
 *
15 16 17 18 19 20 21
 * 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
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32 33
#include <unistd.h>

34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
36
#include <vlc_dialog.h>
zorglub's avatar
zorglub committed
37
#include <vlc_aout.h>
38 39

#include <CoreAudio/CoreAudio.h>
Pierre's avatar
Pierre committed
40
#include <AudioUnit/AudioUnit.h>
41
#include <AudioToolbox/AudioFormat.h>
42 43

#include <CoreServices/CoreServices.h>
44

Pierre's avatar
Pierre committed
45 46 47 48
#ifndef verify_noerr
#define verify_noerr(a) assert((a) == noErr)
#endif

49
#define STREAM_FORMAT_MSG( pre, sfm ) \
Pierre's avatar
Pierre committed
50
    pre "[%u][%4.4s][%u][%u][%u][%u][%u][%u]", \
51 52 53 54 55 56
    (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 ) \
Pierre's avatar
Pierre committed
57
    pre ":\nsamplerate: [%u]\nFormatID: [%4.4s]\nFormatFlags: [%u]\nBypesPerPacket: [%u]\nFramesPerPacket: [%u]\nBytesPerFrame: [%u]\nChannelsPerFrame: [%u]\nBitsPerChannel[%u]", \
58 59 60 61 62
    (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
    sfm.mFormatFlags, sfm.mBytesPerPacket, \
    sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
    sfm.mChannelsPerFrame, sfm.mBitsPerChannel

Pierre's avatar
Pierre committed
63
#define FRAMESIZE 2048
64
#define BUFSIZE (FRAMESIZE * 8) * 8
65
#define AOUT_VAR_SPDIF_FLAG 0xf00000
66

67 68 69 70
/*
 * TODO:
 * - clean up the debug info
 * - be better at changing stream setup or devices setup changes while playing.
hartman's avatar
hartman committed
71
 * - fix 6.1 and 7.1
72 73
 */

74 75 76 77 78 79 80 81 82 83
/*****************************************************************************
 * 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 */
84
    AudioDeviceIOProcID         i_procID;       /* DeviceID of current device */
85
    UInt32                      i_devices;      /* Number of CoreAudio Devices */
86 87
    bool                        b_supports_digital;/* Does the currently selected device support digital mode? */
    bool                        b_digital;      /* Are we running in digital mode? */
88
    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
hartman's avatar
hartman committed
89

90
    /* AUHAL specific */
91
    Component                   au_component;   /* The Audiocomponent we use */
92
    AudioUnit                   au_unit;        /* The AudioUnit we use */
93
    uint8_t                     p_remainder_buffer[BUFSIZE];
94 95
    uint32_t                    i_read_bytes;
    uint32_t                    i_total_bytes;
hartman's avatar
hartman committed
96

97 98 99 100
    /* 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
101
    AudioStreamBasicDescription stream_format;  /* The format we changed the stream to */
102
    AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
103 104
    bool                        b_revert;       /* Wether we need to revert the stream format */
    bool                        b_changed_mixing;/* Wether we need to set the mixing mode back */
105 106 107 108 109 110
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int      Open                    ( vlc_object_t * );
111 112
static int      OpenAnalog              ( audio_output_t * );
static int      OpenSPDIF               ( audio_output_t * );
113 114
static void     Close                   ( vlc_object_t * );

115 116
static void     Play                    ( audio_output_t * );
static void     Probe                   ( audio_output_t * );
117

118
static int      AudioDeviceHasOutput    ( AudioDeviceID );
119 120 121
static int      AudioDeviceSupportsDigital( audio_output_t *, AudioDeviceID );
static int      AudioStreamSupportsDigital( audio_output_t *, AudioStreamID );
static int      AudioStreamChangeFormat ( audio_output_t *, AudioStreamID, AudioStreamBasicDescription );
122 123 124

static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
                                          unsigned int, unsigned int, AudioBufferList *);
125 126
static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
                                          AudioBufferList *, const AudioTimeStamp *, void * );
127 128
static OSStatus HardwareListener        ( AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void * );
static OSStatus StreamListener          ( AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void * );
129 130 131
static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
                                          vlc_value_t, vlc_value_t, void * );

132

Pierre's avatar
Pierre committed
133

134 135 136 137 138 139 140 141
/*****************************************************************************
 * 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.")

142 143 144 145 146 147 148
vlc_module_begin ()
    set_shortname( "auhal" )
    set_description( N_("HAL AudioUnit output") )
    set_capability( "audio output", 101 )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
    set_callbacks( Open, Close )
149
    add_integer( "macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false )
150
vlc_module_end ()
151 152

/*****************************************************************************
153
 * Open: open macosx audio output
154 155 156 157
 *****************************************************************************/
static int Open( vlc_object_t * p_this )
{
    OSStatus                err = noErr;
158
    UInt32                  i_param_size = 0;
159
    struct aout_sys_t       *p_sys = NULL;
160
    vlc_value_t             val;
161
    audio_output_t         *p_aout = (audio_output_t *)p_this;
162

163 164
    /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
     * property size */
165
    int                     b_alive = false;
166

167
    /* Allocate structure */
168 169
    p_aout->sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->sys == NULL )
ivoire's avatar
ivoire committed
170
        return VLC_ENOMEM;
171

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

190 191
    p_aout->pf_play = Play;
    p_aout->pf_pause = NULL;
192
    p_aout->pf_flush = NULL;
193

194
    aout_FormatPrint( p_aout, "VLC is looking for:", &p_aout->format );
195

196
    /* Persistent device variable */
197
    if( var_Type( p_aout->p_libvlc, "macosx-audio-device" ) == 0 )
198
    {
199
        var_Create( p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
200 201
    }

202 203 204
    /* Build a list of devices */
    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
205
        Probe( p_aout );
206
    }
207

208 209 210
    /* What device do we want? */
    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
211
        msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
hartman's avatar
hartman committed
212
        goto error;
213 214
    }

hartman's avatar
hartman committed
215
    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
216
    p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? true : false;
217
    if( p_sys->b_supports_digital )
218
        msg_Dbg( p_aout, "audio device supports digital output" );
219
    else
220
        msg_Dbg( p_aout, "audio device does not support digital output" );
221 222

    /* Check if the desired device is alive and usable */
hartman's avatar
hartman committed
223
    /* TODO: add a callback to the device to alert us if the device dies */
224
    i_param_size = sizeof( b_alive );
225 226 227 228
    AudioObjectPropertyAddress audioDeviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive,
                                              kAudioDevicePropertyScopeOutput,
                                              kAudioObjectPropertyElementMaster };
    err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &audioDeviceAliveAddress, 0, NULL, &i_param_size, &b_alive );
229 230 231

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

ivoire's avatar
ivoire committed
237
    if( !b_alive )
238
    {
239
        msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
240
        p_sys->i_selected_dev = p_sys->i_default_dev;
241
    }
242

243 244 245
    AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
                                  kAudioDevicePropertyScopeOutput,
                                  kAudioObjectPropertyElementMaster };
246
    i_param_size = sizeof( p_sys->i_hog_pid );
247
    err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, &i_param_size, &p_sys->i_hog_pid );
248 249
    if( err != noErr )
    {
250
        /* This is not a fatal error. Some drivers simply don't support this property */
hartman's avatar
hartman committed
251
        msg_Warn( p_aout, "could not check whether device is hogged: %4.4s",
252
                 (char *)&err );
253
        p_sys->i_hog_pid = -1;
254 255
    }

256
    if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
257
    {
hartman's avatar
hartman committed
258
        msg_Err( p_aout, "Selected audio device is exclusively in use by another program." );
Pierre's avatar
Pierre committed
259
        dialog_Fatal( p_aout, _("Audio output failed"), "%s",
260 261
                        _("The selected audio output device is exclusively in "
                          "use by another program.") );
hartman's avatar
hartman committed
262
        goto error;
263 264 265
    }

    /* Check for Digital mode or Analog output mode */
266
    if( AOUT_FMT_NON_LINEAR( &p_aout->format ) && p_sys->b_supports_digital )
267
    {
268
        if( OpenSPDIF( p_aout ) )
269 270
        {
            msg_Dbg( p_aout, "digital output successfully opened" );
271
            return VLC_SUCCESS;
272
        }
273 274 275 276
    }
    else
    {
        if( OpenAnalog( p_aout ) )
277 278
        {
            msg_Dbg( p_aout, "analog output successfully opened" );
279
            return VLC_SUCCESS;
280
        }
281
    }
hartman's avatar
hartman committed
282 283 284

error:
    /* If we reach this, this aout has failed */
285
    msg_Err( p_aout, "opening the auhal output failed" );
286
    var_Destroy( p_aout, "audio-device" );
ivoire's avatar
ivoire committed
287
    free( p_sys );
288 289 290
    return VLC_EGENERIC;
}

291
/*****************************************************************************
hartman's avatar
hartman committed
292
 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
293
 *****************************************************************************/
294
static int OpenAnalog( audio_output_t *p_aout )
295
{
296
    struct aout_sys_t           *p_sys = p_aout->sys;
hartman's avatar
hartman committed
297
    OSStatus                    err = noErr;
298
    UInt32                      i_param_size = 0;
299
    int                         i_original;
300
    ComponentDescription        desc;
hartman's avatar
hartman committed
301 302 303 304
    AudioStreamBasicDescription DeviceFormat;
    AudioChannelLayout          *layout;
    AudioChannelLayout          new_layout;
    AURenderCallbackStruct      input;
305 306 307 308 309 310 311 312

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

313
    p_sys->au_component = FindNextComponent( NULL, &desc );
314 315
    if( p_sys->au_component == NULL )
    {
hartman's avatar
hartman committed
316
        msg_Warn( p_aout, "we cannot find our HAL component" );
317
        return false;
318 319
    }

320
    err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
321
    if( err != noErr )
322
    {
hartman's avatar
hartman committed
323
        msg_Warn( p_aout, "we cannot open our HAL component" );
324
        return false;
325
    }
326

hartman's avatar
hartman committed
327
    /* Set the device we will use for this output unit */
328
    err = AudioUnitSetProperty( p_sys->au_unit,
329 330 331 332
                         kAudioOutputUnitProperty_CurrentDevice,
                         kAudioUnitScope_Global,
                         0,
                         &p_sys->i_selected_dev,
333
                         sizeof( AudioDeviceID ));
334

335 336 337
    if( err != noErr )
    {
        msg_Warn( p_aout, "we cannot select the audio device" );
338
        return false;
339
    }
340

341 342 343
    /* Get the current format */
    i_param_size = sizeof(AudioStreamBasicDescription);

344
    err = AudioUnitGetProperty( p_sys->au_unit,
345 346 347 348
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
349
                                   &i_param_size );
350

351
    if( err != noErr ) return false;
352
    else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
353

hartman's avatar
hartman committed
354
    /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
355
    err = AudioUnitGetPropertyInfo( p_sys->au_unit,
356 357 358 359
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   &i_param_size,
360
                                   NULL );
361

362
    if( err == noErr )
363
    {
364 365 366 367 368 369 370 371
        layout = (AudioChannelLayout *)malloc( i_param_size);

        verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                       kAudioDevicePropertyPreferredChannelLayout,
                                       kAudioUnitScope_Output,
                                       0,
                                       layout,
                                       &i_param_size ));
372

373 374
        /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
        if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
375
        {
376 377 378 379 380 381 382 383 384 385 386 387 388
            /* 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 ));
389
        }
390 391

        msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
392

393
        /* Initialize the VLC core channel count */
394 395
        p_aout->format.i_physical_channels = 0;
        i_original = p_aout->format.i_original_channels & AOUT_CHAN_PHYSMASK;
396

397 398 399
        if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
        {
            /* We only need Mono or cannot output more than 1 channel */
400
            p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
401
        }
402
        else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
hartman's avatar
hartman committed
403
        {
404
            /* We only need Stereo or cannot output more than 2 channels */
405
            p_aout->format.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
hartman's avatar
hartman committed
406
        }
407 408 409
        else
        {
            /* We want more than stereo and we can do that */
410
            for( unsigned int i = 0; i < layout->mNumberChannelDescriptions; i++ )
411 412 413 414 415 416
            {
                msg_Dbg( p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );

                switch( layout->mChannelDescriptions[i].mChannelLabel )
                {
                    case kAudioChannelLabel_Left:
417
                        p_aout->format.i_physical_channels |= AOUT_CHAN_LEFT;
418 419
                        continue;
                    case kAudioChannelLabel_Right:
420
                        p_aout->format.i_physical_channels |= AOUT_CHAN_RIGHT;
421 422
                        continue;
                    case kAudioChannelLabel_Center:
423
                        p_aout->format.i_physical_channels |= AOUT_CHAN_CENTER;
424 425
                        continue;
                    case kAudioChannelLabel_LFEScreen:
426
                        p_aout->format.i_physical_channels |= AOUT_CHAN_LFE;
427 428
                        continue;
                    case kAudioChannelLabel_LeftSurround:
429
                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARLEFT;
430 431
                        continue;
                    case kAudioChannelLabel_RightSurround:
432
                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARRIGHT;
433 434
                        continue;
                    case kAudioChannelLabel_RearSurroundLeft:
435
                        p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
436 437
                        continue;
                    case kAudioChannelLabel_RearSurroundRight:
438
                        p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
439 440
                        continue;
                    case kAudioChannelLabel_CenterSurround:
441
                        p_aout->format.i_physical_channels |= AOUT_CHAN_REARCENTER;
442 443
                        continue;
                    default:
444
                        msg_Warn( p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
445 446
                }
            }
447
            if( p_aout->format.i_physical_channels == 0 )
448
            {
449
                p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
450
                msg_Err( p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
Pierre's avatar
Pierre committed
451
                dialog_Fatal( p_aout, _("Audio device is not configured"), "%s",
452
                                _("You should configure your speaker layout with "
453
                                  "the \"Audio Midi Setup\" utility in /Applications/"
454
                                  "Utilities. Stereo mode is being used now.") );
455 456
            }
        }
ivoire's avatar
ivoire committed
457
        free( layout );
458 459 460 461
    }
    else
    {
        msg_Warn( p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
462
        p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
463 464
    }

465 466
    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->format ) );
    msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->format ));
467

468
    memset (&new_layout, 0, sizeof(new_layout));
469
    switch( aout_FormatNbChannels( &p_aout->format ) )
470 471 472 473 474 475 476 477
    {
        case 1:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
            break;
        case 2:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
            break;
        case 3:
478
            if( p_aout->format.i_physical_channels & AOUT_CHAN_CENTER )
479 480 481
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
            }
482
            else if( p_aout->format.i_physical_channels & AOUT_CHAN_LFE )
483 484 485 486 487
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
            }
            break;
        case 4:
488
            if( p_aout->format.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
489 490 491
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
            }
492
            else if( p_aout->format.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
493 494 495
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
            }
496
            else if( p_aout->format.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
497 498 499 500 501
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
            }
            break;
        case 5:
502
            if( p_aout->format.i_physical_channels & ( AOUT_CHAN_CENTER ) )
503 504 505
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
            }
506
            else if( p_aout->format.i_physical_channels & ( AOUT_CHAN_LFE ) )
507 508 509 510 511
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
            }
            break;
        case 6:
512
            if( p_aout->format.i_physical_channels & ( AOUT_CHAN_LFE ) )
hartman's avatar
hartman committed
513 514 515 516 517 518 519
            {
                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
            }
520 521 522 523 524 525 526 527 528 529
            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;
    }
530 531

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

    /* We use float 32. It's the best supported format by both VLC and Coreaudio */
536
    p_aout->format.i_format = VLC_CODEC_FL32;
537 538
    DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
    DeviceFormat.mBitsPerChannel = 32;
539
    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->format );
540

541
    /* Calculate framesizes and stuff */
542 543 544
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
Pierre's avatar
Pierre committed
545

hartman's avatar
hartman committed
546
    /* Set the desired format */
547 548 549 550 551 552 553
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
554

555
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
556

hartman's avatar
hartman committed
557
    /* Retrieve actual format */
558 559 560 561 562 563
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
564

565
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
566

hartman's avatar
hartman committed
567
    /* Do the last VLC aout setups */
568 569
    aout_FormatPrepare( &p_aout->format );
    p_aout->i_nb_samples = FRAMESIZE;
570 571 572 573 574
    aout_VolumeSoftInit( p_aout );

    /* set the IOproc callback */
    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
575

576 577 578 579
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Input,
                            0, &input, sizeof( input ) ) );
580 581 582

    input.inputProc = (AURenderCallback) RenderCallbackAnalog;
    input.inputProcRefCon = p_aout;
583

hartman's avatar
hartman committed
584
    /* Set the new_layout as the layout VLC will use to feed the AU unit */
585 586 587 588
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_AudioChannelLayout,
                            kAudioUnitScope_Input,
                            0, &new_layout, sizeof(new_layout) ) );
589

hartman's avatar
hartman committed
590 591
    if( new_layout.mNumberChannelDescriptions > 0 )
        free( new_layout.mChannelDescriptions );
592

593 594 595
    /* AU initiliaze */
    verify_noerr( AudioUnitInitialize(p_sys->au_unit) );

hartman's avatar
hartman committed
596 597
    /* Find the difference between device clock and mdate clock */
    p_sys->clock_diff = - (mtime_t)
598
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
hartman's avatar
hartman committed
599 600 601
    p_sys->clock_diff += mdate();

    /* Start the AU */
602
    verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
603

604
    return true;
605 606
}

607 608 609
/*****************************************************************************
 * Setup a encoded digital stream (SPDIF)
 *****************************************************************************/
610
static int OpenSPDIF( audio_output_t * p_aout )
611
{
612
    struct aout_sys_t       *p_sys = p_aout->sys;
613 614
    OSStatus                err = noErr;
    UInt32                  i_param_size = 0, b_mix = 0;
615
    Boolean                 b_writeable = false;
616
    AudioStreamID           *p_streams = NULL;
617
    int                     i_streams = 0;
618 619

    /* Start doing the SPDIF setup proces */
620
    p_sys->b_digital = true;
621 622

    /* Hog the device */
623
    AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
624 625
    i_param_size = sizeof( p_sys->i_hog_pid );
    p_sys->i_hog_pid = getpid() ;
626

627
    err = AudioObjectSetPropertyData( p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid );
628

629 630
    if( err != noErr )
    {
hartman's avatar
hartman committed
631
        msg_Err( p_aout, "failed to set hogmode: [%4.4s]", (char *)&err );
632
        return false;
633
    }
634

635
    /* Set mixable to false if we are allowed to */
636
    AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
637
    err = AudioObjectIsPropertySettable( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable );
638 639
    err = AudioObjectGetPropertyDataSize( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size );
    err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix );
640

641 642 643
    if( !err && b_writeable )
    {
        b_mix = 0;
644
        err = AudioObjectSetPropertyData( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix );
645
        p_sys->b_changed_mixing = true;
646
    }
647

648 649 650
    if( err != noErr )
    {
        msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
651
        return false;
652 653
    }

hartman's avatar
hartman committed
654
    /* Get a list of all the streams on this device */
655 656
    AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
    err = AudioObjectGetPropertyDataSize( p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size );
657 658 659
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
660
        return false;
661
    }
662

663 664 665
    i_streams = i_param_size / sizeof( AudioStreamID );
    p_streams = (AudioStreamID *)malloc( i_param_size );
    if( p_streams == NULL )
666
        return false;
667

668
    err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams );
669

670 671 672
    if( err != noErr )
    {
        msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
ivoire's avatar
ivoire committed
673
        free( p_streams );
674
        return false;
675 676
    }

677 678
    AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
    for( int i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
679
    {
hartman's avatar
hartman committed
680
        /* Find a stream with a cac3 stream */
Ron Frederick's avatar
Ron Frederick committed
681
        AudioStreamRangedDescription *p_format_list = NULL;
682
        int                         i_formats = 0;
683
        bool                  b_digital = false;
684

685
        /* Retrieve all the stream formats supported by each output stream */
686
        err = AudioObjectGetPropertyDataSize( p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size );
687 688
        if( err != noErr )
        {
689
            msg_Err( p_aout, "OpenSPDIF: could not get number of streamformats: [%s] (%i)", (char *)&err, err );
690 691
            continue;
        }
692

Ron Frederick's avatar
Ron Frederick committed
693 694
        i_formats = i_param_size / sizeof( AudioStreamRangedDescription );
        p_format_list = (AudioStreamRangedDescription *)malloc( i_param_size );
695 696
        if( p_format_list == NULL )
            continue;
697

698
        err = AudioObjectGetPropertyData( p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list );
699 700 701
        if( err != noErr )
        {
            msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
ivoire's avatar
ivoire committed
702
            free( p_format_list );
703 704 705
            continue;
        }

hartman's avatar
hartman committed
706
        /* Check if one of the supported formats is a digital format */
707
        for( int j = 0; j < i_formats; j++ )
708
        {
Ron Frederick's avatar
Ron Frederick committed
709 710
            if( p_format_list[j].mFormat.mFormatID == 'IAC3' ||
                  p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 )
711
            {
712
                b_digital = true;
713 714 715
                break;
            }
        }
716

717 718
        if( b_digital )
        {
hartman's avatar
hartman committed
719 720 721 722 723
            /* 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;

724 725 726
            p_sys->i_stream_id = p_streams[i];
            p_sys->i_stream_index = i;

ivoire's avatar
ivoire committed
727
            if( !p_sys->b_revert )
728
            {
hartman's avatar
hartman committed
729
                /* Retrieve the original format of this stream first if not done so already */
730
                i_param_size = sizeof( p_sys->sfmt_revert );
731
                err = AudioObjectGetPropertyData( p_sys->i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, &p_sys->sfmt_revert );