1 /*****************************************************************************
2 * auhal.c: AUHAL and Coreaudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2005, 2012 VLC authors and VideoLAN
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8 * Felix Paul Kühne <fkuehne at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #import <vlc_common.h>
34 #import <vlc_plugin.h>
35 #import <vlc_dialog.h> // dialog_Fatal
36 #import <vlc_aout.h> // aout_*
38 #import <AudioUnit/AudioUnit.h> // AudioUnit
39 #import <CoreAudio/CoreAudio.h> // AudioDeviceID
40 #import <AudioToolbox/AudioFormat.h> // AudioFormatGetProperty
41 #import <CoreServices/CoreServices.h>
44 # define verify_noerr(a) assert((a) == noErr)
47 #define STREAM_FORMAT_MSG(pre, sfm) \
48 pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
49 sfm.mSampleRate, (char *)&sfm.mFormatID, \
50 (unsigned int)sfm.mFormatFlags, (unsigned int)sfm.mBytesPerPacket, \
51 (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
52 (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
54 #define FRAMESIZE 2048
55 #define BUFSIZE (FRAMESIZE * 8) * 8
56 #define AOUT_VAR_SPDIF_FLAG 0xf00000
58 #define AOUT_VOLUME_DEFAULT 256
59 #define AOUT_VOLUME_MAX 512
63 * - clean up the debug info
64 * - be better at changing stream setup or devices setup changes while playing.
68 /*****************************************************************************
69 * aout_sys_t: private audio output method descriptor
70 *****************************************************************************
71 * This structure is part of the audio output thread descriptor.
72 * It describes the CoreAudio specific properties of an output thread.
73 *****************************************************************************/
77 AudioDeviceID i_default_dev; /* DeviceID of defaultOutputDevice */
78 AudioDeviceID i_selected_dev; /* DeviceID of the selected device */
79 AudioDeviceIOProcID i_procID; /* DeviceID of current device */
80 UInt32 i_devices; /* Number of CoreAudio Devices */
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 (audio_output_t *, audio_sample_format_t *);
106 static int OpenSPDIF (audio_output_t *, audio_sample_format_t *);
107 static void Close (vlc_object_t *);
109 static void Probe (audio_output_t *);
111 static int AudioDeviceHasOutput (AudioDeviceID);
112 static int AudioDeviceSupportsDigital(audio_output_t *, AudioDeviceID);
113 static int AudioStreamSupportsDigital(audio_output_t *, AudioStreamID);
114 static int AudioStreamChangeFormat (audio_output_t *, AudioStreamID, AudioStreamBasicDescription);
116 static OSStatus RenderCallbackAnalog (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
117 unsigned int, unsigned int, AudioBufferList *);
118 static OSStatus RenderCallbackSPDIF (AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
119 AudioBufferList *, const AudioTimeStamp *, void *);
120 static OSStatus HardwareListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
121 static OSStatus StreamListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
122 static int AudioDeviceCallback (vlc_object_t *, const char *,
123 vlc_value_t, vlc_value_t, void *);
125 static int VolumeSet (audio_output_t *, float);
126 static int MuteSet (audio_output_t *, bool);
129 /*****************************************************************************
131 *****************************************************************************/
132 #define ADEV_TEXT N_("Audio Device")
133 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
134 "audio device, as listed in your 'Audio Device' menu. This device will " \
135 "then be used by default for audio playback.")
137 #define VOLUME_TEXT N_("Audio volume")
138 #define VOLUME_LONGTEXT VOLUME_TEXT
141 set_shortname("auhal")
142 set_description(N_("HAL AudioUnit output"))
143 set_capability("audio output", 101)
144 set_category(CAT_AUDIO)
145 set_subcategory(SUBCAT_AUDIO_AOUT)
146 set_callbacks(Open, Close)
147 add_integer("macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false)
148 add_integer( "auhal-volume", AOUT_VOLUME_DEFAULT,
149 VOLUME_TEXT, VOLUME_LONGTEXT, true )
150 change_integer_range( 0, AOUT_VOLUME_MAX )
153 /*****************************************************************************
154 * Open: open macosx audio output
155 *****************************************************************************/
156 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
158 OSStatus err = noErr;
159 UInt32 i_param_size = 0;
160 struct aout_sys_t *p_sys = NULL;
163 /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
168 p_sys->i_default_dev = 0;
169 p_sys->i_selected_dev = 0;
170 p_sys->i_devices = 0;
171 p_sys->b_digital = false;
172 p_sys->au_component = NULL;
173 p_sys->au_unit = NULL;
174 p_sys->clock_diff = (mtime_t) 0;
175 p_sys->i_read_bytes = 0;
176 p_sys->i_total_bytes = 0;
177 p_sys->i_hog_pid = -1;
178 p_sys->i_stream_id = 0;
179 p_sys->i_stream_index = -1;
180 p_sys->b_revert = false;
181 p_sys->b_changed_mixing = false;
182 memset(p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE);
184 p_aout->time_get = aout_PacketTimeGet;
185 p_aout->play = aout_PacketPlay;
186 p_aout->pause = NULL;
187 p_aout->flush = aout_PacketFlush;
189 aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
191 /* Persistent device variable */
192 if (var_Type(p_aout->p_libvlc, "macosx-audio-device") == 0)
193 var_Create(p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
195 /* Build a list of devices */
196 if (var_Type(p_aout, "audio-device") == 0)
199 /* What device do we want? */
200 if (var_Get(p_aout, "audio-device", &val) < 0) {
201 msg_Err(p_aout, "audio-device var does not exist. device probe failed.");
205 p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
206 bool b_supports_digital = (val.i_int & AOUT_VAR_SPDIF_FLAG);
207 if (b_supports_digital)
208 msg_Dbg(p_aout, "audio device supports digital output");
210 /* Check if the desired device is alive and usable */
211 i_param_size = sizeof(b_alive);
212 AudioObjectPropertyAddress audioDeviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive,
213 kAudioObjectPropertyScopeGlobal,
214 kAudioObjectPropertyElementMaster };
215 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceAliveAddress, 0, NULL, &i_param_size, &b_alive);
218 /* Be tolerant, only give a warning here */
219 msg_Warn(p_aout, "could not check whether device [0x%x] is alive: %4.4s",
220 (unsigned int)p_sys->i_selected_dev, (char *)&err);
225 msg_Warn(p_aout, "selected audio device is not alive, switching to default device");
226 p_sys->i_selected_dev = p_sys->i_default_dev;
229 /* add a callback to see if the device dies later on */
230 err = AudioObjectAddPropertyListener(p_sys->i_selected_dev, &audioDeviceAliveAddress, HardwareListener, (void *)p_aout);
232 /* Be tolerant, only give a warning here */
233 msg_Warn(p_aout, "could not set alive check callback on device [0x%x]: %4.4s",
234 (unsigned int)p_sys->i_selected_dev, (char *)&err);
237 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
238 kAudioDevicePropertyScopeOutput,
239 kAudioObjectPropertyElementMaster };
240 i_param_size = sizeof(p_sys->i_hog_pid);
241 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, &i_param_size, &p_sys->i_hog_pid);
243 /* This is not a fatal error. Some drivers simply don't support this property */
244 msg_Warn(p_aout, "could not check whether device is hogged: %4.4s",
246 p_sys->i_hog_pid = -1;
249 if (p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid()) {
250 msg_Err(p_aout, "Selected audio device is exclusively in use by another program.");
251 dialog_Fatal(p_aout, _("Audio output failed"), "%s",
252 _("The selected audio output device is exclusively in "
253 "use by another program."));
257 /* If we change the device we want to use, we should renegotiate the audio chain */
258 var_AddCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
260 /* Check for Digital mode or Analog output mode */
261 if (AOUT_FMT_SPDIF (fmt) && b_supports_digital) {
262 if (OpenSPDIF (p_aout, fmt)) {
263 msg_Dbg(p_aout, "digital output successfully opened");
267 if (OpenAnalog(p_aout, fmt)) {
268 msg_Dbg(p_aout, "analog output successfully opened");
274 /* If we reach this, this aout has failed */
275 msg_Err(p_aout, "opening the auhal output failed");
276 var_Destroy(p_aout, "audio-device");
280 /*****************************************************************************
281 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
282 *****************************************************************************/
283 static int OpenAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
285 struct aout_sys_t *p_sys = p_aout->sys;
286 OSStatus err = noErr;
287 UInt32 i_param_size = 0;
289 ComponentDescription desc;
290 AudioStreamBasicDescription DeviceFormat;
291 AudioChannelLayout *layout;
292 AudioChannelLayout new_layout;
293 AURenderCallbackStruct input;
295 /* Lets go find our Component */
296 desc.componentType = kAudioUnitType_Output;
297 desc.componentSubType = kAudioUnitSubType_HALOutput;
298 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
299 desc.componentFlags = 0;
300 desc.componentFlagsMask = 0;
302 p_sys->au_component = FindNextComponent(NULL, &desc);
303 if (p_sys->au_component == NULL) {
304 msg_Warn(p_aout, "we cannot find our HAL component");
308 err = OpenAComponent(p_sys->au_component, &p_sys->au_unit);
310 msg_Warn(p_aout, "we cannot open our HAL component");
314 /* Set the device we will use for this output unit */
315 err = AudioUnitSetProperty(p_sys->au_unit,
316 kAudioOutputUnitProperty_CurrentDevice,
317 kAudioUnitScope_Global,
319 &p_sys->i_selected_dev,
320 sizeof(AudioDeviceID));
323 msg_Warn(p_aout, "we cannot select the audio device");
327 /* Get the current format */
328 i_param_size = sizeof(AudioStreamBasicDescription);
330 err = AudioUnitGetProperty(p_sys->au_unit,
331 kAudioUnitProperty_StreamFormat,
332 kAudioUnitScope_Input,
340 msg_Dbg(p_aout, STREAM_FORMAT_MSG("current format is: ", DeviceFormat));
342 /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
343 err = AudioUnitGetPropertyInfo(p_sys->au_unit,
344 kAudioDevicePropertyPreferredChannelLayout,
345 kAudioUnitScope_Output,
351 layout = (AudioChannelLayout *)malloc(i_param_size);
353 verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
354 kAudioDevicePropertyPreferredChannelLayout,
355 kAudioUnitScope_Output,
360 /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
361 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
362 /* bitmap defined channellayout */
363 verify_noerr(AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
364 sizeof(UInt32), &layout->mChannelBitmap,
367 } else if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions)
369 /* layouttags defined channellayout */
370 verify_noerr(AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
371 sizeof(AudioChannelLayoutTag), &layout->mChannelLayoutTag,
376 msg_Dbg(p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions);
378 /* Initialize the VLC core channel count */
379 fmt->i_physical_channels = 0;
380 i_original = fmt->i_original_channels & AOUT_CHAN_PHYSMASK;
382 if (i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2) {
383 /* We only need Mono or cannot output more than 1 channel */
384 fmt->i_physical_channels = AOUT_CHAN_CENTER;
385 } else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3) {
386 /* We only need Stereo or cannot output more than 2 channels */
387 fmt->i_physical_channels = AOUT_CHANS_STEREO;
389 /* We want more than stereo and we can do that */
390 for (unsigned int i = 0; i < layout->mNumberChannelDescriptions; i++) {
391 msg_Dbg(p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
393 switch(layout->mChannelDescriptions[i].mChannelLabel) {
394 case kAudioChannelLabel_Left:
395 fmt->i_physical_channels |= AOUT_CHAN_LEFT;
397 case kAudioChannelLabel_Right:
398 fmt->i_physical_channels |= AOUT_CHAN_RIGHT;
400 case kAudioChannelLabel_Center:
401 fmt->i_physical_channels |= AOUT_CHAN_CENTER;
403 case kAudioChannelLabel_LFEScreen:
404 fmt->i_physical_channels |= AOUT_CHAN_LFE;
406 case kAudioChannelLabel_LeftSurround:
407 fmt->i_physical_channels |= AOUT_CHAN_REARLEFT;
409 case kAudioChannelLabel_RightSurround:
410 fmt->i_physical_channels |= AOUT_CHAN_REARRIGHT;
412 case kAudioChannelLabel_RearSurroundLeft:
413 fmt->i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
415 case kAudioChannelLabel_RearSurroundRight:
416 fmt->i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
418 case kAudioChannelLabel_CenterSurround:
419 fmt->i_physical_channels |= AOUT_CHAN_REARCENTER;
422 msg_Warn(p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
425 if (fmt->i_physical_channels == 0) {
426 fmt->i_physical_channels = AOUT_CHANS_STEREO;
427 msg_Err(p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode.");
428 dialog_Fatal(p_aout, _("Audio device is not configured"), "%s",
429 _("You should configure your speaker layout with "
430 "the \"Audio Midi Setup\" utility in /Applications/"
431 "Utilities. Stereo mode is being used now."));
436 msg_Warn(p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!");
437 fmt->i_physical_channels = AOUT_CHANS_STEREO;
440 msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
441 msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
443 memset (&new_layout, 0, sizeof(new_layout));
444 switch(aout_FormatNbChannels(fmt)) {
446 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
449 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
452 if (fmt->i_physical_channels & AOUT_CHAN_CENTER)
453 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
454 else if (fmt->i_physical_channels & AOUT_CHAN_LFE)
455 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
458 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
459 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
460 else if (fmt->i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
461 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
462 else if (fmt->i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
463 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
466 if (fmt->i_physical_channels & (AOUT_CHAN_CENTER))
467 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
468 else if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
469 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
472 if (fmt->i_physical_channels & (AOUT_CHAN_LFE))
473 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
475 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
478 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
479 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
482 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
483 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
487 /* Set up the format to be used */
488 DeviceFormat.mSampleRate = fmt->i_rate;
489 DeviceFormat.mFormatID = kAudioFormatLinearPCM;
491 /* We use float 32. It's the best supported format by both VLC and Coreaudio */
492 fmt->i_format = VLC_CODEC_FL32;
493 DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
494 DeviceFormat.mBitsPerChannel = 32;
495 DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(fmt);
497 /* Calculate framesizes and stuff */
498 DeviceFormat.mFramesPerPacket = 1;
499 DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
500 DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
502 /* Set the desired format */
503 i_param_size = sizeof(AudioStreamBasicDescription);
504 verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
505 kAudioUnitProperty_StreamFormat,
506 kAudioUnitScope_Input,
511 msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , DeviceFormat));
513 /* Retrieve actual format */
514 verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
515 kAudioUnitProperty_StreamFormat,
516 kAudioUnitScope_Input,
521 msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , DeviceFormat));
523 /* Do the last VLC aout setups */
524 aout_FormatPrepare(fmt);
525 aout_PacketInit(p_aout, &p_sys->packet, FRAMESIZE, fmt);
527 /* set the IOproc callback */
528 input.inputProc = (AURenderCallback) RenderCallbackAnalog;
529 input.inputProcRefCon = p_aout;
531 verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
532 kAudioUnitProperty_SetRenderCallback,
533 kAudioUnitScope_Input,
534 0, &input, sizeof(input)));
536 input.inputProc = (AURenderCallback) RenderCallbackAnalog;
537 input.inputProcRefCon = p_aout;
539 /* Set the new_layout as the layout VLC will use to feed the AU unit */
540 verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
541 kAudioUnitProperty_AudioChannelLayout,
542 kAudioUnitScope_Input,
543 0, &new_layout, sizeof(new_layout)));
545 if (new_layout.mNumberChannelDescriptions > 0)
546 free(new_layout.mChannelDescriptions);
549 verify_noerr(AudioUnitInitialize(p_sys->au_unit));
551 /* Find the difference between device clock and mdate clock */
552 p_sys->clock_diff = - (mtime_t)
553 AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
554 p_sys->clock_diff += mdate();
557 verify_noerr(AudioOutputUnitStart(p_sys->au_unit));
559 /* Set volume for output unit */
560 float volume = var_InheritInteger(p_aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT;
561 verify_noerr(AudioUnitSetParameter(p_sys->au_unit,
562 kHALOutputParam_Volume,
563 kAudioUnitScope_Global,
565 volume * volume * volume,
571 /*****************************************************************************
572 * Setup a encoded digital stream (SPDIF)
573 *****************************************************************************/
574 static int OpenSPDIF (audio_output_t * p_aout, audio_sample_format_t *fmt)
576 struct aout_sys_t *p_sys = p_aout->sys;
577 OSStatus err = noErr;
578 UInt32 i_param_size = 0, b_mix = 0;
579 Boolean b_writeable = false;
580 AudioStreamID *p_streams = NULL;
581 unsigned i_streams = 0;
583 /* Start doing the SPDIF setup proces */
584 p_sys->b_digital = true;
587 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
588 i_param_size = sizeof(p_sys->i_hog_pid);
589 p_sys->i_hog_pid = getpid() ;
591 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
594 msg_Err(p_aout, "failed to set hogmode: [%4.4s]", (char *)&err);
598 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
600 if (AudioObjectHasProperty(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress)) {
601 /* Set mixable to false if we are allowed to */
602 err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
603 err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size);
604 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
606 if (err == noErr && b_writeable) {
608 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
609 p_sys->b_changed_mixing = true;
613 msg_Err(p_aout, "failed to set mixmode: [%4.4s]", (char *)&err);
618 /* Get a list of all the streams on this device */
619 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
620 err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
622 msg_Err(p_aout, "could not get number of streams: [%4.4s]", (char *)&err);
626 i_streams = i_param_size / sizeof(AudioStreamID);
627 p_streams = (AudioStreamID *)malloc(i_param_size);
628 if (p_streams == NULL)
631 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
634 msg_Err(p_aout, "could not get number of streams: [%4.4s]", (char *)&err);
639 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
640 for (unsigned i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++) {
641 /* Find a stream with a cac3 stream */
642 AudioStreamRangedDescription *p_format_list = NULL;
644 bool b_digital = false;
646 /* Retrieve all the stream formats supported by each output stream */
647 err = AudioObjectGetPropertyDataSize(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size);
649 msg_Err(p_aout, "OpenSPDIF: could not get number of streamformats: [%s] (%i)", (char *)&err, (int32_t)err);
653 i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
654 p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
655 if (p_format_list == NULL)
658 err = AudioObjectGetPropertyData(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
660 msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
665 /* Check if one of the supported formats is a digital format */
666 for (int j = 0; j < i_formats; j++) {
667 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
668 p_format_list[j].mFormat.mFormatID == 'iac3' ||
669 p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
670 p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
677 /* if this stream supports a digital (cac3) format, then go set it. */
678 int i_requested_rate_format = -1;
679 int i_current_rate_format = -1;
680 int i_backup_rate_format = -1;
682 p_sys->i_stream_id = p_streams[i];
683 p_sys->i_stream_index = i;
685 if (!p_sys->b_revert) {
686 AudioObjectPropertyAddress currentPhysicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
687 /* Retrieve the original format of this stream first if not done so already */
688 i_param_size = sizeof(p_sys->sfmt_revert);
689 err = AudioObjectGetPropertyData(p_sys->i_stream_id, ¤tPhysicalFormatAddress, 0, NULL, &i_param_size, &p_sys->sfmt_revert);
691 msg_Err(p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err);
694 p_sys->b_revert = true;
697 for (int j = 0; j < i_formats; j++) {
698 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
699 p_format_list[j].mFormat.mFormatID == 'iac3' ||
700 p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
701 p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
702 if (p_format_list[j].mFormat.mSampleRate == fmt->i_rate) {
703 i_requested_rate_format = j;
705 } else if (p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate)
706 i_current_rate_format = j;
708 if (i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate)
709 i_backup_rate_format = j;
715 if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio */
716 p_sys->stream_format = p_format_list[i_requested_rate_format].mFormat;
717 else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device */
718 p_sys->stream_format = p_format_list[i_current_rate_format].mFormat;
720 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) */
726 /* get notified when we don't have spdif-output anymore */
727 err = AudioObjectAddPropertyListener(p_sys->i_stream_id, &physicalFormatsAddress, HardwareListener, (void *)p_aout);
729 msg_Warn(p_aout, "could not set audio device property streams callback on device: %4.4s",
733 msg_Dbg(p_aout, STREAM_FORMAT_MSG("original stream format: ", p_sys->sfmt_revert));
735 if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->stream_format))
738 /* Set the format flags */
739 if (p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
740 fmt->i_format = VLC_CODEC_SPDIFB;
742 fmt->i_format = VLC_CODEC_SPDIFL;
743 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
744 fmt->i_frame_length = A52_FRAME_NB;
745 fmt->i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
746 aout_FormatPrepare(fmt);
747 aout_PacketInit(p_aout, &p_sys->packet, A52_FRAME_NB, fmt);
749 /* Add IOProc callback */
750 err = AudioDeviceCreateIOProcID(p_sys->i_selected_dev,
751 (AudioDeviceIOProc)RenderCallbackSPDIF,
755 msg_Err(p_aout, "AudioDeviceCreateIOProcID failed: [%4.4s]", (char *)&err);
756 aout_PacketDestroy (p_aout);
760 /* Check for the difference between the Device clock and mdate */
761 p_sys->clock_diff = - (mtime_t)
762 AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
763 p_sys->clock_diff += mdate();
766 err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
768 msg_Err(p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err);
770 err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev, p_sys->i_procID);
772 msg_Err(p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err);
774 aout_PacketDestroy (p_aout);
782 /*****************************************************************************
783 * Close: Close HAL AudioUnit
784 *****************************************************************************/
785 static void Stop(audio_output_t *p_aout)
787 struct aout_sys_t *p_sys = p_aout->sys;
788 OSStatus err = noErr;
789 UInt32 i_param_size = 0;
791 AudioObjectPropertyAddress deviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
792 err = AudioObjectRemovePropertyListener(p_sys->i_selected_dev, &deviceAliveAddress, HardwareListener, NULL);
794 msg_Err(p_aout, "failed to remove audio device life checker: [%4.4s]", (char *)&err);
796 if (p_sys->b_digital) {
797 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
798 err = AudioObjectRemovePropertyListener(p_sys->i_stream_id, &physicalFormatsAddress, HardwareListener, NULL);
800 msg_Err(p_aout, "failed to remove audio device property streams callback: [%4.4s]", (char *)&err);
803 if (p_sys->au_unit) {
804 verify_noerr(AudioOutputUnitStop(p_sys->au_unit));
805 verify_noerr(AudioUnitUninitialize(p_sys->au_unit));
806 verify_noerr(CloseComponent(p_sys->au_unit));
809 if (p_sys->b_digital) {
811 err = AudioDeviceStop(p_sys->i_selected_dev,
814 msg_Err(p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err);
816 /* Remove IOProc callback */
817 err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev,
820 msg_Err(p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err);
823 AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->sfmt_revert);
825 if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
827 Boolean b_writeable = false;
828 /* Revert mixable to true if we are allowed to */
829 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
830 err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
831 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
833 if (err == noErr && b_writeable) {
834 msg_Dbg(p_aout, "mixable is: %d", b_mix);
836 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
840 msg_Err(p_aout, "failed to set mixmode: [%4.4s]", (char *)&err);
844 AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
845 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, NULL);
848 msg_Err(p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err);
850 if (p_sys->i_hog_pid == getpid()) {
851 p_sys->i_hog_pid = -1;
852 i_param_size = sizeof(p_sys->i_hog_pid);
853 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
854 kAudioDevicePropertyScopeOutput,
855 kAudioObjectPropertyElementMaster };
856 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
858 msg_Err(p_aout, "Could not release hogmode: [%4.4s]", (char *)&err);
861 var_DelCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
863 aout_PacketDestroy(p_aout);
866 /*****************************************************************************
867 * Probe: Check which devices the OS has, and add them to our audio-device menu
868 *****************************************************************************/
869 static void Probe(audio_output_t * p_aout)
871 OSStatus err = noErr;
872 UInt32 i_param_size = 0;
873 AudioDeviceID devid_def = 0;
874 AudioDeviceID *p_devices = NULL;
875 vlc_value_t val, text;
877 struct aout_sys_t *p_sys = p_aout->sys;
879 /* Get number of devices */
880 AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
881 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size);
883 msg_Err(p_aout, "Could not get number of devices: [%s]", (char *)&err);
887 p_sys->i_devices = i_param_size / sizeof(AudioDeviceID);
889 if (p_sys->i_devices < 1) {
890 msg_Err(p_aout, "No audio output devices were found.");
893 msg_Dbg(p_aout, "found %u audio device(s)", (unsigned)p_sys->i_devices);
895 /* Allocate DeviceID array */
896 p_devices = (AudioDeviceID*)malloc(sizeof(AudioDeviceID) * p_sys->i_devices);
897 if (p_devices == NULL)
900 /* Populate DeviceID array */
901 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size, p_devices);
903 msg_Err(p_aout, "could not get the device IDs: [%s]", (char *)&err);
907 /* Find the ID of the default Device */
908 AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
909 i_param_size = sizeof(AudioDeviceID);
910 err= AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &i_param_size, &devid_def);
912 msg_Err(p_aout, "could not get default audio device: [%s]", (char *)&err);
915 p_sys->i_default_dev = devid_def;
917 var_Create(p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
918 text.psz_string = (char*)_("Audio Device");
919 var_Change(p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
921 AudioObjectPropertyAddress deviceNameAddress = { kAudioDevicePropertyDeviceName, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
923 for (unsigned int i = 0; i < p_sys->i_devices; i++) {
927 /* Retrieve the length of the device name */
928 err = AudioObjectGetPropertyDataSize(p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size);
932 /* Retrieve the name of the device */
933 psz_name = (char *)malloc(i_param_size);
934 err = AudioObjectGetPropertyData(p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size, psz_name);
938 msg_Dbg(p_aout, "DevID: %u DevName: %s", (unsigned)p_devices[i], psz_name);
940 if (!AudioDeviceHasOutput(p_devices[i])) {
941 msg_Dbg(p_aout, "this device is INPUT only. skipping...");
946 /* Add the menu entries */
947 val.i_int = (int)p_devices[i];
948 text.psz_string = psz_name;
949 var_Change(p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
950 text.psz_string = NULL;
951 if (p_sys->i_default_dev == p_devices[i]) {
952 /* The default device is the selected device normally */
953 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
954 var_Set(p_aout, "audio-device", val);
957 if (AudioDeviceSupportsDigital(p_aout, p_devices[i])) {
958 val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
959 if (asprintf(&text.psz_string, _("%s (Encoded Output)"), psz_name) != -1) {
960 var_Change(p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
961 free(text.psz_string);
962 if (p_sys->i_default_dev == p_devices[i] && var_InheritBool(p_aout, "spdif")) {
963 /* We selected to prefer SPDIF output if available
964 * then this "dummy" entry should be selected */
965 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
966 var_Set(p_aout, "audio-device", val);
974 /* If a device is already "preselected", then use this device */
975 var_Get(p_aout->p_libvlc, "macosx-audio-device", &val);
977 msg_Dbg(p_aout, "using preselected output device %#"PRIx64, val.i_int);
978 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
979 var_Set(p_aout, "audio-device", val);
982 /* Attach a Listener so that we are notified of a change in the Device setup */
983 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, (void *)p_aout);
985 msg_Warn(p_aout, "failed to add listener for audio device configuration (%i)", err);
993 msg_Warn(p_aout, "audio device already in use");
998 /*****************************************************************************
999 * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
1000 *****************************************************************************/
1001 static int AudioDeviceHasOutput(AudioDeviceID i_dev_id)
1005 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1006 verify_noerr(AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &dataSize));
1013 /*****************************************************************************
1014 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1015 *****************************************************************************/
1016 static int AudioDeviceSupportsDigital(audio_output_t *p_aout, AudioDeviceID i_dev_id)
1018 OSStatus err = noErr;
1019 UInt32 i_param_size = 0;
1020 AudioStreamID *p_streams = NULL;
1022 bool b_return = false;
1024 /* Retrieve all the output streams */
1025 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1026 err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1028 msg_Err(p_aout, "could not get number of streams: [%s] (%i)", (char *)&err, (int32_t)err);
1032 i_streams = i_param_size / sizeof(AudioStreamID);
1033 p_streams = (AudioStreamID *)malloc(i_param_size);
1034 if (p_streams == NULL)
1037 err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1039 msg_Err(p_aout, "could not get list of streams: [%s]", (char *)&err);
1043 for (int i = 0; i < i_streams; i++) {
1044 if (AudioStreamSupportsDigital(p_aout, p_streams[i]))
1052 /*****************************************************************************
1053 * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1054 *****************************************************************************/
1055 static int AudioStreamSupportsDigital(audio_output_t *p_aout, AudioStreamID i_stream_id)
1057 OSStatus err = noErr;
1058 UInt32 i_param_size = 0;
1059 AudioStreamRangedDescription *p_format_list = NULL;
1061 bool b_return = false;
1063 /* Retrieve all the stream formats supported by each output stream */
1064 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1065 err = AudioObjectGetPropertyDataSize(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size);
1067 msg_Err(p_aout, "could not get number of streamformats: [%s] (%i)", (char *)&err, (int32_t)err);
1071 i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
1072 msg_Dbg(p_aout, "found %i stream formats", i_formats);
1074 p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
1075 if (p_format_list == NULL)
1078 err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
1080 msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
1081 free(p_format_list);
1082 p_format_list = NULL;
1086 for (int i = 0; i < i_formats; i++) {
1087 msg_Dbg(p_aout, STREAM_FORMAT_MSG("supported format: ", p_format_list[i].mFormat));
1089 if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
1090 p_format_list[i].mFormat.mFormatID == 'iac3' ||
1091 p_format_list[i].mFormat.mFormatID == kAudioFormat60958AC3 ||
1092 p_format_list[i].mFormat.mFormatID == kAudioFormatAC3)
1096 free(p_format_list);
1100 /*****************************************************************************
1101 * AudioStreamChangeFormat: Change i_stream_id to change_format
1102 *****************************************************************************/
1103 static int AudioStreamChangeFormat(audio_output_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format)
1105 OSStatus err = noErr;
1106 UInt32 i_param_size = 0;
1108 AudioObjectPropertyAddress physicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1110 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1112 msg_Dbg(p_aout, STREAM_FORMAT_MSG("setting stream format: ", change_format));
1114 /* Condition because SetProperty is asynchronious */
1115 vlc_cond_init(&w.cond);
1116 vlc_mutex_init(&w.lock);
1117 vlc_mutex_lock(&w.lock);
1119 /* Install the callback */
1120 err = AudioObjectAddPropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1122 msg_Err(p_aout, "AudioObjectAddPropertyListener for kAudioStreamPropertyPhysicalFormat failed: [%4.4s]", (char *)&err);
1126 /* change the format */
1127 err = AudioObjectSetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, sizeof(AudioStreamBasicDescription),
1130 msg_Err(p_aout, "could not set the stream format: [%4.4s]", (char *)&err);
1134 /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1135 * it is also not atomic in its behaviour.
1136 * Therefore we check 5 times before we really give up.
1137 * FIXME: failing isn't actually implemented yet. */
1138 for (int i = 0; i < 5; i++) {
1139 AudioStreamBasicDescription actual_format;
1140 mtime_t timeout = mdate() + 500000;
1142 if (vlc_cond_timedwait(&w.cond, &w.lock, timeout))
1143 msg_Dbg(p_aout, "reached timeout");
1145 i_param_size = sizeof(AudioStreamBasicDescription);
1146 err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, &i_param_size, &actual_format);
1148 msg_Dbg(p_aout, STREAM_FORMAT_MSG("actual format in use: ", actual_format));
1149 if (actual_format.mSampleRate == change_format.mSampleRate &&
1150 actual_format.mFormatID == change_format.mFormatID &&
1151 actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
1152 /* The right format is now active */
1155 /* We need to check again */
1158 /* Removing the property listener */
1159 err = AudioObjectRemovePropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1161 msg_Err(p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err);
1165 /* Destroy the lock and condition */
1166 vlc_mutex_unlock(&w.lock);
1167 vlc_mutex_destroy(&w.lock);
1168 vlc_cond_destroy(&w.cond);
1173 /*****************************************************************************
1174 * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1175 * us to provide some more audio data.
1176 * Don't print anything during normal playback, calling blocking function from
1177 * this callback is not allowed.
1178 *****************************************************************************/
1179 static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
1180 AudioUnitRenderActionFlags *ioActionFlags,
1181 const AudioTimeStamp *inTimeStamp,
1182 unsigned int inBusNumber,
1183 unsigned int inNumberFrames,
1184 AudioBufferList *ioData)
1186 AudioTimeStamp host_time;
1187 mtime_t current_date = 0;
1188 uint32_t i_mData_bytes = 0;
1190 audio_output_t * p_aout = (audio_output_t *)_p_aout;
1191 struct aout_sys_t * p_sys = p_aout->sys;
1193 VLC_UNUSED(ioActionFlags);
1194 VLC_UNUSED(inBusNumber);
1195 VLC_UNUSED(inNumberFrames);
1197 host_time.mFlags = kAudioTimeStampHostTimeValid;
1198 AudioDeviceTranslateTime(p_sys->i_selected_dev, inTimeStamp, &host_time);
1200 /* Check for the difference between the Device clock and mdate */
1201 p_sys->clock_diff = - (mtime_t)
1202 AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
1203 p_sys->clock_diff += mdate();
1205 current_date = p_sys->clock_diff +
1206 AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
1207 //- ((mtime_t) 1000000 / p_aout->format.i_rate * 31); // 31 = Latency in Frames. retrieve somewhere
1209 if (ioData == NULL || ioData->mNumberBuffers < 1) {
1210 msg_Err(p_aout, "no iodata or buffers");
1213 if (ioData->mNumberBuffers > 1)
1214 msg_Err(p_aout, "well this is weird. seems like there is more than one buffer...");
1217 if (p_sys->i_total_bytes > 0) {
1218 i_mData_bytes = __MIN(p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize);
1219 memcpy(ioData->mBuffers[0].mData,
1220 &p_sys->p_remainder_buffer[p_sys->i_read_bytes],
1222 p_sys->i_read_bytes += i_mData_bytes;
1223 current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
1224 (i_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
1226 if (p_sys->i_read_bytes >= p_sys->i_total_bytes)
1227 p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
1230 while(i_mData_bytes < ioData->mBuffers[0].mDataByteSize) {
1231 /* We don't have enough data yet */
1233 p_buffer = aout_PacketNext(p_aout, current_date);
1235 if (p_buffer != NULL)
1237 uint32_t i_second_mData_bytes = __MIN(p_buffer->i_buffer, ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
1239 memcpy((uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes,
1240 p_buffer->p_buffer, i_second_mData_bytes);
1241 i_mData_bytes += i_second_mData_bytes;
1243 if (i_mData_bytes >= ioData->mBuffers[0].mDataByteSize)
1245 p_sys->i_total_bytes = p_buffer->i_buffer - i_second_mData_bytes;
1246 memcpy(p_sys->p_remainder_buffer,
1247 &p_buffer->p_buffer[i_second_mData_bytes],
1248 p_sys->i_total_bytes);
1249 block_Release(p_buffer);
1252 /* update current_date */
1253 current_date += (mtime_t) ((mtime_t) 1000000 / p_sys->packet.format.i_rate) *
1254 (i_second_mData_bytes / 4 / aout_FormatNbChannels(&p_sys->packet.format)); // 4 is fl32 specific
1255 block_Release(p_buffer);
1257 memset((uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes,
1258 0,ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
1259 i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
1265 /*****************************************************************************
1266 * RenderCallbackSPDIF: callback for SPDIF audio output
1267 *****************************************************************************/
1268 static OSStatus RenderCallbackSPDIF (AudioDeviceID inDevice,
1269 const AudioTimeStamp * inNow,
1270 const void * inInputData,
1271 const AudioTimeStamp * inInputTime,
1272 AudioBufferList * outOutputData,
1273 const AudioTimeStamp * inOutputTime,
1274 void * threadGlobals)
1277 mtime_t current_date;
1279 audio_output_t * p_aout = (audio_output_t *)threadGlobals;
1280 struct aout_sys_t * p_sys = p_aout->sys;
1282 VLC_UNUSED(inDevice);
1283 VLC_UNUSED(inInputData);
1284 VLC_UNUSED(inInputTime);
1286 /* Check for the difference between the Device clock and mdate */
1287 p_sys->clock_diff = - (mtime_t)
1288 AudioConvertHostTimeToNanos(inNow->mHostTime) / 1000;
1289 p_sys->clock_diff += mdate();
1291 current_date = p_sys->clock_diff +
1292 AudioConvertHostTimeToNanos(inOutputTime->mHostTime) / 1000;
1293 //- ((mtime_t) 1000000 / p_aout->format.i_rate * 31); // 31 = Latency in Frames. retrieve somewhere
1295 p_buffer = aout_PacketNext(p_aout, current_date);
1297 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1298 if (p_buffer != NULL) {
1299 if ((int)BUFFER.mDataByteSize != (int)p_buffer->i_buffer)
1300 msg_Warn(p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_buffer);
1302 /* move data into output data buffer */
1303 memcpy(BUFFER.mData, p_buffer->p_buffer, p_buffer->i_buffer);
1304 block_Release(p_buffer);
1307 memset(BUFFER.mData, 0, BUFFER.mDataByteSize);
1313 /*****************************************************************************
1314 * HardwareListener: Warns us of changes in the list of registered devices
1315 *****************************************************************************/
1316 static OSStatus HardwareListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
1318 OSStatus err = noErr;
1319 audio_output_t *p_aout = (audio_output_t *)inClientData;
1320 VLC_UNUSED(inObjectID);
1322 for (unsigned int i = 0; i < inNumberAddresses; i++) {
1323 if (inAddresses[i].mSelector == kAudioHardwarePropertyDevices) {
1324 /* something changed in the list of devices */
1325 /* We trigger the audio-device's aout_ChannelsRestart callback */
1326 msg_Warn(p_aout, "audio device configuration changed, resetting cache");
1327 var_TriggerCallback(p_aout, "audio-device");
1328 var_Destroy(p_aout, "audio-device");
1329 } else if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceIsAlive) {
1330 msg_Warn(p_aout, "audio device died, resetting aout");
1331 var_TriggerCallback(p_aout, "audio-device");
1332 var_Destroy(p_aout, "audio-device");
1333 } else if (inAddresses[i].mSelector == kAudioStreamPropertyAvailablePhysicalFormats) {
1334 msg_Warn(p_aout, "available physical formats for audio device changed, resetting aout");
1335 var_TriggerCallback(p_aout, "audio-device");
1336 var_Destroy(p_aout, "audio-device");
1343 /*****************************************************************************
1345 *****************************************************************************/
1346 static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
1348 OSStatus err = noErr;
1349 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1351 VLC_UNUSED(inObjectID);
1353 for (unsigned int i = 0; i < inNumberAddresses; i++) {
1354 if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
1355 vlc_mutex_lock(&w->lock);
1356 vlc_cond_signal(&w->cond);
1357 vlc_mutex_unlock(&w->lock);
1364 /*****************************************************************************
1365 * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1366 *****************************************************************************/
1367 static int AudioDeviceCallback(vlc_object_t *p_this, const char *psz_variable,
1368 vlc_value_t old_val, vlc_value_t new_val, void *param)
1370 audio_output_t *p_aout = (audio_output_t *)p_this;
1371 var_Set(p_aout->p_libvlc, "macosx-audio-device", new_val);
1372 msg_Dbg(p_aout, "Set Device: %#"PRIx64, new_val.i_int);
1373 return aout_ChannelsRestart(p_this, psz_variable, old_val, new_val, param);
1377 /*****************************************************************************
1378 * VolumeSet: Implements volume_set(). Update the CoreAudio AU volume immediately.
1379 *****************************************************************************/
1380 static int VolumeSet(audio_output_t * p_aout, float volume)
1382 struct aout_sys_t *p_sys = p_aout->sys;
1385 aout_VolumeReport(p_aout, volume);
1387 /* Set volume for output unit */
1388 ostatus = AudioUnitSetParameter(p_sys->au_unit,
1389 kHALOutputParam_Volume,
1390 kAudioUnitScope_Global,
1392 volume * volume * volume,
1395 if (var_InheritBool(p_aout, "volume-save"))
1396 config_PutInt(p_aout, "auhal-volume", lroundf(volume * AOUT_VOLUME_DEFAULT));
1401 static int MuteSet(audio_output_t * p_aout, bool mute)
1403 struct aout_sys_t *p_sys = p_aout->sys;
1406 aout_MuteReport(p_aout, mute);
1411 volume = var_InheritInteger(p_aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT;
1413 ostatus = AudioUnitSetParameter(p_sys->au_unit,
1414 kHALOutputParam_Volume,
1415 kAudioUnitScope_Global,
1417 volume * volume * volume,
1423 static int Open(vlc_object_t *obj)
1425 audio_output_t *aout = (audio_output_t *)obj;
1426 aout_sys_t *sys = malloc(sizeof (*sys));
1428 if (unlikely(sys == NULL))
1431 aout->start = Start;
1433 aout->volume_set = VolumeSet;
1434 aout->mute_set = MuteSet;
1436 /* remember the volume */
1437 msg_Warn(aout, "we got %lli, lroundf=%f", var_InheritInteger(aout, "auhal-volume"), (var_InheritInteger(aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT));
1438 aout_VolumeReport(aout, var_InheritInteger(aout, "auhal-volume") / (float)AOUT_VOLUME_DEFAULT);
1439 MuteSet(aout, var_InheritBool(aout, "mute"));
1444 static void Close(vlc_object_t *obj)
1446 audio_output_t *aout = (audio_output_t *)obj;
1447 aout_sys_t *sys = aout->sys;