1 /*****************************************************************************
2 * auhal.c: AUHAL and Coreaudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2005, 2012 the VideoLAN team
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
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU 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_*
37 #import <vlc_aout_intf.h>
39 #import <AudioUnit/AudioUnit.h> // AudioUnit
40 #import <CoreAudio/CoreAudio.h> // AudioDeviceID
41 #import <AudioToolbox/AudioFormat.h> // AudioFormatGetProperty
42 #import <CoreServices/CoreServices.h>
45 # define verify_noerr(a) assert((a) == noErr)
48 #define STREAM_FORMAT_MSG(pre, sfm) \
49 pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
50 sfm.mSampleRate, (char *)&sfm.mFormatID, \
51 (unsigned int)sfm.mFormatFlags, (unsigned int)sfm.mBytesPerPacket, \
52 (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
53 (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
55 #define FRAMESIZE 2048
56 #define BUFSIZE (FRAMESIZE * 8) * 8
57 #define AOUT_VAR_SPDIF_FLAG 0xf00000
61 * - clean up the debug info
62 * - be better at changing stream setup or devices setup changes while playing.
66 /*****************************************************************************
67 * aout_sys_t: private audio output method descriptor
68 *****************************************************************************
69 * This structure is part of the audio output thread descriptor.
70 * It describes the CoreAudio specific properties of an output thread.
71 *****************************************************************************/
75 AudioDeviceID i_default_dev; /* DeviceID of defaultOutputDevice */
76 AudioDeviceID i_selected_dev; /* DeviceID of the selected device */
77 AudioDeviceIOProcID i_procID; /* DeviceID of current device */
78 UInt32 i_devices; /* Number of CoreAudio Devices */
79 bool b_digital; /* Are we running in digital mode? */
80 mtime_t clock_diff; /* Difference between VLC clock and Device clock */
83 Component au_component; /* The Audiocomponent we use */
84 AudioUnit au_unit; /* The AudioUnit we use */
85 uint8_t p_remainder_buffer[BUFSIZE];
86 uint32_t i_read_bytes;
87 uint32_t i_total_bytes;
89 /* CoreAudio SPDIF mode specific */
90 pid_t i_hog_pid; /* The keep the pid of our hog status */
91 AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */
92 int i_stream_index; /* The index of i_stream_id in an AudioBufferList */
93 AudioStreamBasicDescription stream_format; /* The format we changed the stream to */
94 AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */
95 bool b_revert; /* Wether we need to revert the stream format */
96 bool b_changed_mixing; /* Wether we need to set the mixing mode back */
99 /*****************************************************************************
101 *****************************************************************************/
102 static int Open (vlc_object_t *);
103 static int OpenAnalog (audio_output_t *);
104 static int OpenSPDIF (audio_output_t *);
105 static void Close (vlc_object_t *);
107 static void Probe (audio_output_t *);
109 static int AudioDeviceHasOutput (AudioDeviceID);
110 static int AudioDeviceSupportsDigital(audio_output_t *, AudioDeviceID);
111 static int AudioStreamSupportsDigital(audio_output_t *, AudioStreamID);
112 static int AudioStreamChangeFormat (audio_output_t *, AudioStreamID, AudioStreamBasicDescription);
114 static OSStatus RenderCallbackAnalog (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
115 unsigned int, unsigned int, AudioBufferList *);
116 static OSStatus RenderCallbackSPDIF (AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
117 AudioBufferList *, const AudioTimeStamp *, void *);
118 static OSStatus HardwareListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
119 static OSStatus StreamListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
120 static int AudioDeviceCallback (vlc_object_t *, const char *,
121 vlc_value_t, vlc_value_t, void *);
123 static int VolumeSet (audio_output_t *, float);
124 static int MuteSet (audio_output_t *, bool);
127 /*****************************************************************************
129 *****************************************************************************/
130 #define ADEV_TEXT N_("Audio Device")
131 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
132 "audio device, as listed in your 'Audio Device' menu. This device will " \
133 "then be used by default for audio playback.")
136 set_shortname("auhal")
137 set_description(N_("HAL AudioUnit output"))
138 set_capability("audio output", 101)
139 set_category(CAT_AUDIO)
140 set_subcategory(SUBCAT_AUDIO_AOUT)
141 set_callbacks(Open, Close)
142 add_integer("macosx-audio-device", 0, ADEV_TEXT, ADEV_LONGTEXT, false)
145 /*****************************************************************************
146 * Open: open macosx audio output
147 *****************************************************************************/
148 static int Open(vlc_object_t * p_this)
150 OSStatus err = noErr;
151 UInt32 i_param_size = 0;
152 struct aout_sys_t *p_sys = NULL;
154 audio_output_t *p_aout = (audio_output_t *)p_this;
156 /* Use int here, to match kAudioDevicePropertyDeviceIsAlive
160 /* Allocate structure */
161 p_aout->sys = malloc(sizeof(aout_sys_t));
162 if (p_aout->sys == NULL)
166 p_sys->i_default_dev = 0;
167 p_sys->i_selected_dev = 0;
168 p_sys->i_devices = 0;
169 p_sys->b_digital = false;
170 p_sys->au_component = NULL;
171 p_sys->au_unit = NULL;
172 p_sys->clock_diff = (mtime_t) 0;
173 p_sys->i_read_bytes = 0;
174 p_sys->i_total_bytes = 0;
175 p_sys->i_hog_pid = -1;
176 p_sys->i_stream_id = 0;
177 p_sys->i_stream_index = -1;
178 p_sys->b_revert = false;
179 p_sys->b_changed_mixing = false;
180 memset(p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE);
182 p_aout->play = aout_PacketPlay;
183 p_aout->pause = aout_PacketPause;
184 p_aout->flush = aout_PacketFlush;
186 aout_FormatPrint(p_aout, "VLC is looking for:", &p_aout->format);
188 /* Persistent device variable */
189 if (var_Type(p_aout->p_libvlc, "macosx-audio-device") == 0)
190 var_Create(p_aout->p_libvlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
192 /* Build a list of devices */
193 if (var_Type(p_aout, "audio-device") == 0)
196 /* What device do we want? */
197 if (var_Get(p_aout, "audio-device", &val) < 0) {
198 msg_Err(p_aout, "audio-device var does not exist. device probe failed.");
202 p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
203 bool b_supports_digital = (val.i_int & AOUT_VAR_SPDIF_FLAG);
204 if (b_supports_digital)
205 msg_Dbg(p_aout, "audio device supports digital output");
207 /* Check if the desired device is alive and usable */
208 i_param_size = sizeof(b_alive);
209 AudioObjectPropertyAddress audioDeviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive,
210 kAudioObjectPropertyScopeGlobal,
211 kAudioObjectPropertyElementMaster };
212 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceAliveAddress, 0, NULL, &i_param_size, &b_alive);
215 /* Be tolerant, only give a warning here */
216 msg_Warn(p_aout, "could not check whether device [0x%x] is alive: %4.4s",
217 (unsigned int)p_sys->i_selected_dev, (char *)&err);
222 msg_Warn(p_aout, "selected audio device is not alive, switching to default device");
223 p_sys->i_selected_dev = p_sys->i_default_dev;
226 /* add a callback to see if the device dies later on */
227 err = AudioObjectAddPropertyListener(p_sys->i_selected_dev, &audioDeviceAliveAddress, HardwareListener, (void *)p_aout);
229 /* Be tolerant, only give a warning here */
230 msg_Warn(p_aout, "could not set alive check callback on device [0x%x]: %4.4s",
231 (unsigned int)p_sys->i_selected_dev, (char *)&err);
234 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
235 kAudioDevicePropertyScopeOutput,
236 kAudioObjectPropertyElementMaster };
237 i_param_size = sizeof(p_sys->i_hog_pid);
238 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, &i_param_size, &p_sys->i_hog_pid);
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()) {
247 msg_Err(p_aout, "Selected audio device is exclusively in use by another program.");
248 dialog_Fatal(p_aout, _("Audio output failed"), "%s",
249 _("The selected audio output device is exclusively in "
250 "use by another program."));
254 /* If we change the device we want to use, we should renegotiate the audio chain */
255 var_AddCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
257 /* Check for Digital mode or Analog output mode */
258 if (AOUT_FMT_SPDIF (&p_aout->format) && b_supports_digital) {
259 if (OpenSPDIF (p_aout)) {
260 msg_Dbg(p_aout, "digital output successfully opened");
264 if (OpenAnalog(p_aout)) {
265 msg_Dbg(p_aout, "analog output successfully opened");
271 /* If we reach this, this aout has failed */
272 msg_Err(p_aout, "opening the auhal output failed");
273 var_Destroy(p_aout, "audio-device");
278 /*****************************************************************************
279 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
280 *****************************************************************************/
281 static int OpenAnalog(audio_output_t *p_aout)
283 struct aout_sys_t *p_sys = p_aout->sys;
284 OSStatus err = noErr;
285 UInt32 i_param_size = 0;
287 ComponentDescription desc;
288 AudioStreamBasicDescription DeviceFormat;
289 AudioChannelLayout *layout;
290 AudioChannelLayout new_layout;
291 AURenderCallbackStruct input;
293 /* Lets go find our Component */
294 desc.componentType = kAudioUnitType_Output;
295 desc.componentSubType = kAudioUnitSubType_HALOutput;
296 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
297 desc.componentFlags = 0;
298 desc.componentFlagsMask = 0;
300 p_sys->au_component = FindNextComponent(NULL, &desc);
301 if (p_sys->au_component == NULL) {
302 msg_Warn(p_aout, "we cannot find our HAL component");
306 err = OpenAComponent(p_sys->au_component, &p_sys->au_unit);
308 msg_Warn(p_aout, "we cannot open our HAL component");
312 /* Set the device we will use for this output unit */
313 err = AudioUnitSetProperty(p_sys->au_unit,
314 kAudioOutputUnitProperty_CurrentDevice,
315 kAudioUnitScope_Global,
317 &p_sys->i_selected_dev,
318 sizeof(AudioDeviceID));
321 msg_Warn(p_aout, "we cannot select the audio device");
325 /* Get the current format */
326 i_param_size = sizeof(AudioStreamBasicDescription);
328 err = AudioUnitGetProperty(p_sys->au_unit,
329 kAudioUnitProperty_StreamFormat,
330 kAudioUnitScope_Input,
338 msg_Dbg(p_aout, STREAM_FORMAT_MSG("current format is: ", DeviceFormat));
340 /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
341 err = AudioUnitGetPropertyInfo(p_sys->au_unit,
342 kAudioDevicePropertyPreferredChannelLayout,
343 kAudioUnitScope_Output,
349 layout = (AudioChannelLayout *)malloc(i_param_size);
351 verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
352 kAudioDevicePropertyPreferredChannelLayout,
353 kAudioUnitScope_Output,
358 /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
359 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
360 /* bitmap defined channellayout */
361 verify_noerr(AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
362 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->format.i_physical_channels = 0;
378 i_original = p_aout->format.i_original_channels & AOUT_CHAN_PHYSMASK;
380 if (i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2) {
381 /* We only need Mono or cannot output more than 1 channel */
382 p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
383 } else if (i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3) {
384 /* We only need Stereo or cannot output more than 2 channels */
385 p_aout->format.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
387 /* We want more than stereo and we can do that */
388 for (unsigned int i = 0; i < layout->mNumberChannelDescriptions; i++) {
389 msg_Dbg(p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
391 switch(layout->mChannelDescriptions[i].mChannelLabel) {
392 case kAudioChannelLabel_Left:
393 p_aout->format.i_physical_channels |= AOUT_CHAN_LEFT;
395 case kAudioChannelLabel_Right:
396 p_aout->format.i_physical_channels |= AOUT_CHAN_RIGHT;
398 case kAudioChannelLabel_Center:
399 p_aout->format.i_physical_channels |= AOUT_CHAN_CENTER;
401 case kAudioChannelLabel_LFEScreen:
402 p_aout->format.i_physical_channels |= AOUT_CHAN_LFE;
404 case kAudioChannelLabel_LeftSurround:
405 p_aout->format.i_physical_channels |= AOUT_CHAN_REARLEFT;
407 case kAudioChannelLabel_RightSurround:
408 p_aout->format.i_physical_channels |= AOUT_CHAN_REARRIGHT;
410 case kAudioChannelLabel_RearSurroundLeft:
411 p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
413 case kAudioChannelLabel_RearSurroundRight:
414 p_aout->format.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
416 case kAudioChannelLabel_CenterSurround:
417 p_aout->format.i_physical_channels |= AOUT_CHAN_REARCENTER;
420 msg_Warn(p_aout, "unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel);
423 if (p_aout->format.i_physical_channels == 0) {
424 p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
425 msg_Err(p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode.");
426 dialog_Fatal(p_aout, _("Audio device is not configured"), "%s",
427 _("You should configure your speaker layout with "
428 "the \"Audio Midi Setup\" utility in /Applications/"
429 "Utilities. Stereo mode is being used now."));
434 msg_Warn(p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!");
435 p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
438 msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(&p_aout->format));
439 msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(&p_aout->format));
441 memset (&new_layout, 0, sizeof(new_layout));
442 switch(aout_FormatNbChannels(&p_aout->format)) {
444 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
447 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
450 if (p_aout->format.i_physical_channels & AOUT_CHAN_CENTER)
451 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
452 else if (p_aout->format.i_physical_channels & AOUT_CHAN_LFE)
453 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
456 if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_LFE))
457 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
458 else if (p_aout->format.i_physical_channels & (AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT))
459 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
460 else if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER))
461 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
464 if (p_aout->format.i_physical_channels & (AOUT_CHAN_CENTER))
465 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
466 else if (p_aout->format.i_physical_channels & (AOUT_CHAN_LFE))
467 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
470 if (p_aout->format.i_physical_channels & (AOUT_CHAN_LFE))
471 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
473 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
476 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
477 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
480 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
481 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
485 /* Set up the format to be used */
486 DeviceFormat.mSampleRate = p_aout->format.i_rate;
487 DeviceFormat.mFormatID = kAudioFormatLinearPCM;
489 /* We use float 32. It's the best supported format by both VLC and Coreaudio */
490 p_aout->format.i_format = VLC_CODEC_FL32;
491 DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
492 DeviceFormat.mBitsPerChannel = 32;
493 DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels(&p_aout->format);
495 /* Calculate framesizes and stuff */
496 DeviceFormat.mFramesPerPacket = 1;
497 DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
498 DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
500 /* Set the desired format */
501 i_param_size = sizeof(AudioStreamBasicDescription);
502 verify_noerr(AudioUnitSetProperty(p_sys->au_unit,
503 kAudioUnitProperty_StreamFormat,
504 kAudioUnitScope_Input,
509 msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , DeviceFormat));
511 /* Retrieve actual format */
512 verify_noerr(AudioUnitGetProperty(p_sys->au_unit,
513 kAudioUnitProperty_StreamFormat,
514 kAudioUnitScope_Input,
519 msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , DeviceFormat));
521 /* Do the last VLC aout setups */
522 aout_FormatPrepare(&p_aout->format);
523 aout_PacketInit(p_aout, &p_sys->packet, FRAMESIZE);
524 p_aout->volume_set = VolumeSet;
525 p_aout->mute_set = MuteSet;
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));
562 /*****************************************************************************
563 * Setup a encoded digital stream (SPDIF)
564 *****************************************************************************/
565 static int OpenSPDIF (audio_output_t * p_aout)
567 struct aout_sys_t *p_sys = p_aout->sys;
568 OSStatus err = noErr;
569 UInt32 i_param_size = 0, b_mix = 0;
570 Boolean b_writeable = false;
571 AudioStreamID *p_streams = NULL;
572 unsigned i_streams = 0;
574 /* Start doing the SPDIF setup proces */
575 p_sys->b_digital = true;
578 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
579 i_param_size = sizeof(p_sys->i_hog_pid);
580 p_sys->i_hog_pid = getpid() ;
582 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
585 msg_Err(p_aout, "failed to set hogmode: [%4.4s]", (char *)&err);
589 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
591 if (AudioObjectHasProperty(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress)) {
592 /* Set mixable to false if we are allowed to */
593 err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
594 err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size);
595 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
597 if (err == noErr && b_writeable) {
599 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
600 p_sys->b_changed_mixing = true;
604 msg_Err(p_aout, "failed to set mixmode: [%4.4s]", (char *)&err);
609 /* Get a list of all the streams on this device */
610 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
611 err = AudioObjectGetPropertyDataSize(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size);
613 msg_Err(p_aout, "could not get number of streams: [%4.4s]", (char *)&err);
617 i_streams = i_param_size / sizeof(AudioStreamID);
618 p_streams = (AudioStreamID *)malloc(i_param_size);
619 if (p_streams == NULL)
622 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &streamsAddress, 0, NULL, &i_param_size, p_streams);
625 msg_Err(p_aout, "could not get number of streams: [%4.4s]", (char *)&err);
630 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
631 for (unsigned i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++) {
632 /* Find a stream with a cac3 stream */
633 AudioStreamRangedDescription *p_format_list = NULL;
635 bool b_digital = false;
637 /* Retrieve all the stream formats supported by each output stream */
638 err = AudioObjectGetPropertyDataSize(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size);
640 msg_Err(p_aout, "OpenSPDIF: could not get number of streamformats: [%s] (%i)", (char *)&err, (int32_t)err);
644 i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
645 p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
646 if (p_format_list == NULL)
649 err = AudioObjectGetPropertyData(p_streams[i], &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
651 msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
656 /* Check if one of the supported formats is a digital format */
657 for (int j = 0; j < i_formats; j++) {
658 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
659 p_format_list[j].mFormat.mFormatID == 'iac3' ||
660 p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
661 p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
668 /* if this stream supports a digital (cac3) format, then go set it. */
669 int i_requested_rate_format = -1;
670 int i_current_rate_format = -1;
671 int i_backup_rate_format = -1;
673 p_sys->i_stream_id = p_streams[i];
674 p_sys->i_stream_index = i;
676 if (!p_sys->b_revert) {
677 AudioObjectPropertyAddress currentPhysicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
678 /* Retrieve the original format of this stream first if not done so already */
679 i_param_size = sizeof(p_sys->sfmt_revert);
680 err = AudioObjectGetPropertyData(p_sys->i_stream_id, ¤tPhysicalFormatAddress, 0, NULL, &i_param_size, &p_sys->sfmt_revert);
682 msg_Err(p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err);
685 p_sys->b_revert = true;
688 for (int j = 0; j < i_formats; j++) {
689 if (p_format_list[j].mFormat.mFormatID == 'IAC3' ||
690 p_format_list[j].mFormat.mFormatID == 'iac3' ||
691 p_format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 ||
692 p_format_list[j].mFormat.mFormatID == kAudioFormatAC3) {
693 if (p_format_list[j].mFormat.mSampleRate == p_aout->format.i_rate) {
694 i_requested_rate_format = j;
696 } else if (p_format_list[j].mFormat.mSampleRate == p_sys->sfmt_revert.mSampleRate)
697 i_current_rate_format = j;
699 if (i_backup_rate_format < 0 || p_format_list[j].mFormat.mSampleRate > p_format_list[i_backup_rate_format].mFormat.mSampleRate)
700 i_backup_rate_format = j;
706 if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio */
707 p_sys->stream_format = p_format_list[i_requested_rate_format].mFormat;
708 else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device */
709 p_sys->stream_format = p_format_list[i_current_rate_format].mFormat;
711 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) */
717 /* get notified when we don't have spdif-output anymore */
718 err = AudioObjectAddPropertyListener(p_sys->i_stream_id, &physicalFormatsAddress, HardwareListener, (void *)p_aout);
720 msg_Warn(p_aout, "could not set audio device property streams callback on device: %4.4s",
724 msg_Dbg(p_aout, STREAM_FORMAT_MSG("original stream format: ", p_sys->sfmt_revert));
726 if (!AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->stream_format))
729 /* Set the format flags */
730 if (p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)
731 p_aout->format.i_format = VLC_CODEC_SPDIFB;
733 p_aout->format.i_format = VLC_CODEC_SPDIFL;
734 p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
735 p_aout->format.i_frame_length = A52_FRAME_NB;
736 p_aout->format.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
737 aout_FormatPrepare(&p_aout->format);
738 aout_PacketInit(p_aout, &p_sys->packet, A52_FRAME_NB);
739 p_aout->volume_set = NULL;
740 p_aout->mute_set = NULL;
742 /* Add IOProc callback */
743 err = AudioDeviceCreateIOProcID(p_sys->i_selected_dev,
744 (AudioDeviceIOProc)RenderCallbackSPDIF,
748 msg_Err(p_aout, "AudioDeviceCreateIOProcID failed: [%4.4s]", (char *)&err);
749 aout_PacketDestroy (p_aout);
753 /* Check for the difference between the Device clock and mdate */
754 p_sys->clock_diff = - (mtime_t)
755 AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
756 p_sys->clock_diff += mdate();
759 err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
761 msg_Err(p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err);
763 err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev, p_sys->i_procID);
765 msg_Err(p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err);
767 aout_PacketDestroy (p_aout);
775 /*****************************************************************************
776 * Close: Close HAL AudioUnit
777 *****************************************************************************/
778 static void Close(vlc_object_t * p_this)
780 audio_output_t *p_aout = (audio_output_t *)p_this;
781 struct aout_sys_t *p_sys = p_aout->sys;
782 OSStatus err = noErr;
783 UInt32 i_param_size = 0;
785 AudioObjectPropertyAddress deviceAliveAddress = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
786 err = AudioObjectRemovePropertyListener(p_sys->i_selected_dev, &deviceAliveAddress, HardwareListener, NULL);
788 msg_Err(p_aout, "failed to remove audio device life checker: [%4.4s]", (char *)&err);
790 if (p_sys->b_digital) {
791 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
792 err = AudioObjectRemovePropertyListener(p_sys->i_stream_id, &physicalFormatsAddress, HardwareListener, NULL);
794 msg_Err(p_aout, "failed to remove audio device property streams callback: [%4.4s]", (char *)&err);
797 if (p_sys->au_unit) {
798 verify_noerr(AudioOutputUnitStop(p_sys->au_unit));
799 verify_noerr(AudioUnitUninitialize(p_sys->au_unit));
800 verify_noerr(CloseComponent(p_sys->au_unit));
803 if (p_sys->b_digital) {
805 err = AudioDeviceStop(p_sys->i_selected_dev,
808 msg_Err(p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err);
810 /* Remove IOProc callback */
811 err = AudioDeviceDestroyIOProcID(p_sys->i_selected_dev,
814 msg_Err(p_aout, "AudioDeviceDestroyIOProcID failed: [%4.4s]", (char *)&err);
817 AudioStreamChangeFormat(p_aout, p_sys->i_stream_id, p_sys->sfmt_revert);
819 if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
821 Boolean b_writeable = false;
822 /* Revert mixable to true if we are allowed to */
823 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
824 err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
825 err = AudioObjectGetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, &i_param_size, &b_mix);
827 if (err == noErr && b_writeable) {
828 msg_Dbg(p_aout, "mixable is: %d", b_mix);
830 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, 0, NULL, i_param_size, &b_mix);
834 msg_Err(p_aout, "failed to set mixmode: [%4.4s]", (char *)&err);
838 AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
839 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, NULL);
842 msg_Err(p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err);
844 if (p_sys->i_hog_pid == getpid()) {
845 p_sys->i_hog_pid = -1;
846 i_param_size = sizeof(p_sys->i_hog_pid);
847 AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
848 kAudioDevicePropertyScopeOutput,
849 kAudioObjectPropertyElementMaster };
850 err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
852 msg_Err(p_aout, "Could not release hogmode: [%4.4s]", (char *)&err);
855 var_DelCallback(p_aout, "audio-device", AudioDeviceCallback, NULL);
857 aout_PacketDestroy(p_aout);
861 /*****************************************************************************
862 * Probe: Check which devices the OS has, and add them to our audio-device menu
863 *****************************************************************************/
864 static void Probe(audio_output_t * p_aout)
866 OSStatus err = noErr;
867 UInt32 i_param_size = 0;
868 AudioDeviceID devid_def = 0;
869 AudioDeviceID *p_devices = NULL;
870 vlc_value_t val, text;
872 struct aout_sys_t *p_sys = p_aout->sys;
874 /* Get number of devices */
875 AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
876 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size);
878 msg_Err(p_aout, "Could not get number of devices: [%s]", (char *)&err);
882 p_sys->i_devices = i_param_size / sizeof(AudioDeviceID);
884 if (p_sys->i_devices < 1) {
885 msg_Err(p_aout, "No audio output devices were found.");
888 msg_Dbg(p_aout, "found %u audio device(s)", (unsigned)p_sys->i_devices);
890 /* Allocate DeviceID array */
891 p_devices = (AudioDeviceID*)malloc(sizeof(AudioDeviceID) * p_sys->i_devices);
892 if (p_devices == NULL)
895 /* Populate DeviceID array */
896 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesAddress, 0, NULL, &i_param_size, p_devices);
898 msg_Err(p_aout, "could not get the device IDs: [%s]", (char *)&err);
902 /* Find the ID of the default Device */
903 AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
904 i_param_size = sizeof(AudioDeviceID);
905 err= AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &i_param_size, &devid_def);
907 msg_Err(p_aout, "could not get default audio device: [%s]", (char *)&err);
910 p_sys->i_default_dev = devid_def;
912 var_Create(p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
913 text.psz_string = (char*)_("Audio Device");
914 var_Change(p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
916 AudioObjectPropertyAddress deviceNameAddress = { kAudioDevicePropertyDeviceName, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
918 for (unsigned int i = 0; i < p_sys->i_devices; i++) {
922 /* Retrieve the length of the device name */
923 err = AudioObjectGetPropertyDataSize(p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size);
927 /* Retrieve the name of the device */
928 psz_name = (char *)malloc(i_param_size);
929 err = AudioObjectGetPropertyData(p_devices[i], &deviceNameAddress, 0, NULL, &i_param_size, psz_name);
933 msg_Dbg(p_aout, "DevID: %u DevName: %s", (unsigned)p_devices[i], psz_name);
935 if (!AudioDeviceHasOutput(p_devices[i])) {
936 msg_Dbg(p_aout, "this device is INPUT only. skipping...");
941 /* Add the menu entries */
942 val.i_int = (int)p_devices[i];
943 text.psz_string = psz_name;
944 var_Change(p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
945 text.psz_string = NULL;
946 if (p_sys->i_default_dev == p_devices[i]) {
947 /* The default device is the selected device normally */
948 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
949 var_Set(p_aout, "audio-device", val);
952 if (AudioDeviceSupportsDigital(p_aout, p_devices[i])) {
953 val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
954 if (asprintf(&text.psz_string, _("%s (Encoded Output)"), psz_name) != -1) {
955 var_Change(p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
956 free(text.psz_string);
957 if (p_sys->i_default_dev == p_devices[i] && var_InheritBool(p_aout, "spdif")) {
958 /* We selected to prefer SPDIF output if available
959 * then this "dummy" entry should be selected */
960 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
961 var_Set(p_aout, "audio-device", val);
969 /* If a device is already "preselected", then use this device */
970 var_Get(p_aout->p_libvlc, "macosx-audio-device", &val);
972 msg_Dbg(p_aout, "using preselected output device %#"PRIx64, val.i_int);
973 var_Change(p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL);
974 var_Set(p_aout, "audio-device", val);
977 /* Attach a Listener so that we are notified of a change in the Device setup */
978 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, (void *)p_aout);
980 msg_Warn(p_aout, "failed to add listener for audio device configuration (%i)", err);
988 msg_Warn(p_aout, "audio device already in use");
993 /*****************************************************************************
994 * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
995 *****************************************************************************/
996 static int AudioDeviceHasOutput(AudioDeviceID i_dev_id)
1000 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1001 verify_noerr(AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &dataSize));
1008 /*****************************************************************************
1009 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1010 *****************************************************************************/
1011 static int AudioDeviceSupportsDigital(audio_output_t *p_aout, AudioDeviceID i_dev_id)
1013 OSStatus err = noErr;
1014 UInt32 i_param_size = 0;
1015 AudioStreamID *p_streams = NULL;
1017 bool b_return = false;
1019 /* Retrieve all the output streams */
1020 AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
1021 err = AudioObjectGetPropertyDataSize(i_dev_id, &streamsAddress, 0, NULL, &i_param_size);
1023 msg_Err(p_aout, "could not get number of streams: [%s] (%i)", (char *)&err, (int32_t)err);
1027 i_streams = i_param_size / sizeof(AudioStreamID);
1028 p_streams = (AudioStreamID *)malloc(i_param_size);
1029 if (p_streams == NULL)
1032 err = AudioObjectGetPropertyData(i_dev_id, &streamsAddress, 0, NULL, &i_param_size, p_streams);
1034 msg_Err(p_aout, "could not get list of streams: [%s]", (char *)&err);
1038 for (int i = 0; i < i_streams; i++) {
1039 if (AudioStreamSupportsDigital(p_aout, p_streams[i]))
1047 /*****************************************************************************
1048 * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1049 *****************************************************************************/
1050 static int AudioStreamSupportsDigital(audio_output_t *p_aout, AudioStreamID i_stream_id)
1052 OSStatus err = noErr;
1053 UInt32 i_param_size = 0;
1054 AudioStreamRangedDescription *p_format_list = NULL;
1056 bool b_return = false;
1058 /* Retrieve all the stream formats supported by each output stream */
1059 AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 };
1060 err = AudioObjectGetPropertyDataSize(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size);
1062 msg_Err(p_aout, "could not get number of streamformats: [%s] (%i)", (char *)&err, (int32_t)err);
1066 i_formats = i_param_size / sizeof(AudioStreamRangedDescription);
1067 msg_Dbg(p_aout, "found %i stream formats", i_formats);
1069 p_format_list = (AudioStreamRangedDescription *)malloc(i_param_size);
1070 if (p_format_list == NULL)
1073 err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatsAddress, 0, NULL, &i_param_size, p_format_list);
1075 msg_Err(p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err);
1076 free(p_format_list);
1077 p_format_list = NULL;
1081 for (int i = 0; i < i_formats; i++) {
1082 msg_Dbg(p_aout, STREAM_FORMAT_MSG("supported format: ", p_format_list[i].mFormat));
1084 if (p_format_list[i].mFormat.mFormatID == 'IAC3' ||
1085 p_format_list[i].mFormat.mFormatID == 'iac3' ||
1086 p_format_list[i].mFormat.mFormatID == kAudioFormat60958AC3 ||
1087 p_format_list[i].mFormat.mFormatID == kAudioFormatAC3)
1091 free(p_format_list);
1095 /*****************************************************************************
1096 * AudioStreamChangeFormat: Change i_stream_id to change_format
1097 *****************************************************************************/
1098 static int AudioStreamChangeFormat(audio_output_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format)
1100 OSStatus err = noErr;
1101 UInt32 i_param_size = 0;
1103 AudioObjectPropertyAddress physicalFormatAddress = { kAudioStreamPropertyPhysicalFormat, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1105 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1107 msg_Dbg(p_aout, STREAM_FORMAT_MSG("setting stream format: ", change_format));
1109 /* Condition because SetProperty is asynchronious */
1110 vlc_cond_init(&w.cond);
1111 vlc_mutex_init(&w.lock);
1112 vlc_mutex_lock(&w.lock);
1114 /* Install the callback */
1115 err = AudioObjectAddPropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1117 msg_Err(p_aout, "AudioObjectAddPropertyListener for kAudioStreamPropertyPhysicalFormat failed: [%4.4s]", (char *)&err);
1121 /* change the format */
1122 err = AudioObjectSetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, sizeof(AudioStreamBasicDescription),
1125 msg_Err(p_aout, "could not set the stream format: [%4.4s]", (char *)&err);
1129 /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1130 * it is also not atomic in its behaviour.
1131 * Therefore we check 5 times before we really give up.
1132 * FIXME: failing isn't actually implemented yet. */
1133 for (int i = 0; i < 5; i++) {
1134 AudioStreamBasicDescription actual_format;
1135 mtime_t timeout = mdate() + 500000;
1137 if (vlc_cond_timedwait(&w.cond, &w.lock, timeout))
1138 msg_Dbg(p_aout, "reached timeout");
1140 i_param_size = sizeof(AudioStreamBasicDescription);
1141 err = AudioObjectGetPropertyData(i_stream_id, &physicalFormatAddress, 0, NULL, &i_param_size, &actual_format);
1143 msg_Dbg(p_aout, STREAM_FORMAT_MSG("actual format in use: ", actual_format));
1144 if (actual_format.mSampleRate == change_format.mSampleRate &&
1145 actual_format.mFormatID == change_format.mFormatID &&
1146 actual_format.mFramesPerPacket == change_format.mFramesPerPacket) {
1147 /* The right format is now active */
1150 /* We need to check again */
1153 /* Removing the property listener */
1154 err = AudioObjectRemovePropertyListener(i_stream_id, &physicalFormatAddress, StreamListener, (void *)&w);
1156 msg_Err(p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err);
1160 /* Destroy the lock and condition */
1161 vlc_mutex_unlock(&w.lock);
1162 vlc_mutex_destroy(&w.lock);
1163 vlc_cond_destroy(&w.cond);
1168 /*****************************************************************************
1169 * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1170 * us to provide some more audio data.
1171 * Don't print anything during normal playback, calling blocking function from
1172 * this callback is not allowed.
1173 *****************************************************************************/
1174 static OSStatus RenderCallbackAnalog(vlc_object_t *_p_aout,
1175 AudioUnitRenderActionFlags *ioActionFlags,
1176 const AudioTimeStamp *inTimeStamp,
1177 unsigned int inBusNumber,
1178 unsigned int inNumberFrames,
1179 AudioBufferList *ioData)
1181 AudioTimeStamp host_time;
1182 mtime_t current_date = 0;
1183 uint32_t i_mData_bytes = 0;
1185 audio_output_t * p_aout = (audio_output_t *)_p_aout;
1186 struct aout_sys_t * p_sys = p_aout->sys;
1188 VLC_UNUSED(ioActionFlags);
1189 VLC_UNUSED(inBusNumber);
1190 VLC_UNUSED(inNumberFrames);
1192 host_time.mFlags = kAudioTimeStampHostTimeValid;
1193 AudioDeviceTranslateTime(p_sys->i_selected_dev, inTimeStamp, &host_time);
1195 /* Check for the difference between the Device clock and mdate */
1196 p_sys->clock_diff = - (mtime_t)
1197 AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
1198 p_sys->clock_diff += mdate();
1200 current_date = p_sys->clock_diff +
1201 AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
1202 //- ((mtime_t) 1000000 / p_aout->format.i_rate * 31); // 31 = Latency in Frames. retrieve somewhere
1204 if (ioData == NULL || ioData->mNumberBuffers < 1) {
1205 msg_Err(p_aout, "no iodata or buffers");
1208 if (ioData->mNumberBuffers > 1)
1209 msg_Err(p_aout, "well this is weird. seems like there is more than one buffer...");
1212 if (p_sys->i_total_bytes > 0) {
1213 i_mData_bytes = __MIN(p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize);
1214 memcpy(ioData->mBuffers[0].mData,
1215 &p_sys->p_remainder_buffer[p_sys->i_read_bytes],
1217 p_sys->i_read_bytes += i_mData_bytes;
1218 current_date += (mtime_t) ((mtime_t) 1000000 / p_aout->format.i_rate) *
1219 (i_mData_bytes / 4 / aout_FormatNbChannels(&p_aout->format)); // 4 is fl32 specific
1221 if (p_sys->i_read_bytes >= p_sys->i_total_bytes)
1222 p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
1225 while(i_mData_bytes < ioData->mBuffers[0].mDataByteSize) {
1226 /* We don't have enough data yet */
1228 p_buffer = aout_PacketNext(p_aout, current_date);
1230 if (p_buffer != NULL)
1232 uint32_t i_second_mData_bytes = __MIN(p_buffer->i_buffer, ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
1234 memcpy((uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes,
1235 p_buffer->p_buffer, i_second_mData_bytes);
1236 i_mData_bytes += i_second_mData_bytes;
1238 if (i_mData_bytes >= ioData->mBuffers[0].mDataByteSize)
1240 p_sys->i_total_bytes = p_buffer->i_buffer - i_second_mData_bytes;
1241 memcpy(p_sys->p_remainder_buffer,
1242 &p_buffer->p_buffer[i_second_mData_bytes],
1243 p_sys->i_total_bytes);
1244 block_Release(p_buffer);
1247 /* update current_date */
1248 current_date += (mtime_t) ((mtime_t) 1000000 / p_aout->format.i_rate) *
1249 (i_second_mData_bytes / 4 / aout_FormatNbChannels(&p_aout->format)); // 4 is fl32 specific
1250 block_Release(p_buffer);
1252 memset((uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes,
1253 0,ioData->mBuffers[0].mDataByteSize - i_mData_bytes);
1254 i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
1260 /*****************************************************************************
1261 * RenderCallbackSPDIF: callback for SPDIF audio output
1262 *****************************************************************************/
1263 static OSStatus RenderCallbackSPDIF (AudioDeviceID inDevice,
1264 const AudioTimeStamp * inNow,
1265 const void * inInputData,
1266 const AudioTimeStamp * inInputTime,
1267 AudioBufferList * outOutputData,
1268 const AudioTimeStamp * inOutputTime,
1269 void * threadGlobals)
1272 mtime_t current_date;
1274 audio_output_t * p_aout = (audio_output_t *)threadGlobals;
1275 struct aout_sys_t * p_sys = p_aout->sys;
1277 VLC_UNUSED(inDevice);
1278 VLC_UNUSED(inInputData);
1279 VLC_UNUSED(inInputTime);
1281 /* Check for the difference between the Device clock and mdate */
1282 p_sys->clock_diff = - (mtime_t)
1283 AudioConvertHostTimeToNanos(inNow->mHostTime) / 1000;
1284 p_sys->clock_diff += mdate();
1286 current_date = p_sys->clock_diff +
1287 AudioConvertHostTimeToNanos(inOutputTime->mHostTime) / 1000;
1288 //- ((mtime_t) 1000000 / p_aout->format.i_rate * 31); // 31 = Latency in Frames. retrieve somewhere
1290 p_buffer = aout_PacketNext(p_aout, current_date);
1292 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1293 if (p_buffer != NULL) {
1294 if ((int)BUFFER.mDataByteSize != (int)p_buffer->i_buffer)
1295 msg_Warn(p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_buffer);
1297 /* move data into output data buffer */
1298 memcpy(BUFFER.mData, p_buffer->p_buffer, p_buffer->i_buffer);
1299 block_Release(p_buffer);
1302 memset(BUFFER.mData, 0, BUFFER.mDataByteSize);
1308 /*****************************************************************************
1309 * HardwareListener: Warns us of changes in the list of registered devices
1310 *****************************************************************************/
1311 static OSStatus HardwareListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
1313 OSStatus err = noErr;
1314 audio_output_t *p_aout = (audio_output_t *)inClientData;
1315 VLC_UNUSED(inObjectID);
1317 for (unsigned int i = 0; i < inNumberAddresses; i++) {
1318 if (inAddresses[i].mSelector == kAudioHardwarePropertyDevices) {
1319 /* something changed in the list of devices */
1320 /* We trigger the audio-device's aout_ChannelsRestart callback */
1321 msg_Warn(p_aout, "audio device configuration changed, resetting cache");
1322 var_TriggerCallback(p_aout, "audio-device");
1323 var_Destroy(p_aout, "audio-device");
1324 } else if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceIsAlive) {
1325 msg_Warn(p_aout, "audio device died, resetting aout");
1326 var_TriggerCallback(p_aout, "audio-device");
1327 var_Destroy(p_aout, "audio-device");
1328 } else if (inAddresses[i].mSelector == kAudioStreamPropertyAvailablePhysicalFormats) {
1329 msg_Warn(p_aout, "available physical formats for audio device changed, resetting aout");
1330 var_TriggerCallback(p_aout, "audio-device");
1331 var_Destroy(p_aout, "audio-device");
1338 /*****************************************************************************
1340 *****************************************************************************/
1341 static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
1343 OSStatus err = noErr;
1344 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1346 VLC_UNUSED(inObjectID);
1348 for (unsigned int i = 0; i < inNumberAddresses; i++) {
1349 if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
1350 vlc_mutex_lock(&w->lock);
1351 vlc_cond_signal(&w->cond);
1352 vlc_mutex_unlock(&w->lock);
1359 /*****************************************************************************
1360 * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1361 *****************************************************************************/
1362 static int AudioDeviceCallback(vlc_object_t *p_this, const char *psz_variable,
1363 vlc_value_t old_val, vlc_value_t new_val, void *param)
1365 audio_output_t *p_aout = (audio_output_t *)p_this;
1366 var_Set(p_aout->p_libvlc, "macosx-audio-device", new_val);
1367 msg_Dbg(p_aout, "Set Device: %#"PRIx64, new_val.i_int);
1368 return aout_ChannelsRestart(p_this, psz_variable, old_val, new_val, param);
1372 /*****************************************************************************
1373 * VolumeSet: Implements volume_set(). Update the CoreAudio AU volume immediately.
1374 *****************************************************************************/
1375 static int VolumeSet(audio_output_t * p_aout, float volume)
1377 struct aout_sys_t *p_sys = p_aout->sys;
1380 aout_VolumeReport(p_aout, volume);
1382 volume = volume * volume * volume; // cubic mapping from output.c
1384 /* Set volume for output unit */
1385 ostatus = AudioUnitSetParameter(p_sys->au_unit,
1386 kHALOutputParam_Volume,
1387 kAudioUnitScope_Global,
1395 static int MuteSet(audio_output_t * p_aout, bool mute)
1397 aout_MuteReport(p_aout, mute);