auhal.c 58.5 KB
Newer Older
1
/*****************************************************************************
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
2
 * auhal.c: AUHAL and Coreaudio output plugin
3
 *****************************************************************************
4
 * Copyright (C) 2005 the VideoLAN team
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
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26
 *****************************************************************************/

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

31 32
#include <unistd.h>

33
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac 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.
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
65
 * - fix 6.1 and 7.1
66 67
 */

68 69 70 71 72 73 74 75 76 77 78
/*****************************************************************************
 * aout_sys_t: private audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the CoreAudio specific properties of an output thread.
 *****************************************************************************/
struct aout_sys_t
{
    AudioDeviceID               i_default_dev;  /* Keeps DeviceID of defaultOutputDevice */
    AudioDeviceID               i_selected_dev; /* Keeps DeviceID of the selected device */
    UInt32                      i_devices;      /* Number of CoreAudio Devices */
79 80
    bool                  b_supports_digital;/* Does the currently selected device support digital mode? */
    bool                  b_digital;      /* Are we running in digital mode? */
81
    mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
Derk-Jan Hartman's avatar
Derk-Jan 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;
Derk-Jan Hartman's avatar
Derk-Jan 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 */
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
94
    AudioStreamBasicDescription stream_format;  /* The format we changed the stream to */
95
    AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
96 97
    bool                  b_revert;       /* Wether we need to revert the stream format */
    bool                  b_changed_mixing;/* Wether we need to set the mixing mode back */
98 99 100 101 102 103
};

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

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

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

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

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

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define ADEV_TEXT N_("Audio Device")
#define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
    "audio device, as listed in your 'Audio Device' menu. This device will " \
    "then be used by default for audio playback.")

vlc_module_begin();
    set_shortname( "auhal" );
    set_description( _("HAL AudioUnit output") );
138
    set_capability( "audio output", 101 );
139 140 141
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
    set_callbacks( Open, Close );
142
    add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, false );
143 144 145
vlc_module_end();

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

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

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

168 169 170 171
    p_sys = p_aout->output.p_sys;
    p_sys->i_default_dev = 0;
    p_sys->i_selected_dev = 0;
    p_sys->i_devices = 0;
172 173
    p_sys->b_supports_digital = false;
    p_sys->b_digital = false;
174 175 176 177 178
    p_sys->au_component = NULL;
    p_sys->au_unit = NULL;
    p_sys->clock_diff = (mtime_t) 0;
    p_sys->i_read_bytes = 0;
    p_sys->i_total_bytes = 0;
179 180
    p_sys->i_hog_pid = -1;
    p_sys->i_stream_id = 0;
181
    p_sys->i_stream_index = -1;
182 183
    p_sys->b_revert = false;
    p_sys->b_changed_mixing = false;
184
    memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
185 186

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    p_sys->au_component = FindNextComponent( NULL, &desc );
    if( p_sys->au_component == NULL )
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
298
        msg_Warn( p_aout, "we cannot find our HAL component" );
299
        return false;
300 301 302
    }

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

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

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

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

        verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                       kAudioDevicePropertyPreferredChannelLayout,
                                       kAudioUnitScope_Output,
                                       0,
                                       layout,
                                       &i_param_size ));
354
 
355 356
        /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
        if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
357
        {
358 359 360 361 362 363 364 365 366 367 368 369 370
            /* bitmap defined channellayout */
            verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
                                    sizeof( UInt32), &layout->mChannelBitmap,
                                    &i_param_size,
                                    layout ));
        }
        else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
        {
            /* layouttags defined channellayout */
            verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
                                    sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
                                    &i_param_size,
                                    layout ));
371
        }
372 373

        msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
374
 
375 376 377
        /* Initialize the VLC core channel count */
        p_aout->output.output.i_physical_channels = 0;
        i_original = p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK;
378
 
379 380 381 382
        if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
        {
            /* We only need Mono or cannot output more than 1 channel */
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
383
        }
384
        else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
385
        {
386 387
            /* We only need Stereo or cannot output more than 2 channels */
            p_aout->output.output.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
388
        }
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
        else
        {
            /* We want more than stereo and we can do that */
            for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
            {
                msg_Dbg( p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );

                switch( layout->mChannelDescriptions[i].mChannelLabel )
                {
                    case kAudioChannelLabel_Left:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
                        continue;
                    case kAudioChannelLabel_Right:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
                        continue;
                    case kAudioChannelLabel_Center:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
                        continue;
                    case kAudioChannelLabel_LFEScreen:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
                        continue;
                    case kAudioChannelLabel_LeftSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
                        continue;
                    case kAudioChannelLabel_RightSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
                        continue;
                    case kAudioChannelLabel_RearSurroundLeft:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
                        continue;
                    case kAudioChannelLabel_RearSurroundRight:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
                        continue;
                    case kAudioChannelLabel_CenterSurround:
                        p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
                        continue;
                    default:
426
                        msg_Warn( p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
427 428 429 430 431 432
                }
            }
            if( p_aout->output.output.i_physical_channels == 0 )
            {
                p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
                msg_Err( p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
433
                intf_UserFatal( p_aout, false, _("Audio device is not configured"),
434
                                _("You should configure your speaker layout with "
435
                                  "the \"Audio Midi Setup\" utility in /Applications/"
436
                                  "Utilities. Stereo mode is being used now.") );
437 438
            }
        }
439
        free( layout );
440 441 442 443 444
    }
    else
    {
        msg_Warn( p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
        p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
445 446
    }

447
    msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
448
    msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
449

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
    memset (&new_layout, 0, sizeof(new_layout));
    switch( aout_FormatNbChannels( &p_aout->output.output ) )
    {
        case 1:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
            break;
        case 2:
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
            break;
        case 3:
            if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
            }
            else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
            }
            break;
        case 4:
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
            }
            break;
        case 5:
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
            }
            else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
            }
            break;
        case 6:
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
494 495 496 497 498 499 500 501
            if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
            }
            else
            {
                new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
            }
502 503 504 505 506 507 508 509 510 511
            break;
        case 7:
            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
            break;
        case 8:
            /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
            new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
            break;
    }
512 513 514 515 516 517 518 519 520 521

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

    /* We use float 32. It's the best supported format by both VLC and Coreaudio */
    p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
    DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
    DeviceFormat.mBitsPerChannel = 32;
    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
522
 
523
    /* Calculate framesizes and stuff */
524 525 526
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
527
 
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
528
    /* Set the desired format */
529 530 531 532 533 534 535
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
536
 
537
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
538
 
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
539
    /* Retrieve actual format */
540 541 542 543 544 545
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
546
 
547
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
548

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

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

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

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

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

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

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

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

617 618 619 620 621 622
    /* Set mixable to false if we are allowed to */
    err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                    &i_param_size, &b_writeable );

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

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

667
    for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
668
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
669
        /* Find a stream with a cac3 stream */
670 671
        AudioStreamBasicDescription *p_format_list = NULL;
        int                         i_formats = 0, j = 0;
672
        bool                  b_digital = false;
673
 
674 675 676 677 678 679 680 681 682
        /* Retrieve all the stream formats supported by each output stream */
        err = AudioStreamGetPropertyInfo( p_streams[i], 0,
                                          kAudioStreamPropertyPhysicalFormats,
                                          &i_param_size, NULL );
        if( err != noErr )
        {
            msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
            continue;
        }
683
 
684 685 686 687
        i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
        p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
        if( p_format_list == NULL )
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
688
            msg_Err( p_aout, "could not malloc the memory" );
689 690
            continue;
        }
691
 
692 693 694 695 696 697
        err = AudioStreamGetProperty( p_streams[i], 0,
                                          kAudioStreamPropertyPhysicalFormats,
                                          &i_param_size, p_format_list );
        if( err != noErr )
        {
            msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
698
            free( p_format_list );
699 700 701
            continue;
        }

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
702
        /* Check if one of the supported formats is a digital format */
703 704 705 706 707
        for( j = 0; j < i_formats; j++ )
        {
            if( p_format_list[j].mFormatID == 'IAC3' ||
                  p_format_list[j].mFormatID == kAudioFormat60958AC3 )
            {
708
                b_digital = true;
709 710 711
                break;
            }
        }
712
 
713 714
        if( b_digital )
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
715 716 717 718 719
            /* if this stream supports a digital (cac3) format, then go set it. */
            int i_requested_rate_format = -1;
            int i_current_rate_format = -1;
            int i_backup_rate_format = -1;

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

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

739 740 741 742
            for( j = 0; j < i_formats; j++ )
            {
                if( p_format_list[j].mFormatID == 'IAC3' ||
                      p_format_list[j].mFormatID == kAudioFormat60958AC3 )
743
                {
744 745
                    if( p_format_list[j].mSampleRate == p_aout->output.output.i_rate )
                    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
746
                        i_requested_rate_format = j;
747 748 749 750
                        break;
                    }
                    else if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
                    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
751
                        i_current_rate_format = j;
752 753 754
                    }
                    else
                    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
755 756
                        if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
                            i_backup_rate_format = j;
757
                    }
758
                }
759
 
760
            }
761
 
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
762 763 764 765 766
            if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
                p_sys->stream_format = p_format_list[i_requested_rate_format];
            else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
                p_sys->stream_format = p_format_list[i_current_rate_format];
            else p_sys->stream_format = p_format_list[i_backup_rate_format]; /* And if we have to, any digital format will be just fine (highest rate possible) */
767
        }
768
        free( p_format_list );
769
    }
770
    free( p_streams );
771 772 773

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

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
777
    /* Set the format flags */
778 779 780 781 782 783 784 785
    if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
    else
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
    p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
    p_aout->output.output.i_frame_length = A52_FRAME_NB;
    p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
    p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
786
    aout_FormatPrepare( &p_aout->output.output );
787 788 789
    aout_VolumeNoneInit( p_aout );

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

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

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

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

820
    return true;
821 822 823
}


824 825 826 827 828 829 830
/*****************************************************************************
 * Close: Close HAL AudioUnit
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    aout_instance_t     *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t   *p_sys = p_aout->output.p_sys;
831 832
    OSStatus            err = noErr;
    UInt32              i_param_size = 0;
833
 
834 835 836 837 838 839
    if( p_sys->au_unit )
    {
        verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
        verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
        verify_noerr( CloseComponent( p_sys->au_unit ) );
    }
840
 
841 842 843
    if( p_sys->b_digital )
    {
        /* Stop device */
844 845
        err = AudioDeviceStop( p_sys->i_selected_dev,
                               (AudioDeviceIOProc)RenderCallbackSPDIF );
846 847 848 849 850
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
        }

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

864 865 866 867 868 869 870 871 872 873
        if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
        {
            int b_mix;
            Boolean b_writeable;
            /* Revert mixable to true if we are allowed to */
            err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                        &i_param_size, &b_writeable );

            err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
                                        &i_param_size, &b_mix );
874
 
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
            if( !err && b_writeable )
            {
                msg_Dbg( p_aout, "mixable is: %d", b_mix );
                b_mix = 1;
                err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                    kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
            }

            if( err != noErr )
            {
                msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
            }
        }
    }

    err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
                                               HardwareListener );
892
 
893 894 895 896
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
    }
897
 
898 899 900 901 902 903 904 905
    if( p_sys->i_hog_pid == getpid() )
    {
        p_sys->i_hog_pid = -1;
        i_param_size = sizeof( p_sys->i_hog_pid );
        err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
                                         kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
        if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
    }
906
 
907
    free( p_sys );
908 909 910 911 912 913 914 915 916 917 918
}

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


/*****************************************************************************
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
919
 * Probe: Check which devices the OS has, and add them to our audio-device menu
920
 *****************************************************************************/
921
static void Probe( aout_instance_t * p_aout )