1 /*****************************************************************************
2 * auhal.c: AUHAL and Coreaudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
38 #include <CoreAudio/CoreAudio.h>
39 #include <AudioUnit/AudioUnitProperties.h>
40 #include <AudioUnit/AudioUnitParameters.h>
41 #include <AudioUnit/AudioOutputUnit.h>
42 #include <AudioToolbox/AudioFormat.h>
44 #define STREAM_FORMAT_MSG( pre, sfm ) \
45 pre "[%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
46 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
47 sfm.mFormatFlags, sfm.mBytesPerPacket, \
48 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
49 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
51 #define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
52 pre ":\nsamplerate: [%ld]\nFormatID: [%4.4s]\nFormatFlags: [%ld]\nBypesPerPacket: [%ld]\nFramesPerPacket: [%ld]\nBytesPerFrame: [%ld]\nChannelsPerFrame: [%ld]\nBitsPerChannel[%ld]", \
53 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
54 sfm.mFormatFlags, sfm.mBytesPerPacket, \
55 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
56 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
58 #define BUFSIZE 0xffffff
59 #define AOUT_VAR_SPDIF_FLAG 0xf00000
63 * - clean up the debug info
65 * - be better at changing stream setup or devices setup changes while playing.
69 /*****************************************************************************
70 * aout_sys_t: private audio output method descriptor
71 *****************************************************************************
72 * This structure is part of the audio output thread descriptor.
73 * It describes the CoreAudio specific properties of an output thread.
74 *****************************************************************************/
77 AudioDeviceID i_default_dev
; /* Keeps DeviceID of defaultOutputDevice */
78 AudioDeviceID i_selected_dev
; /* Keeps DeviceID of the selected device */
79 UInt32 i_devices
; /* Number of CoreAudio Devices */
80 bool b_supports_digital
;/* Does the currently selected device support digital mode? */
81 bool b_digital
; /* Are we running in digital mode? */
82 mtime_t clock_diff
; /* Difference between VLC clock and Device clock */
85 Component au_component
; /* The Audiocomponent we use */
86 AudioUnit au_unit
; /* The AudioUnit we use */
87 uint8_t p_remainder_buffer
[BUFSIZE
];
88 uint32_t i_read_bytes
;
89 uint32_t i_total_bytes
;
91 /* CoreAudio SPDIF mode specific */
92 pid_t i_hog_pid
; /* The keep the pid of our hog status */
93 AudioStreamID i_stream_id
; /* The StreamID that has a cac3 streamformat */
94 int i_stream_index
; /* The index of i_stream_id in an AudioBufferList */
95 AudioStreamBasicDescription stream_format
; /* The format we changed the stream to */
96 AudioStreamBasicDescription sfmt_revert
; /* The original format of the stream */
97 bool b_revert
; /* Wether we need to revert the stream format */
98 bool b_changed_mixing
;/* Wether we need to set the mixing mode back */
101 /*****************************************************************************
103 *****************************************************************************/
104 static int Open ( vlc_object_t
* );
105 static int OpenAnalog ( aout_instance_t
* );
106 static int OpenSPDIF ( aout_instance_t
* );
107 static void Close ( vlc_object_t
* );
109 static void Play ( aout_instance_t
* );
110 static void Probe ( aout_instance_t
* );
112 static int AudioDeviceHasOutput ( AudioDeviceID
);
113 static int AudioDeviceSupportsDigital( aout_instance_t
*, AudioDeviceID
);
114 static int AudioStreamSupportsDigital( aout_instance_t
*, AudioStreamID
);
115 static int AudioStreamChangeFormat ( aout_instance_t
*, AudioStreamID
, AudioStreamBasicDescription
);
117 static OSStatus
RenderCallbackAnalog ( vlc_object_t
*, AudioUnitRenderActionFlags
*, const AudioTimeStamp
*,
118 unsigned int, unsigned int, AudioBufferList
*);
119 static OSStatus
RenderCallbackSPDIF ( AudioDeviceID
, const AudioTimeStamp
*, const void *, const AudioTimeStamp
*,
120 AudioBufferList
*, const AudioTimeStamp
*, void * );
121 static OSStatus
HardwareListener ( AudioHardwarePropertyID
, void *);
122 static OSStatus
StreamListener ( AudioStreamID
, UInt32
,
123 AudioDevicePropertyID
, void * );
124 static int AudioDeviceCallback ( vlc_object_t
*, const char *,
125 vlc_value_t
, vlc_value_t
, void * );
128 /*****************************************************************************
130 *****************************************************************************/
131 #define ADEV_TEXT N_("Audio Device")
132 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
133 "audio device, as listed in your 'Audio Device' menu. This device will " \
134 "then be used by default for audio playback.")
137 set_shortname( "auhal" );
138 set_description( N_("HAL AudioUnit output") );
139 set_capability( "audio output", 101 );
140 set_category( CAT_AUDIO
);
141 set_subcategory( SUBCAT_AUDIO_AOUT
);
142 set_callbacks( Open
, Close
);
143 add_integer( "macosx-audio-device", 0, NULL
, ADEV_TEXT
, ADEV_LONGTEXT
, false );
146 /*****************************************************************************
147 * Open: open macosx audio output
148 *****************************************************************************/
149 static int Open( vlc_object_t
* p_this
)
151 OSStatus err
= noErr
;
152 UInt32 i_param_size
= 0;
153 struct aout_sys_t
*p_sys
= NULL
;
155 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
157 /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
161 /* Allocate structure */
162 p_aout
->output
.p_sys
= malloc( sizeof( aout_sys_t
) );
163 if( p_aout
->output
.p_sys
== NULL
)
165 msg_Err( p_aout
, "out of memory" );
166 return( VLC_ENOMEM
);
169 p_sys
= p_aout
->output
.p_sys
;
170 p_sys
->i_default_dev
= 0;
171 p_sys
->i_selected_dev
= 0;
172 p_sys
->i_devices
= 0;
173 p_sys
->b_supports_digital
= false;
174 p_sys
->b_digital
= false;
175 p_sys
->au_component
= NULL
;
176 p_sys
->au_unit
= NULL
;
177 p_sys
->clock_diff
= (mtime_t
) 0;
178 p_sys
->i_read_bytes
= 0;
179 p_sys
->i_total_bytes
= 0;
180 p_sys
->i_hog_pid
= -1;
181 p_sys
->i_stream_id
= 0;
182 p_sys
->i_stream_index
= -1;
183 p_sys
->b_revert
= false;
184 p_sys
->b_changed_mixing
= false;
185 memset( p_sys
->p_remainder_buffer
, 0, sizeof(uint8_t) * BUFSIZE
);
187 p_aout
->output
.pf_play
= Play
;
189 aout_FormatPrint( p_aout
, "VLC is looking for:", (audio_sample_format_t
*)&p_aout
->output
.output
);
191 /* Persistent device variable */
192 if( var_Type( p_aout
->p_libvlc
, "macosx-audio-device" ) == 0 )
194 var_Create( p_aout
->p_libvlc
, "macosx-audio-device", VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
197 /* Build a list of devices */
198 if( var_Type( p_aout
, "audio-device" ) == 0 )
203 /* What device do we want? */
204 if( var_Get( p_aout
, "audio-device", &val
) < 0 )
206 msg_Err( p_aout
, "audio-device var does not exist. device probe failed." );
210 p_sys
->i_selected_dev
= val
.i_int
& ~AOUT_VAR_SPDIF_FLAG
; /* remove SPDIF flag to get the true DeviceID */
211 p_sys
->b_supports_digital
= ( val
.i_int
& AOUT_VAR_SPDIF_FLAG
) ? true : false;
213 /* Check if the desired device is alive and usable */
214 /* TODO: add a callback to the device to alert us if the device dies */
215 i_param_size
= sizeof( b_alive
);
216 err
= AudioDeviceGetProperty( p_sys
->i_selected_dev
, 0, FALSE
,
217 kAudioDevicePropertyDeviceIsAlive
,
218 &i_param_size
, &b_alive
);
222 /* Be tolerant, only give a warning here */
223 msg_Warn( p_aout
, "could not check whether device [0x%x] is alive: %4.4s", (unsigned int)p_sys
->i_selected_dev
, (char *)&err
);
227 if( b_alive
== false )
229 msg_Warn( p_aout
, "selected audio device is not alive, switching to default device" );
230 p_sys
->i_selected_dev
= p_sys
->i_default_dev
;
233 i_param_size
= sizeof( p_sys
->i_hog_pid
);
234 err
= AudioDeviceGetProperty( p_sys
->i_selected_dev
, 0, FALSE
,
235 kAudioDevicePropertyHogMode
,
236 &i_param_size
, &p_sys
->i_hog_pid
);
240 /* This is not a fatal error. Some drivers simply don't support this property */
241 msg_Warn( p_aout
, "could not check whether device is hogged: %4.4s",
243 p_sys
->i_hog_pid
= -1;
246 if( p_sys
->i_hog_pid
!= -1 && p_sys
->i_hog_pid
!= getpid() )
248 msg_Err( p_aout
, "Selected audio device is exclusively in use by another program." );
249 intf_UserFatal( p_aout
, false, _("Audio output failed"),
250 _("The selected audio output device is exclusively in "
251 "use by another program.") );
255 /* Check for Digital mode or Analog output mode */
256 if( AOUT_FMT_NON_LINEAR( &p_aout
->output
.output
) && p_sys
->b_supports_digital
)
258 if( OpenSPDIF( p_aout
) )
263 if( OpenAnalog( p_aout
) )
268 /* If we reach this, this aout has failed */
269 var_Destroy( p_aout
, "audio-device" );
274 /*****************************************************************************
275 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
276 *****************************************************************************/
277 static int OpenAnalog( aout_instance_t
*p_aout
)
279 struct aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
280 OSStatus err
= noErr
;
281 UInt32 i_param_size
= 0, i
= 0;
283 ComponentDescription desc
;
284 AudioStreamBasicDescription DeviceFormat
;
285 AudioChannelLayout
*layout
;
286 AudioChannelLayout new_layout
;
287 AURenderCallbackStruct input
;
289 /* Lets go find our Component */
290 desc
.componentType
= kAudioUnitType_Output
;
291 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
292 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
293 desc
.componentFlags
= 0;
294 desc
.componentFlagsMask
= 0;
296 p_sys
->au_component
= FindNextComponent( NULL
, &desc
);
297 if( p_sys
->au_component
== NULL
)
299 msg_Warn( p_aout
, "we cannot find our HAL component" );
303 err
= OpenAComponent( p_sys
->au_component
, &p_sys
->au_unit
);
306 msg_Warn( p_aout
, "we cannot open our HAL component" );
310 /* Set the device we will use for this output unit */
311 err
= AudioUnitSetProperty( p_sys
->au_unit
,
312 kAudioOutputUnitProperty_CurrentDevice
,
313 kAudioUnitScope_Global
,
315 &p_sys
->i_selected_dev
,
316 sizeof( AudioDeviceID
));
320 msg_Warn( p_aout
, "we cannot select the audio device" );
324 /* Get the current format */
325 i_param_size
= sizeof(AudioStreamBasicDescription
);
327 err
= AudioUnitGetProperty( p_sys
->au_unit
,
328 kAudioUnitProperty_StreamFormat
,
329 kAudioUnitScope_Input
,
334 if( err
!= noErr
) return false;
335 else msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat
) );
337 /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
338 err
= AudioUnitGetPropertyInfo( p_sys
->au_unit
,
339 kAudioDevicePropertyPreferredChannelLayout
,
340 kAudioUnitScope_Output
,
347 layout
= (AudioChannelLayout
*)malloc( i_param_size
);
349 verify_noerr( AudioUnitGetProperty( p_sys
->au_unit
,
350 kAudioDevicePropertyPreferredChannelLayout
,
351 kAudioUnitScope_Output
,
356 /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
357 if( layout
->mChannelLayoutTag
== kAudioChannelLayoutTag_UseChannelBitmap
)
359 /* bitmap defined channellayout */
360 verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap
,
361 sizeof( UInt32
), &layout
->mChannelBitmap
,
365 else if( layout
->mChannelLayoutTag
!= kAudioChannelLayoutTag_UseChannelDescriptions
)
367 /* layouttags defined channellayout */
368 verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag
,
369 sizeof( AudioChannelLayoutTag
), &layout
->mChannelLayoutTag
,
374 msg_Dbg( p_aout
, "layout of AUHAL has %d channels" , (int)layout
->mNumberChannelDescriptions
);
376 /* Initialize the VLC core channel count */
377 p_aout
->output
.output
.i_physical_channels
= 0;
378 i_original
= p_aout
->output
.output
.i_original_channels
& AOUT_CHAN_PHYSMASK
;
380 if( i_original
== AOUT_CHAN_CENTER
|| layout
->mNumberChannelDescriptions
< 2 )
382 /* We only need Mono or cannot output more than 1 channel */
383 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_CENTER
;
385 else if( i_original
== (AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
) || layout
->mNumberChannelDescriptions
< 3 )
387 /* We only need Stereo or cannot output more than 2 channels */
388 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_RIGHT
| AOUT_CHAN_LEFT
;
392 /* We want more than stereo and we can do that */
393 for( i
= 0; i
< layout
->mNumberChannelDescriptions
; i
++ )
395 msg_Dbg( p_aout
, "this is channel: %d", (int)layout
->mChannelDescriptions
[i
].mChannelLabel
);
397 switch( layout
->mChannelDescriptions
[i
].mChannelLabel
)
399 case kAudioChannelLabel_Left
:
400 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_LEFT
;
402 case kAudioChannelLabel_Right
:
403 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_RIGHT
;
405 case kAudioChannelLabel_Center
:
406 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_CENTER
;
408 case kAudioChannelLabel_LFEScreen
:
409 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_LFE
;
411 case kAudioChannelLabel_LeftSurround
:
412 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_REARLEFT
;
414 case kAudioChannelLabel_RightSurround
:
415 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_REARRIGHT
;
417 case kAudioChannelLabel_RearSurroundLeft
:
418 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_MIDDLELEFT
;
420 case kAudioChannelLabel_RearSurroundRight
:
421 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_MIDDLERIGHT
;
423 case kAudioChannelLabel_CenterSurround
:
424 p_aout
->output
.output
.i_physical_channels
|= AOUT_CHAN_REARCENTER
;
427 msg_Warn( p_aout
, "unrecognized channel form provided by driver: %d", (int)layout
->mChannelDescriptions
[i
].mChannelLabel
);
430 if( p_aout
->output
.output
.i_physical_channels
== 0 )
432 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
433 msg_Err( p_aout
, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
434 intf_UserFatal( p_aout
, false, _("Audio device is not configured"),
435 _("You should configure your speaker layout with "
436 "the \"Audio Midi Setup\" utility in /Applications/"
437 "Utilities. Stereo mode is being used now.") );
444 msg_Warn( p_aout
, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
445 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
448 msg_Dbg( p_aout
, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout
->output
.output
) );
449 msg_Dbg( p_aout
, "VLC will output: %s", aout_FormatPrintChannels( &p_aout
->output
.output
));
451 memset (&new_layout
, 0, sizeof(new_layout
));
452 switch( aout_FormatNbChannels( &p_aout
->output
.output
) )
455 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_Mono
;
458 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_Stereo
;
461 if( p_aout
->output
.output
.i_physical_channels
& AOUT_CHAN_CENTER
)
463 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_7
; // L R C
465 else if( p_aout
->output
.output
.i_physical_channels
& AOUT_CHAN_LFE
)
467 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_4
; // L R LFE
471 if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_CENTER
| AOUT_CHAN_LFE
) )
473 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_10
; // L R C LFE
475 else if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
) )
477 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_3
; // L R Ls Rs
479 else if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_CENTER
| AOUT_CHAN_REARCENTER
) )
481 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_3
; // L R C Cs
485 if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_CENTER
) )
487 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_19
; // L R Ls Rs C
489 else if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_LFE
) )
491 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_18
; // L R Ls Rs LFE
495 if( p_aout
->output
.output
.i_physical_channels
& ( AOUT_CHAN_LFE
) )
497 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_DVD_20
; // L R Ls Rs C LFE
501 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_AudioUnit_6_0
; // L R Ls Rs C Cs
505 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
506 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_MPEG_6_1_A
; // L R C LFE Ls Rs Cs
509 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
510 new_layout
.mChannelLayoutTag
= kAudioChannelLayoutTag_MPEG_7_1_A
; // L R C LFE Ls Rs Lc Rc
514 /* Set up the format to be used */
515 DeviceFormat
.mSampleRate
= p_aout
->output
.output
.i_rate
;
516 DeviceFormat
.mFormatID
= kAudioFormatLinearPCM
;
518 /* We use float 32. It's the best supported format by both VLC and Coreaudio */
519 p_aout
->output
.output
.i_format
= VLC_FOURCC( 'f','l','3','2');
520 DeviceFormat
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
;
521 DeviceFormat
.mBitsPerChannel
= 32;
522 DeviceFormat
.mChannelsPerFrame
= aout_FormatNbChannels( &p_aout
->output
.output
);
524 /* Calculate framesizes and stuff */
525 DeviceFormat
.mFramesPerPacket
= 1;
526 DeviceFormat
.mBytesPerFrame
= DeviceFormat
.mBitsPerChannel
* DeviceFormat
.mChannelsPerFrame
/ 8;
527 DeviceFormat
.mBytesPerPacket
= DeviceFormat
.mBytesPerFrame
* DeviceFormat
.mFramesPerPacket
;
529 /* Set the desired format */
530 i_param_size
= sizeof(AudioStreamBasicDescription
);
531 verify_noerr( AudioUnitSetProperty( p_sys
->au_unit
,
532 kAudioUnitProperty_StreamFormat
,
533 kAudioUnitScope_Input
,
538 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat
) );
540 /* Retrieve actual format */
541 verify_noerr( AudioUnitGetProperty( p_sys
->au_unit
,
542 kAudioUnitProperty_StreamFormat
,
543 kAudioUnitScope_Input
,
548 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat
) );
550 /* Do the last VLC aout setups */
551 aout_FormatPrepare( &p_aout
->output
.output
);
552 p_aout
->output
.i_nb_samples
= 2048;
553 aout_VolumeSoftInit( p_aout
);
555 /* set the IOproc callback */
556 input
.inputProc
= (AURenderCallback
) RenderCallbackAnalog
;
557 input
.inputProcRefCon
= p_aout
;
559 verify_noerr( AudioUnitSetProperty( p_sys
->au_unit
,
560 kAudioUnitProperty_SetRenderCallback
,
561 kAudioUnitScope_Input
,
562 0, &input
, sizeof( input
) ) );
564 input
.inputProc
= (AURenderCallback
) RenderCallbackAnalog
;
565 input
.inputProcRefCon
= p_aout
;
567 /* Set the new_layout as the layout VLC will use to feed the AU unit */
568 verify_noerr( AudioUnitSetProperty( p_sys
->au_unit
,
569 kAudioUnitProperty_AudioChannelLayout
,
570 kAudioUnitScope_Input
,
571 0, &new_layout
, sizeof(new_layout
) ) );
573 if( new_layout
.mNumberChannelDescriptions
> 0 )
574 free( new_layout
.mChannelDescriptions
);
577 verify_noerr( AudioUnitInitialize(p_sys
->au_unit
) );
579 /* Find the difference between device clock and mdate clock */
580 p_sys
->clock_diff
= - (mtime_t
)
581 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
582 p_sys
->clock_diff
+= mdate();
585 verify_noerr( AudioOutputUnitStart(p_sys
->au_unit
) );
590 /*****************************************************************************
591 * Setup a encoded digital stream (SPDIF)
592 *****************************************************************************/
593 static int OpenSPDIF( aout_instance_t
* p_aout
)
595 struct aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
596 OSStatus err
= noErr
;
597 UInt32 i_param_size
= 0, b_mix
= 0;
598 Boolean b_writeable
= false;
599 AudioStreamID
*p_streams
= NULL
;
600 int i
= 0, i_streams
= 0;
602 /* Start doing the SPDIF setup proces */
603 p_sys
->b_digital
= true;
606 i_param_size
= sizeof( p_sys
->i_hog_pid
);
607 p_sys
->i_hog_pid
= getpid() ;
609 err
= AudioDeviceSetProperty( p_sys
->i_selected_dev
, 0, 0, FALSE
,
610 kAudioDevicePropertyHogMode
, i_param_size
, &p_sys
->i_hog_pid
);
614 msg_Err( p_aout
, "failed to set hogmode: [%4.4s]", (char *)&err
);
618 /* Set mixable to false if we are allowed to */
619 err
= AudioDeviceGetPropertyInfo( p_sys
->i_selected_dev
, 0, FALSE
, kAudioDevicePropertySupportsMixing
,
620 &i_param_size
, &b_writeable
);
622 err
= AudioDeviceGetProperty( p_sys
->i_selected_dev
, 0, FALSE
, kAudioDevicePropertySupportsMixing
,
623 &i_param_size
, &b_mix
);
625 if( !err
&& b_writeable
)
628 err
= AudioDeviceSetProperty( p_sys
->i_selected_dev
, 0, 0, FALSE
,
629 kAudioDevicePropertySupportsMixing
, i_param_size
, &b_mix
);
630 p_sys
->b_changed_mixing
= true;
635 msg_Err( p_aout
, "failed to set mixmode: [%4.4s]", (char *)&err
);
639 /* Get a list of all the streams on this device */
640 err
= AudioDeviceGetPropertyInfo( p_sys
->i_selected_dev
, 0, FALSE
,
641 kAudioDevicePropertyStreams
,
642 &i_param_size
, NULL
);
645 msg_Err( p_aout
, "could not get number of streams: [%4.4s]", (char *)&err
);
649 i_streams
= i_param_size
/ sizeof( AudioStreamID
);
650 p_streams
= (AudioStreamID
*)malloc( i_param_size
);
651 if( p_streams
== NULL
)
653 msg_Err( p_aout
, "out of memory" );
657 err
= AudioDeviceGetProperty( p_sys
->i_selected_dev
, 0, FALSE
,
658 kAudioDevicePropertyStreams
,
659 &i_param_size
, p_streams
);
663 msg_Err( p_aout
, "could not get number of streams: [%4.4s]", (char *)&err
);
668 for( i
= 0; i
< i_streams
&& p_sys
->i_stream_index
< 0 ; i
++ )
670 /* Find a stream with a cac3 stream */
671 AudioStreamBasicDescription
*p_format_list
= NULL
;
672 int i_formats
= 0, j
= 0;
673 bool b_digital
= false;
675 /* Retrieve all the stream formats supported by each output stream */
676 err
= AudioStreamGetPropertyInfo( p_streams
[i
], 0,
677 kAudioStreamPropertyPhysicalFormats
,
678 &i_param_size
, NULL
);
681 msg_Err( p_aout
, "could not get number of streamformats: [%4.4s]", (char *)&err
);
685 i_formats
= i_param_size
/ sizeof( AudioStreamBasicDescription
);
686 p_format_list
= (AudioStreamBasicDescription
*)malloc( i_param_size
);
687 if( p_format_list
== NULL
)
689 msg_Err( p_aout
, "could not malloc the memory" );
693 err
= AudioStreamGetProperty( p_streams
[i
], 0,
694 kAudioStreamPropertyPhysicalFormats
,
695 &i_param_size
, p_format_list
);
698 msg_Err( p_aout
, "could not get the list of streamformats: [%4.4s]", (char *)&err
);
699 free( p_format_list
);
703 /* Check if one of the supported formats is a digital format */
704 for( j
= 0; j
< i_formats
; j
++ )
706 if( p_format_list
[j
].mFormatID
== 'IAC3' ||
707 p_format_list
[j
].mFormatID
== kAudioFormat60958AC3
)
716 /* if this stream supports a digital (cac3) format, then go set it. */
717 int i_requested_rate_format
= -1;
718 int i_current_rate_format
= -1;
719 int i_backup_rate_format
= -1;
721 p_sys
->i_stream_id
= p_streams
[i
];
722 p_sys
->i_stream_index
= i
;
724 if( p_sys
->b_revert
== false )
726 /* Retrieve the original format of this stream first if not done so already */
727 i_param_size
= sizeof( p_sys
->sfmt_revert
);
728 err
= AudioStreamGetProperty( p_sys
->i_stream_id
, 0,
729 kAudioStreamPropertyPhysicalFormat
,
731 &p_sys
->sfmt_revert
);
734 msg_Err( p_aout
, "could not retrieve the original streamformat: [%4.4s]", (char *)&err
);
737 p_sys
->b_revert
= true;
740 for( j
= 0; j
< i_formats
; j
++ )
742 if( p_format_list
[j
].mFormatID
== 'IAC3' ||
743 p_format_list
[j
].mFormatID
== kAudioFormat60958AC3
)
745 if( p_format_list
[j
].mSampleRate
== p_aout
->output
.output
.i_rate
)
747 i_requested_rate_format
= j
;
750 else if( p_format_list
[j
].mSampleRate
== p_sys
->sfmt_revert
.mSampleRate
)
752 i_current_rate_format
= j
;
756 if( i_backup_rate_format
< 0 || p_format_list
[j
].mSampleRate
> p_format_list
[i_backup_rate_format
].mSampleRate
)
757 i_backup_rate_format
= j
;
763 if( i_requested_rate_format
>= 0 ) /* We prefer to output at the samplerate of the original audio */
764 p_sys
->stream_format
= p_format_list
[i_requested_rate_format
];
765 else if( i_current_rate_format
>= 0 ) /* If not possible, we will try to use the current samplerate of the device */
766 p_sys
->stream_format
= p_format_list
[i_current_rate_format
];
767 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) */
769 free( p_format_list
);
773 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "original stream format: ", p_sys
->sfmt_revert
) );
775 if( !AudioStreamChangeFormat( p_aout
, p_sys
->i_stream_id
, p_sys
->stream_format
) )
778 /* Set the format flags */
779 if( p_sys
->stream_format
.mFormatFlags
& kAudioFormatFlagIsBigEndian
)
780 p_aout
->output
.output
.i_format
= VLC_FOURCC('s','p','d','b');
782 p_aout
->output
.output
.i_format
= VLC_FOURCC('s','p','d','i');
783 p_aout
->output
.output
.i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
784 p_aout
->output
.output
.i_frame_length
= A52_FRAME_NB
;
785 p_aout
->output
.i_nb_samples
= p_aout
->output
.output
.i_frame_length
;
786 p_aout
->output
.output
.i_rate
= (unsigned int)p_sys
->stream_format
.mSampleRate
;
787 aout_FormatPrepare( &p_aout
->output
.output
);
788 aout_VolumeNoneInit( p_aout
);
790 /* Add IOProc callback */
791 err
= AudioDeviceAddIOProc( p_sys
->i_selected_dev
,
792 (AudioDeviceIOProc
)RenderCallbackSPDIF
,
797 msg_Err( p_aout
, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err
);
801 /* Check for the difference between the Device clock and mdate */
802 p_sys
->clock_diff
= - (mtime_t
)
803 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
804 p_sys
->clock_diff
+= mdate();
807 err
= AudioDeviceStart( p_sys
->i_selected_dev
, (AudioDeviceIOProc
)RenderCallbackSPDIF
);
810 msg_Err( p_aout
, "AudioDeviceStart failed: [%4.4s]", (char *)&err
);
812 err
= AudioDeviceRemoveIOProc( p_sys
->i_selected_dev
,
813 (AudioDeviceIOProc
)RenderCallbackSPDIF
);
816 msg_Err( p_aout
, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err
);
825 /*****************************************************************************
826 * Close: Close HAL AudioUnit
827 *****************************************************************************/
828 static void Close( vlc_object_t
* p_this
)
830 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
831 struct aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
832 OSStatus err
= noErr
;
833 UInt32 i_param_size
= 0;
837 verify_noerr( AudioOutputUnitStop( p_sys
->au_unit
) );
838 verify_noerr( AudioUnitUninitialize( p_sys
->au_unit
) );
839 verify_noerr( CloseComponent( p_sys
->au_unit
) );
842 if( p_sys
->b_digital
)
845 err
= AudioDeviceStop( p_sys
->i_selected_dev
,
846 (AudioDeviceIOProc
)RenderCallbackSPDIF
);
849 msg_Err( p_aout
, "AudioDeviceStop failed: [%4.4s]", (char *)&err
);
852 /* Remove IOProc callback */
853 err
= AudioDeviceRemoveIOProc( p_sys
->i_selected_dev
,
854 (AudioDeviceIOProc
)RenderCallbackSPDIF
);
857 msg_Err( p_aout
, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err
);
860 if( p_sys
->b_revert
)
862 AudioStreamChangeFormat( p_aout
, p_sys
->i_stream_id
, p_sys
->sfmt_revert
);
865 if( p_sys
->b_changed_mixing
&& p_sys
->sfmt_revert
.mFormatID
!= kAudioFormat60958AC3
)
869 /* Revert mixable to true if we are allowed to */
870 err
= AudioDeviceGetPropertyInfo( p_sys
->i_selected_dev
, 0, FALSE
, kAudioDevicePropertySupportsMixing
,
871 &i_param_size
, &b_writeable
);
873 err
= AudioDeviceGetProperty( p_sys
->i_selected_dev
, 0, FALSE
, kAudioDevicePropertySupportsMixing
,
874 &i_param_size
, &b_mix
);
876 if( !err
&& b_writeable
)
878 msg_Dbg( p_aout
, "mixable is: %d", b_mix
);
880 err
= AudioDeviceSetProperty( p_sys
->i_selected_dev
, 0, 0, FALSE
,
881 kAudioDevicePropertySupportsMixing
, i_param_size
, &b_mix
);
886 msg_Err( p_aout
, "failed to set mixmode: [%4.4s]", (char *)&err
);
891 err
= AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices
,
896 msg_Err( p_aout
, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err
);
899 if( p_sys
->i_hog_pid
== getpid() )
901 p_sys
->i_hog_pid
= -1;
902 i_param_size
= sizeof( p_sys
->i_hog_pid
);
903 err
= AudioDeviceSetProperty( p_sys
->i_selected_dev
, 0, 0, FALSE
,
904 kAudioDevicePropertyHogMode
, i_param_size
, &p_sys
->i_hog_pid
);
905 if( err
!= noErr
) msg_Err( p_aout
, "Could not release hogmode: [%4.4s]", (char *)&err
);
911 /*****************************************************************************
912 * Play: nothing to do
913 *****************************************************************************/
914 static void Play( aout_instance_t
* p_aout
)
919 /*****************************************************************************
920 * Probe: Check which devices the OS has, and add them to our audio-device menu
921 *****************************************************************************/
922 static void Probe( aout_instance_t
* p_aout
)
924 OSStatus err
= noErr
;
925 UInt32 i
= 0, i_param_size
= 0;
926 AudioDeviceID devid_def
= 0;
927 AudioDeviceID
*p_devices
= NULL
;
928 vlc_value_t val
, text
;
930 struct aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
932 /* Get number of devices */
933 err
= AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices
,
934 &i_param_size
, NULL
);
937 msg_Err( p_aout
, "Could not get number of devices: [%4.4s]", (char *)&err
);
941 p_sys
->i_devices
= i_param_size
/ sizeof( AudioDeviceID
);
943 if( p_sys
->i_devices
< 1 )
945 msg_Err( p_aout
, "No audio output devices were found." );
949 msg_Dbg( p_aout
, "system has [%ld] device(s)", p_sys
->i_devices
);
951 /* Allocate DeviceID array */
952 p_devices
= (AudioDeviceID
*)malloc( sizeof(AudioDeviceID
) * p_sys
->i_devices
);
953 if( p_devices
== NULL
)
955 msg_Err( p_aout
, "out of memory" );
959 /* Populate DeviceID array */
960 err
= AudioHardwareGetProperty( kAudioHardwarePropertyDevices
,
961 &i_param_size
, p_devices
);
964 msg_Err( p_aout
, "could not get the device IDs: [%4.4s]", (char *)&err
);
968 /* Find the ID of the default Device */
969 i_param_size
= sizeof( AudioDeviceID
);
970 err
= AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice
,
971 &i_param_size
, &devid_def
);
974 msg_Err( p_aout
, "could not get default audio device: [%4.4s]", (char *)&err
);
977 p_sys
->i_default_dev
= devid_def
;
979 var_Create( p_aout
, "audio-device", VLC_VAR_INTEGER
|VLC_VAR_HASCHOICE
);
980 text
.psz_string
= _("Audio Device");
981 var_Change( p_aout
, "audio-device", VLC_VAR_SETTEXT
, &text
, NULL
);
983 for( i
= 0; i
< p_sys
->i_devices
; i
++ )
988 /* Retrieve the length of the device name */
989 err
= AudioDeviceGetPropertyInfo(
990 p_devices
[i
], 0, false,
991 kAudioDevicePropertyDeviceName
,
992 &i_param_size
, NULL
);
993 if( err
) goto error
;
995 /* Retrieve the name of the device */
996 psz_name
= (char *)malloc( i_param_size
);
997 err
= AudioDeviceGetProperty(
998 p_devices
[i
], 0, false,
999 kAudioDevicePropertyDeviceName
,
1000 &i_param_size
, psz_name
);
1001 if( err
) goto error
;
1003 msg_Dbg( p_aout
, "DevID: %#lx DevName: %s", p_devices
[i
], psz_name
);
1005 if( !AudioDeviceHasOutput( p_devices
[i
]) )
1007 msg_Dbg( p_aout
, "this device is INPUT only. skipping..." );
1011 /* Add the menu entries */
1012 val
.i_int
= (int)p_devices
[i
];
1013 text
.psz_string
= strdup( psz_name
);
1014 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
, &val
, &text
);
1015 if( p_sys
->i_default_dev
== p_devices
[i
] )
1017 /* The default device is the selected device normally */
1018 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
1019 var_Set( p_aout
, "audio-device", val
);
1022 if( AudioDeviceSupportsDigital( p_aout
, p_devices
[i
] ) )
1024 val
.i_int
= (int)p_devices
[i
] | AOUT_VAR_SPDIF_FLAG
;
1025 asprintf( &text
.psz_string
, _("%s (Encoded Output)"), psz_name
);
1026 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
, &val
, &text
);
1027 if( p_sys
->i_default_dev
== p_devices
[i
] && config_GetInt( p_aout
, "spdif" ) )
1029 /* We selected to prefer SPDIF output if available
1030 * then this "dummy" entry should be selected */
1031 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
1032 var_Set( p_aout
, "audio-device", val
);
1039 /* If a device is already "preselected", then use this device */
1040 var_Get( p_aout
->p_libvlc
, "macosx-audio-device", &val
);
1043 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
1044 var_Set( p_aout
, "audio-device", val
);
1047 /* If we change the device we want to use, we should renegotiate the audio chain */
1048 var_AddCallback( p_aout
, "audio-device", AudioDeviceCallback
, NULL
);
1050 /* Attach a Listener so that we are notified of a change in the Device setup */
1051 err
= AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices
,
1061 var_Destroy( p_aout
, "audio-device" );
1066 /*****************************************************************************
1067 * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
1068 *****************************************************************************/
1069 static int AudioDeviceHasOutput( AudioDeviceID i_dev_id
)
1074 verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id
, 0, FALSE
, kAudioDevicePropertyStreams
, &dataSize
, &isWritable
) );
1075 if (dataSize
== 0) return FALSE
;
1080 /*****************************************************************************
1081 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1082 *****************************************************************************/
1083 static int AudioDeviceSupportsDigital( aout_instance_t
*p_aout
, AudioDeviceID i_dev_id
)
1085 OSStatus err
= noErr
;
1086 UInt32 i_param_size
= 0;
1087 AudioStreamID
*p_streams
= NULL
;
1088 int i
= 0, i_streams
= 0;
1089 bool b_return
= false;
1091 /* Retrieve all the output streams */
1092 err
= AudioDeviceGetPropertyInfo( i_dev_id
, 0, FALSE
,
1093 kAudioDevicePropertyStreams
,
1094 &i_param_size
, NULL
);
1097 msg_Err( p_aout
, "could not get number of streams: [%4.4s]", (char *)&err
);
1101 i_streams
= i_param_size
/ sizeof( AudioStreamID
);
1102 p_streams
= (AudioStreamID
*)malloc( i_param_size
);
1103 if( p_streams
== NULL
)
1105 msg_Err( p_aout
, "out of memory" );
1109 err
= AudioDeviceGetProperty( i_dev_id
, 0, FALSE
,
1110 kAudioDevicePropertyStreams
,
1111 &i_param_size
, p_streams
);
1115 msg_Err( p_aout
, "could not get number of streams: [%4.4s]", (char *)&err
);
1119 for( i
= 0; i
< i_streams
; i
++ )
1121 if( AudioStreamSupportsDigital( p_aout
, p_streams
[i
] ) )
1129 /*****************************************************************************
1130 * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1131 *****************************************************************************/
1132 static int AudioStreamSupportsDigital( aout_instance_t
*p_aout
, AudioStreamID i_stream_id
)
1134 OSStatus err
= noErr
;
1135 UInt32 i_param_size
= 0;
1136 AudioStreamBasicDescription
*p_format_list
= NULL
;
1137 int i
= 0, i_formats
= 0;
1138 bool b_return
= false;
1140 /* Retrieve all the stream formats supported by each output stream */
1141 err
= AudioStreamGetPropertyInfo( i_stream_id
, 0,
1142 kAudioStreamPropertyPhysicalFormats
,
1143 &i_param_size
, NULL
);
1146 msg_Err( p_aout
, "could not get number of streamformats: [%4.4s]", (char *)&err
);
1150 i_formats
= i_param_size
/ sizeof( AudioStreamBasicDescription
);
1151 p_format_list
= (AudioStreamBasicDescription
*)malloc( i_param_size
);
1152 if( p_format_list
== NULL
)
1154 msg_Err( p_aout
, "could not malloc the memory" );
1158 err
= AudioStreamGetProperty( i_stream_id
, 0,
1159 kAudioStreamPropertyPhysicalFormats
,
1160 &i_param_size
, p_format_list
);
1163 msg_Err( p_aout
, "could not get the list of streamformats: [%4.4s]", (char *)&err
);
1164 free( p_format_list
);
1165 p_format_list
= NULL
;
1169 for( i
= 0; i
< i_formats
; i
++ )
1171 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "supported format: ", p_format_list
[i
] ) );
1173 if( p_format_list
[i
].mFormatID
== 'IAC3' ||
1174 p_format_list
[i
].mFormatID
== kAudioFormat60958AC3
)
1180 free( p_format_list
);
1184 /*****************************************************************************
1185 * AudioStreamChangeFormat: Change i_stream_id to change_format
1186 *****************************************************************************/
1187 static int AudioStreamChangeFormat( aout_instance_t
*p_aout
, AudioStreamID i_stream_id
, AudioStreamBasicDescription change_format
)
1189 OSStatus err
= noErr
;
1190 UInt32 i_param_size
= 0;
1193 struct { vlc_mutex_t lock
; vlc_cond_t cond
; } w
;
1195 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "setting stream format: ", change_format
) );
1197 /* Condition because SetProperty is asynchronious */
1198 vlc_cond_init( p_aout
, &w
.cond
);
1199 vlc_mutex_init( &w
.lock
);
1200 vlc_mutex_lock( &w
.lock
);
1202 /* Install the callback */
1203 err
= AudioStreamAddPropertyListener( i_stream_id
, 0,
1204 kAudioStreamPropertyPhysicalFormat
,
1205 StreamListener
, (void *)&w
);
1208 msg_Err( p_aout
, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err
);
1212 /* change the format */
1213 err
= AudioStreamSetProperty( i_stream_id
, 0, 0,
1214 kAudioStreamPropertyPhysicalFormat
,
1215 sizeof( AudioStreamBasicDescription
),
1219 msg_Err( p_aout
, "could not set the stream format: [%4.4s]", (char *)&err
);
1223 /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1224 * it is also not atomic in its behaviour.
1225 * Therefore we check 5 times before we really give up.
1226 * FIXME: failing isn't actually implemented yet. */
1227 for( i
= 0; i
< 5; i
++ )
1229 AudioStreamBasicDescription actual_format
;
1230 mtime_t timeout
= mdate() + 500000;
1232 if( vlc_cond_timedwait( &w
.cond
, &w
.lock
, timeout
) )
1234 msg_Dbg( p_aout
, "reached timeout" );
1237 i_param_size
= sizeof( AudioStreamBasicDescription
);
1238 err
= AudioStreamGetProperty( i_stream_id
, 0,
1239 kAudioStreamPropertyPhysicalFormat
,
1243 msg_Dbg( p_aout
, STREAM_FORMAT_MSG( "actual format in use: ", actual_format
) );
1244 if( actual_format
.mSampleRate
== change_format
.mSampleRate
&&
1245 actual_format
.mFormatID
== change_format
.mFormatID
&&
1246 actual_format
.mFramesPerPacket
== change_format
.mFramesPerPacket
)
1248 /* The right format is now active */
1251 /* We need to check again */
1254 /* Removing the property listener */
1255 err
= AudioStreamRemovePropertyListener( i_stream_id
, 0,
1256 kAudioStreamPropertyPhysicalFormat
,
1260 msg_Err( p_aout
, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err
);
1264 /* Destroy the lock and condition */
1265 vlc_mutex_unlock( &w
.lock
);
1266 vlc_mutex_destroy( &w
.lock
);
1267 vlc_cond_destroy( &w
.cond
);
1272 /*****************************************************************************
1273 * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1274 * us to provide some more audio data.
1275 * Don't print anything during normal playback, calling blocking function from
1276 * this callback is not allowed.
1277 *****************************************************************************/
1278 static OSStatus
RenderCallbackAnalog( vlc_object_t
*_p_aout
,
1279 AudioUnitRenderActionFlags
*ioActionFlags
,
1280 const AudioTimeStamp
*inTimeStamp
,
1281 unsigned int inBusNummer
,
1282 unsigned int inNumberFrames
,
1283 AudioBufferList
*ioData
)
1285 AudioTimeStamp host_time
;
1286 mtime_t current_date
= 0;
1287 uint32_t i_mData_bytes
= 0;
1289 aout_instance_t
* p_aout
= (aout_instance_t
*)_p_aout
;
1290 struct aout_sys_t
* p_sys
= p_aout
->output
.p_sys
;
1292 host_time
.mFlags
= kAudioTimeStampHostTimeValid
;
1293 AudioDeviceTranslateTime( p_sys
->i_selected_dev
, inTimeStamp
, &host_time
);
1295 /* Check for the difference between the Device clock and mdate */
1296 p_sys
->clock_diff
= - (mtime_t
)
1297 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
1298 p_sys
->clock_diff
+= mdate();
1300 current_date
= p_sys
->clock_diff
+
1301 AudioConvertHostTimeToNanos( host_time
.mHostTime
) / 1000;
1302 //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1304 if( ioData
== NULL
&& ioData
->mNumberBuffers
< 1 )
1306 msg_Err( p_aout
, "no iodata or buffers");
1309 if( ioData
->mNumberBuffers
> 1 )
1310 msg_Err( p_aout
, "well this is weird. seems like there is more than one buffer..." );
1313 if( p_sys
->i_total_bytes
> 0 )
1315 i_mData_bytes
= __MIN( p_sys
->i_total_bytes
- p_sys
->i_read_bytes
, ioData
->mBuffers
[0].mDataByteSize
);
1316 vlc_memcpy( ioData
->mBuffers
[0].mData
,
1317 &p_sys
->p_remainder_buffer
[p_sys
->i_read_bytes
],
1319 p_sys
->i_read_bytes
+= i_mData_bytes
;
1320 current_date
+= (mtime_t
) ( (mtime_t
) 1000000 / p_aout
->output
.output
.i_rate
) *
1321 ( i_mData_bytes
/ 4 / aout_FormatNbChannels( &p_aout
->output
.output
) ); // 4 is fl32 specific
1323 if( p_sys
->i_read_bytes
>= p_sys
->i_total_bytes
)
1324 p_sys
->i_read_bytes
= p_sys
->i_total_bytes
= 0;
1327 while( i_mData_bytes
< ioData
->mBuffers
[0].mDataByteSize
)
1329 /* We don't have enough data yet */
1330 aout_buffer_t
* p_buffer
;
1331 p_buffer
= aout_OutputNextBuffer( p_aout
, current_date
, false );
1333 if( p_buffer
!= NULL
)
1335 uint32_t i_second_mData_bytes
= __MIN( p_buffer
->i_nb_bytes
, ioData
->mBuffers
[0].mDataByteSize
- i_mData_bytes
);
1337 vlc_memcpy( (uint8_t *)ioData
->mBuffers
[0].mData
+ i_mData_bytes
,
1338 p_buffer
->p_buffer
, i_second_mData_bytes
);
1339 i_mData_bytes
+= i_second_mData_bytes
;
1341 if( i_mData_bytes
>= ioData
->mBuffers
[0].mDataByteSize
)
1343 p_sys
->i_total_bytes
= p_buffer
->i_nb_bytes
- i_second_mData_bytes
;
1344 vlc_memcpy( p_sys
->p_remainder_buffer
,
1345 &p_buffer
->p_buffer
[i_second_mData_bytes
],
1346 p_sys
->i_total_bytes
);
1350 /* update current_date */
1351 current_date
+= (mtime_t
) ( (mtime_t
) 1000000 / p_aout
->output
.output
.i_rate
) *
1352 ( i_second_mData_bytes
/ 4 / aout_FormatNbChannels( &p_aout
->output
.output
) ); // 4 is fl32 specific
1354 aout_BufferFree( p_buffer
);
1358 vlc_memset( (uint8_t *)ioData
->mBuffers
[0].mData
+i_mData_bytes
,
1359 0,ioData
->mBuffers
[0].mDataByteSize
- i_mData_bytes
);
1360 i_mData_bytes
+= ioData
->mBuffers
[0].mDataByteSize
- i_mData_bytes
;
1366 /*****************************************************************************
1367 * RenderCallbackSPDIF: callback for SPDIF audio output
1368 *****************************************************************************/
1369 static OSStatus
RenderCallbackSPDIF( AudioDeviceID inDevice
,
1370 const AudioTimeStamp
* inNow
,
1371 const void * inInputData
,
1372 const AudioTimeStamp
* inInputTime
,
1373 AudioBufferList
* outOutputData
,
1374 const AudioTimeStamp
* inOutputTime
,
1375 void * threadGlobals
)
1377 aout_buffer_t
* p_buffer
;
1378 mtime_t current_date
;
1380 aout_instance_t
* p_aout
= (aout_instance_t
*)threadGlobals
;
1381 struct aout_sys_t
* p_sys
= p_aout
->output
.p_sys
;
1383 /* Check for the difference between the Device clock and mdate */
1384 p_sys
->clock_diff
= - (mtime_t
)
1385 AudioConvertHostTimeToNanos( inNow
->mHostTime
) / 1000;
1386 p_sys
->clock_diff
+= mdate();
1388 current_date
= p_sys
->clock_diff
+
1389 AudioConvertHostTimeToNanos( inOutputTime
->mHostTime
) / 1000;
1390 //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1392 p_buffer
= aout_OutputNextBuffer( p_aout
, current_date
, true );
1394 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1395 if( p_buffer
!= NULL
)
1397 if( (int)BUFFER
.mDataByteSize
!= (int)p_buffer
->i_nb_bytes
)
1398 msg_Warn( p_aout
, "bytesize: %d nb_bytes: %d", (int)BUFFER
.mDataByteSize
, (int)p_buffer
->i_nb_bytes
);
1400 /* move data into output data buffer */
1401 vlc_memcpy( BUFFER
.mData
, p_buffer
->p_buffer
, p_buffer
->i_nb_bytes
);
1402 aout_BufferFree( p_buffer
);
1406 vlc_memset( BUFFER
.mData
, 0, BUFFER
.mDataByteSize
);
1413 /*****************************************************************************
1414 * HardwareListener: Warns us of changes in the list of registered devices
1415 *****************************************************************************/
1416 static OSStatus
HardwareListener( AudioHardwarePropertyID inPropertyID
,
1417 void * inClientData
)
1419 OSStatus err
= noErr
;
1420 aout_instance_t
*p_aout
= (aout_instance_t
*)inClientData
;
1422 switch( inPropertyID
)
1424 case kAudioHardwarePropertyDevices
:
1426 /* something changed in the list of devices */
1427 /* We trigger the audio-device's aout_ChannelsRestart callback */
1428 var_Change( p_aout
, "audio-device", VLC_VAR_TRIGGER_CALLBACKS
, NULL
, NULL
);
1429 var_Destroy( p_aout
, "audio-device" );
1437 /*****************************************************************************
1439 *****************************************************************************/
1440 static OSStatus
StreamListener( AudioStreamID inStream
,
1442 AudioDevicePropertyID inPropertyID
,
1443 void * inClientData
)
1445 OSStatus err
= noErr
;
1446 struct { vlc_mutex_t lock
; vlc_cond_t cond
; } * w
= inClientData
;
1448 switch( inPropertyID
)
1450 case kAudioStreamPropertyPhysicalFormat
:
1451 vlc_mutex_lock( &w
->lock
);
1452 vlc_cond_signal( &w
->cond
);
1453 vlc_mutex_unlock( &w
->lock
);
1462 /*****************************************************************************
1463 * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1464 *****************************************************************************/
1465 static int AudioDeviceCallback( vlc_object_t
*p_this
, const char *psz_variable
,
1466 vlc_value_t old_val
, vlc_value_t new_val
, void *param
)
1468 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
1469 var_Set( p_aout
->p_libvlc
, "macosx-audio-device", new_val
);
1470 msg_Dbg( p_aout
, "Set Device: %#x", new_val
.i_int
);
1471 return aout_ChannelsRestart( p_this
, psz_variable
, old_val
, new_val
, param
);