auhal.c 59.7 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, 2011 the VideoLAN team
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
Antoine Cellerier's avatar
Antoine Cellerier 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>
Clément Stenac's avatar
Clément Stenac committed
37
#include <vlc_aout.h>
38 39

#include <CoreAudio/CoreAudio.h>
40
#include <AudioUnit/AudioUnit.h>
41
#include <AudioToolbox/AudioFormat.h>
42 43

#include <CoreServices/CoreServices.h>
44

45 46 47 48
#ifndef verify_noerr
#define verify_noerr(a) assert((a) == noErr)
#endif

49
#define STREAM_FORMAT_MSG( pre, sfm ) \
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 ) \
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

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.
Derk-Jan Hartman's avatar
Derk-Jan 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 */
Derk-Jan Hartman's avatar
Derk-Jan 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;
Derk-Jan Hartman's avatar
Derk-Jan 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 */
Derk-Jan Hartman's avatar
Derk-Jan 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

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 )
Rémi Duraffort's avatar
Rémi Duraffort 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

464 465
    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 ));
466

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

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

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

540
    /* Calculate framesizes and stuff */
541 542 543
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
544

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

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

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

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

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

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

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

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

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
589 590
    if( new_layout.mNumberChannelDescriptions > 0 )
        free( new_layout.mChannelDescriptions );
591

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

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

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

603
    return true;
604 605
}

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

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

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

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

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

634
    /* Set mixable to false if we are allowed to */
635
    AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
636
    err = AudioObjectIsPropertySettable( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable );
637 638
    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 );
639

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

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

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

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

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

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

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

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

692 693 694 695
        i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
        p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
        if( p_format_list == NULL )
            continue;
696

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

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

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

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

Rémi Duraffort's avatar
Rémi Duraffort committed
726
            if( !p_sys->b_revert )
727
            {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
728
                /* Retrieve the original format of this stream first if not done so already */
729
                i_param_size = sizeof( p_sys->sfmt_revert );
730
                err = AudioObjectGetPropertyData( p_sys->i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, &p_sys->sfmt_revert );
731
                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
            for( int j = 0; j < i_formats; j++ )
740 741 742
            {
                if( p_format_list[j].mFormatID == 'IAC3' ||
                      p_format_list[j].mFormatID == kAudioFormat60958AC3 )
743
                {
744
                    if( p_format_list[j].mSampleRate == p_aout->format.i_rate )
745
                    {
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
    if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
779
        p_aout->format.i_format = VLC_CODEC_SPDIFB;
780
    else
781 782 783 784 785 786
        p_aout->format.i_format = VLC_CODEC_SPDIFL;
    p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
    p_aout->format.i_frame_length = A52_FRAME_NB;
    p_aout->format.i_nb_samples = p_aout->format.i_frame_length;
    p_aout->format.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
    aout_FormatPrepare( &p_aout->format );
787 788 789
    aout_VolumeNoneInit( p_aout );

    /* Add IOProc callback */
790 791 792 793
    err = AudioDeviceCreateIOProcID( p_sys->i_selected_dev,
                                   (AudioDeviceIOProc)RenderCallbackSPDIF,
                                   (void *)p_aout,
                                   &p_sys->i_procID );
794 795
    if( err != noErr )
    {
796
        msg_Err( p_aout, "AudioDeviceCreateIOProcID 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
    p_sys->clock_diff += mdate();
804

805
    /* Start device */
806
    err = AudioDeviceStart( p_sys->i_selected_dev, p_sys->i_procID );
807 808 809 810
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );

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

820
    return true;
821 822 823
}


824 825 826 827 828
/*****************************************************************************
 * Close: Close HAL AudioUnit
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
829
    audio_output_t     *p_aout = (audio_output_t *)p_this;
830
    struct aout_sys_t   *p_sys = p_aout->sys;
831 832
    OSStatus            err = noErr;
    UInt32              i_param_size = 0;
833

834 835 836 837
    if( p_sys->au_unit )
    {
        verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
        verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
838
        verify_noerr( CloseComponent( p_sys->au_unit ) );
839
    }
840

841 842 843
    if( p_sys->b_digital )
    {
        /* Stop device */
844
        err = AudioDeviceStop( p_sys->i_selected_dev,
845
                               p_sys->i_procID );
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 = AudioDeviceDestroyIOProcID( p_sys->i_selected_dev,
                                          p_sys->i_procID );
854 855
        if( err != noErr )
        {
856
            msg_Err( p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err );
857
        }
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