auhal.c 61.3 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
 * 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
21 22
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28

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

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
35 36
#include <vlc_dialog.h>                   // dialog_Fatal
#include <vlc_aout.h>                     // aout_*
37
#include <vlc_aout_intf.h>
38

39
#include <AudioUnit/AudioUnit.h>          // AudioUnit
40
#include <CoreAudio/CoreAudio.h>      // AudioDeviceID
41
#include <AudioToolbox/AudioFormat.h>     // AudioFormatGetProperty
42
#include <CoreServices/CoreServices.h>
43

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

48
#define STREAM_FORMAT_MSG( pre, sfm ) \
49 50 51 52 53
    pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
    sfm.mSampleRate, (char *)&sfm.mFormatID, \
    (unsigned int)sfm.mFormatFlags, (unsigned int)sfm.mBytesPerPacket, \
    (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
    (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
54

55
#define FRAMESIZE 2048
56
#define BUFSIZE (FRAMESIZE * 8) * 8
57
#define AOUT_VAR_SPDIF_FLAG 0xf00000
58

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

66 67 68 69 70 71 72 73
/*****************************************************************************
 * 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
{
74
    aout_packet_t               packet;
75 76 77 78 79 80
    AudioDeviceID               i_default_dev;       /* DeviceID of defaultOutputDevice */
    AudioDeviceID               i_selected_dev;      /* DeviceID of the selected device */
    AudioDeviceIOProcID         i_procID;            /* DeviceID of current device */
    UInt32                      i_devices;           /* Number of CoreAudio Devices */
    bool                        b_digital;           /* Are we running in digital mode? */
    mtime_t                     clock_diff;          /* Difference between VLC clock and Device clock */
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
81

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

89
    /* CoreAudio SPDIF mode specific */
90 91 92 93 94 95 96
    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 */
    AudioStreamBasicDescription stream_format;       /* The format we changed the stream to */
    AudioStreamBasicDescription sfmt_revert;         /* The original format of the stream */
    bool                        b_revert;            /* Wether we need to revert the stream format */
    bool                        b_changed_mixing;    /* Wether we need to set the mixing mode back */
97 98 99 100 101 102
};

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

107
static void     Probe                   ( audio_output_t * );
108

109
static int      AudioDeviceHasOutput    ( AudioDeviceID );
110 111 112
static int      AudioDeviceSupportsDigital( audio_output_t *, AudioDeviceID );
static int      AudioStreamSupportsDigital( audio_output_t *, AudioStreamID );
static int      AudioStreamChangeFormat ( audio_output_t *, AudioStreamID, AudioStreamBasicDescription );
113 114 115

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

123 124
static int      VolumeSet               ( audio_output_t *, float );
static int      MuteSet                 ( audio_output_t *, bool );
Jon Stacey's avatar
Jon Stacey committed
125

126 127 128 129 130 131 132 133 134

/*****************************************************************************
 * 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.")

135 136 137 138 139 140 141
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 )
142
    add_integer( "macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false )
143
vlc_module_end ()
144 145

/*****************************************************************************
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
    vlc_value_t             val;
154
    audio_output_t          *p_aout = (audio_output_t *)p_this;
155

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

160
    /* Allocate structure */
161 162
    p_aout->sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->sys == NULL )
163
        return VLC_ENOMEM;
164

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

182 183 184
    p_aout->pf_play = aout_PacketPlay;
    p_aout->pf_pause = aout_PacketPause;
    p_aout->pf_flush = aout_PacketFlush;
185

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

188
    /* Persistent device variable */
189
    if( var_Type( p_aout->p_libvlc, "macosx-audio-device" ) == 0 )
190
    {
191
        var_Create( p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
192 193
    }

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

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
207
    p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
208 209
    bool b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG );
    if( b_supports_digital )
210
        msg_Dbg( p_aout, "audio device supports digital output" );
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
    i_param_size = sizeof( b_alive );
215 216 217 218
    AudioObjectPropertyAddress audioDeviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive,
                                              kAudioDevicePropertyScopeOutput,
                                              kAudioObjectPropertyElementMaster };
    err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &audioDeviceAliveAddress, 0, NULL, &i_param_size, &b_alive );
219 220 221

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

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

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

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

256 257 258
    /* If we change the device we want to use, we should renegotiate the audio chain */
    var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );

259
    /* Check for Digital mode or Analog output mode */
260
    if( AOUT_FMT_SPDIF( &p_aout->format ) && b_supports_digital )
261
    {
262
        if( OpenSPDIF( p_aout ) )
263 264
        {
            msg_Dbg( p_aout, "digital output successfully opened" );
265
            return VLC_SUCCESS;
266
        }
267 268 269 270
    }
    else
    {
        if( OpenAnalog( p_aout ) )
271 272
        {
            msg_Dbg( p_aout, "analog output successfully opened" );
273
            return VLC_SUCCESS;
274
        }
275
    }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
276 277 278

error:
    /* If we reach this, this aout has failed */
279
    msg_Err( p_aout, "opening the auhal output failed" );
280
    var_Destroy( p_aout, "audio-device" );
281
    free( p_sys );
282 283 284
    return VLC_EGENERIC;
}

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

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

307
    p_sys->au_component = FindNextComponent( NULL, &desc );
308 309
    if( p_sys->au_component == NULL )
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
310
        msg_Warn( p_aout, "we cannot find our HAL component" );
311
        return false;
312 313
    }

314
    err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
315
    if( err != noErr )
316
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
317
        msg_Warn( p_aout, "we cannot open our HAL component" );
318
        return false;
319
    }
320

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

329 330 331
    if( err != noErr )
    {
        msg_Warn( p_aout, "we cannot select the audio device" );
332
        return false;
333
    }
334

335 336 337
    /* Get the current format */
    i_param_size = sizeof(AudioStreamBasicDescription);

338
    err = AudioUnitGetProperty( p_sys->au_unit,
339 340 341 342
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
343
                                   &i_param_size );
344

345
    if( err != noErr ) return false;
346
    else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
347

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
348
    /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
349
    err = AudioUnitGetPropertyInfo( p_sys->au_unit,
350 351 352 353
                                   kAudioDevicePropertyPreferredChannelLayout,
                                   kAudioUnitScope_Output,
                                   0,
                                   &i_param_size,
354
                                   NULL );
355

356
    if( err == noErr )
357
    {
358 359 360 361 362 363 364 365
        layout = (AudioChannelLayout *)malloc( i_param_size);

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

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

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

387
        /* Initialize the VLC core channel count */
388 389
        p_aout->format.i_physical_channels = 0;
        i_original = p_aout->format.i_original_channels & AOUT_CHAN_PHYSMASK;
390

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

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

459 460
    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 ));
461

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

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

    /* We use float 32. It's the best supported format by both VLC and Coreaudio */
530
    p_aout->format.i_format = VLC_CODEC_FL32;
531 532
    DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
    DeviceFormat.mBitsPerChannel = 32;
533
    DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->format );
534

535
    /* Calculate framesizes and stuff */
536 537 538
    DeviceFormat.mFramesPerPacket = 1;
    DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
    DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
539

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
540
    /* Set the desired format */
541 542 543 544 545 546 547
    i_param_size = sizeof(AudioStreamBasicDescription);
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   i_param_size ));
548

549
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
550

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
551
    /* Retrieve actual format */
552 553 554 555 556 557
    verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &DeviceFormat,
                                   &i_param_size ));
558

559
    msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
560

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
561
    /* Do the last VLC aout setups */
562
    aout_FormatPrepare( &p_aout->format );
563
    aout_PacketInit( p_aout, &p_sys->packet, FRAMESIZE );
564 565
    p_aout->volume_set = VolumeSet;
    p_aout->mute_set = MuteSet;
566 567 568 569

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

571 572 573 574
    verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
                            kAudioUnitProperty_SetRenderCallback,
                            kAudioUnitScope_Input,
                            0, &input, sizeof( input ) ) );
575 576 577

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

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
585 586
    if( new_layout.mNumberChannelDescriptions > 0 )
        free( new_layout.mChannelDescriptions );
587

588 589 590
    /* AU initiliaze */
    verify_noerr( AudioUnitInitialize(p_sys->au_unit) );

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

    /* Start the AU */
597
    verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
598

599
    return true;
600 601
}

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

    /* Start doing the SPDIF setup proces */
615
    p_sys->b_digital = true;
616 617

    /* Hog the device */
618
    AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
619 620
    i_param_size = sizeof( p_sys->i_hog_pid );
    p_sys->i_hog_pid = getpid() ;
621

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

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

630
    AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
631

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

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

        if( err != noErr )
        {
            msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
            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
    i_streams = i_param_size / sizeof( AudioStreamID );
663 664
    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
    AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
677
    for( unsigned 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 */
Ron Frederick's avatar
Ron Frederick committed
680
        AudioStreamRangedDescription *p_format_list = NULL;
681 682
        int                          i_formats = 0;
        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, (int32_t)err );
689 690
            continue;
        }
691

Ron Frederick's avatar
Ron Frederick committed
692 693
        i_formats = i_param_size / sizeof( AudioStreamRangedDescription );
        p_format_list = (AudioStreamRangedDescription *)malloc( i_param_size );
694 695
        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
        {
Ron Frederick's avatar
Ron Frederick committed
708
            if( p_format_list[j].mFormat.mFormatID == 'IAC3' ||
709 710 711
               p_format_list[j].mFormat.mFormatID == 'iac3' ||
               p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
               p_format_list[j].mFormat.mFormatID == kAudioFormatAC3 )
712
            {
713
                b_digital = true;
714 715 716
                break;
            }
        }
717

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

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

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

742
            for( int j = 0; j < i_formats; j++ )
743
            {
Ron Frederick's avatar
Ron Frederick committed
744
                if( p_format_list[j].mFormat.mFormatID == 'IAC3' ||
745 746 747
                   p_format_list[j].mFormat.mFormatID == 'iac3' ||
                   p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
                   p_format_list[j].mFormat.mFormatID == kAudioFormatAC3 )
748
                {
Ron Frederick's avatar
Ron Frederick committed
749
                    if( p_format_list[j].mFormat.mSampleRate == p_aout->format.i_rate )
750
                    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
751
                        i_requested_rate_format = j;
752 753
                        break;
                    }
Ron Frederick's avatar
Ron Frederick committed
754
                    else if( p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate )
755
                    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
756
                        i_current_rate_format = j;
757 758 759
                    }
                    else
                    {
Ron Frederick's avatar
Ron Frederick committed
760
                        if( i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate )
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
761
                            i_backup_rate_format = j;
762
                    }
763
                }
764

765
            }
766

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
767
            if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
Ron Frederick's avatar
Ron Frederick committed
768
                p_sys->stream_format = p_format_list[i_requested_rate_format].mFormat;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
769
            else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
Ron Frederick's avatar
Ron Frederick committed
770 771
                p_sys->stream_format = p_format_list[i_current_rate_format].mFormat;
            else p_sys->stream_format = p_format_list[i_backup_rate_format].mFormat; /* And if we have to, any digital format will be just fine (highest rate possible) */
772
        }
773
        free( p_format_list );
774
    }
775
    free( p_streams );
776 777 778

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
779
    if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
780
        return false;
781

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
782
    /* Set the format flags */
783
    if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
784
        p_aout->format.i_format = VLC_CODEC_SPDIFB;
785
    else
786 787 788 789 790
        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_rate = (unsigned int)p_sys->stream_format.mSampleRate;
    aout_FormatPrepare( &p_aout->format );
791
    aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB );
792 793
    p_aout->volume_set = NULL;
    p_aout->mute_set = NULL;
794 795

    /* Add IOProc callback */
796 797 798 799
    err = AudioDeviceCreateIOProcID( p_sys->i_selected_dev,
                                   (AudioDeviceIOProc)RenderCallbackSPDIF,
                                   (void *)p_aout,
                                   &p_sys->i_procID );
800 801
    if( err != noErr )
    {
802
        msg_Err( p_aout, "AudioDeviceCreateIOProcID failed: [%4.4s]", (char *)&err );
803
        aout_PacketDestroy (p_aout);
804
        return false;
805 806 807 808
    }

    /* Check for the difference between the Device clock and mdate */
    p_sys->clock_diff = - (mtime_t)
809
        AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
810
    p_sys->clock_diff += mdate();
811

812
    /* Start device */
813
    err = AudioDeviceStart( p_sys->i_selected_dev, p_sys->i_procID );
814 815 816 817
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );

818 819
        err = AudioDeviceDestroyIOProcID( p_sys->i_selected_dev,
                                          p_sys->i_procID );
820 821
        if( err != noErr )
        {
822
            msg_Err( p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err );
823
        }
824
        aout_PacketDestroy (p_aout);
825
        return false;
826 827
    }

828
    return true;
829 830 831
}


832 833 834 835 836
/*****************************************************************************
 * Close: Close HAL AudioUnit
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
837
    audio_output_t     *p_aout = (audio_output_t *)p_this;
838
    struct aout_sys_t   *p_sys = p_aout->sys;
839 840
    OSStatus            err = noErr;
    UInt32              i_param_size = 0;
841

842 843 844 845
    if( p_sys->au_unit )
    {
        verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
        verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
846
        verify_noerr( CloseComponent( p_sys->au_unit ) );
847
    }
848

849 850 851
    if( p_sys->b_digital )
    {
        /* Stop device */
852
        err = AudioDeviceStop( p_sys->i_selected_dev,
853
                               p_sys->i_procID );
854 855 856 857 858
        if( err != noErr )
        {
            msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
        }

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
859
        /* Remove IOProc callback */
860 861
        err = AudioDeviceDestroyIOProcID( p_sys->i_selected_dev,
                                          p_sys->i_procID );
862 863
        if( err != noErr )
        {
864
            msg_Err( p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err );
865
        }
866

867 868
        if( p_sys->b_revert )
        {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
869
            AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->sfmt_revert );
870
        }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
871

872 873 874
        if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
        {
            int b_mix;
875
            Boolean b_writeable = false;
876
            /* Revert mixable to true if we are allowed to */
877
            AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
878
            err = AudioObjectIsPropertySettable( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable );
879
            err = AudioObjectGetPropertyData( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix );
880

881
            if( err == noErr && b_writeable )
882 883 884
            {
                msg_Dbg( p_aout, "mixable is: %d", b_mix );
                b_mix = 1;
885
                err = AudioObjectSetPropertyData( p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix );
886 887 888 889 890 891 892 893 894
            }

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

895 896
    AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
    err = AudioObjectRemovePropertyListener( kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, NULL );
897

898 899 900 901
    if( err != noErr )
    {
        msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
    }
902

903 904 905 906
    if( p_sys->i_hog_pid == getpid() )
    {
        p_sys->i_hog_pid = -1;
        i_param_size = sizeof( p_sys->i_hog_pid );
907 908 909 910
        AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
            kAudioDevicePropertyScopeOutput,
            kAudioObjectPropertyElementMaster };
        err = AudioObjectSetPropertyData( p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid );
911 912
        if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
    }
913

914 915
    var_DelCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );

916
    aout_PacketDestroy( p_aout );
917
    free( p_sys );
918 919 920
}

/*****************************************************************************
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
921
 * Probe: Check which devices the OS has, and add them to our audio-device menu
922
 *****************************************************************************/
923
static void Probe( audio_output_t * p_aout )
924 925
{
    OSStatus            err = noErr;
926
    UInt32              i_param_size = 0;
927
    AudioDeviceID       devid_def = 0;
928 929 930
    AudioDeviceID       *p_devices = NULL;
    vlc_value_t         val, text;

931
    struct aout_sys_t   *p_sys = p_aout->sys;
932 933

    /* Get number of devices */
934 935
    AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
    err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size);
936 937
    if( err != noErr )
    {
938
        msg_Err( p_aout, "Could not get number of devices: [%s]", (char *)&err );
939 940 941 942 943 944 945
        goto error;
    }

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

    if( p_sys->i_devices < 1 )
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
946
        msg_Err( p_aout, "No audio output devices were found." );
947 948
        goto error;
    }
949
    msg_Dbg( p_aout, "found %u audio device(s)", (unsigned)p_sys->i_devices );
950 951

    /* Allocate DeviceID array */
952
    p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
953 954 955 956
    if( p_devices == NULL )
        goto error;

    /* Populate DeviceID array */
957
    err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size, p_devices );
958 959
    if( err != noErr )
    {
960
        msg_Err( p_aout, "could not get the device IDs: [%s]", (char *)&err );
961 962 963 964
        goto error;
    }

    /* Find the ID of the default Device */
965
    AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
966
    i_param_size = sizeof( AudioDeviceID );
967
    err= AudioObjectGetPropertyData( kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &i_param_size, &devid_def );
968 969
    if( err != noErr )
    {
970
        msg_Err( p_aout, "could not get default audio device: [%s]", (char *)&err );
971 972 973
        goto error;
    }
    p_sys->i_default_dev = devid_def;
974

975
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
976
    text.psz_string = (char*)_("Audio Device");
977
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
978

979 980
    AudioObjectPropertyAddress deviceNameAddress = { kAudioDevicePropertyDeviceName, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };

981
    for( unsigned int i = 0; i < p_sys->i_devices; i++ )
982
    {
983 984
        char *psz_name;
        i_param_size = 0;
985

986
        /* Retrieve the length of the device name */
987
        err = AudioObjectGetPropertyDataSize( p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size );
988
        if( err ) goto error;
989

990 991
        /* Retrieve the name of the device */
        psz_name = (char *)malloc( i_param_size );
992
        err = AudioObjectGetPropertyData( p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size, psz_name );
993
        if( err ) goto error;
994

995
        msg_Dbg( p_aout, "DevID: %u DevName: %s", (unsigned)p_devices[i], psz_name );
996

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
997
        if( !AudioDeviceHasOutput( p_devices[i]) )
998 999
        {
            msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
1000
            free( psz_name );
1001 1002 1003
            continue;
        }

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
1004
        /* Add the menu entries */