*****************************************************************************/
struct aout_sys_t
{
- AudioObjectID i_default_dev; /* DeviceID of defaultOutputDevice */
AudioObjectID i_selected_dev; /* DeviceID of the selected device */
AudioObjectID i_new_selected_dev; /* DeviceID of device which will be selected on start */
bool b_selected_dev_is_digital;
+ bool b_selected_dev_is_default; /* true if the user selected the default audio device (id 0) */
+
AudioDeviceIOProcID i_procID; /* DeviceID of current device */
bool b_digital; /* Are we running in digital mode? */
- mtime_t clock_diff; /* Difference between VLC clock and Device clock */
uint8_t chans_to_reorder; /* do we need channel reordering */
uint8_t chan_table[AOUT_CHAN_MAX];
- UInt32 i_numberOfChannels;
TPCircularBuffer circular_buffer; /* circular buffer to swap the audio data */
/* AUHAL specific */
vlc_mutex_t lock;
vlc_cond_t cond;
+
+ bool b_ignore_streams_changed_callback;
};
#pragma mark -
static OSStatus DevicesListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
static OSStatus DeviceAliveListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
+static OSStatus DefaultDeviceChangedListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
static OSStatus StreamsChangedListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
static OSStatus StreamListener (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
vlc_mutex_init(&p_sys->lock);
vlc_cond_init(&p_sys->cond);
p_sys->b_digital = false;
+ p_sys->b_ignore_streams_changed_callback = false;
+ p_sys->b_selected_dev_is_default = false;
p_aout->sys = p_sys;
p_aout->start = Start;
p_aout->device_select = SwitchAudioDevice;
p_sys->device_list = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
- /* Attach a Listener so that we are notified of a change in the Device setup */
+ /* Attach a listener so that we are notified of a change in the device setup */
AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, DevicesListener, (void *)p_aout);
if (err != noErr)
msg_Err(p_aout, "failed to add listener for audio device configuration [%4.4s]", (char *)&err);
+ /* Attach a listener to be notified about changes in default audio device */
+ AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
+ if (err != noErr)
+ msg_Err(p_aout, "failed to add listener for default audio device [%4.4s]", (char *)&err);
+
RebuildDeviceList(p_aout);
/* remember the volume */
if (err != noErr)
msg_Err(p_aout, "AudioHardwareRemovePropertyListener failed [%4.4s]", (char *)&err);
+ /* remove listener to be notified about changes in default audio device */
+ AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, DefaultDeviceChangedListener, (void *)p_aout);
+ if (err != noErr)
+ msg_Err(p_aout, "failed to remove listener for default audio device [%4.4s]", (char *)&err);
+
vlc_mutex_lock(&p_sys->var_lock);
/* remove streams callbacks */
CFIndex count = CFArrayGetCount(p_sys->device_list);
p_sys->b_digital = false;
p_sys->au_component = NULL;
p_sys->au_unit = NULL;
- p_sys->clock_diff = (mtime_t) 0;
p_sys->i_hog_pid = -1;
p_sys->i_stream_id = 0;
p_sys->i_stream_index = -1;
msg_Warn(p_aout, "selected audio device is not alive, switching to default device");
}
+ p_sys->b_selected_dev_is_default = false;
if (!b_alive || p_sys->i_selected_dev == 0) {
+ p_sys->b_selected_dev_is_default = true;
+
AudioObjectID defaultDeviceID = 0;
- UInt32 propertySize = 0;
+ UInt32 propertySize = sizeof(AudioObjectID);
AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
propertySize = sizeof(AudioObjectID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
if (err != noErr) {
msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
+ vlc_mutex_unlock(&p_sys->var_lock);
goto error;
}
else
p_sys->i_selected_dev = defaultDeviceID;
p_sys->b_selected_dev_is_digital = var_InheritBool(p_aout, "spdif");
}
+ vlc_mutex_unlock(&p_sys->var_lock);
// recheck if device still supports digital
b_start_digital = p_sys->b_selected_dev_is_digital;
b_success = true;
}
}
- vlc_mutex_unlock(&p_sys->var_lock);
if (b_success) {
p_aout->play = Play;
error:
/* If we reach this, this aout has failed */
- vlc_mutex_unlock(&p_sys->var_lock);
msg_Err(p_aout, "opening auhal output failed");
return VLC_EGENERIC;
}
msg_Dbg(p_aout, "selected %d physical channels for device output", aout_FormatNbChannels(fmt));
msg_Dbg(p_aout, "VLC will output: %s", aout_FormatPrintChannels(fmt));
- p_sys->i_numberOfChannels = aout_FormatNbChannels(fmt);
memset (&new_layout, 0, sizeof(new_layout));
uint32_t chans_out[AOUT_CHAN_MAX];
+ memset(&chans_out, 0, sizeof(chans_out));
/* Some channel abbreviations used below:
* L - left
break;
case 6:
if (fmt->i_physical_channels & (AOUT_CHAN_LFE)) {
- new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
+ /* note: for some reason, kAudioChannelLayoutTag_DVD_20, which would not require channel reordering,
+ does not work anymore */
+ new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1; // L R C LFE Ls Rs
chans_out[0] = AOUT_CHAN_LEFT;
chans_out[1] = AOUT_CHAN_RIGHT;
- chans_out[2] = AOUT_CHAN_REARLEFT;
- chans_out[3] = AOUT_CHAN_REARRIGHT;
- chans_out[4] = AOUT_CHAN_CENTER;
- chans_out[5] = AOUT_CHAN_LFE;
+ chans_out[2] = AOUT_CHAN_CENTER;
+ chans_out[3] = AOUT_CHAN_LFE;
+ chans_out[4] = AOUT_CHAN_REARLEFT;
+ chans_out[5] = AOUT_CHAN_REARRIGHT;
p_aout->sys->chans_to_reorder = aout_CheckChannelReorder(NULL, chans_out, fmt->i_physical_channels, p_aout->sys->chan_table);
if (p_aout->sys->chans_to_reorder)
/* AU initiliaze */
verify_noerr(AudioUnitInitialize(p_sys->au_unit));
- /* Find the difference between device clock and mdate clock */
- p_sys->clock_diff = - (mtime_t)
- AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
- p_sys->clock_diff += mdate();
-
/* setup circular buffer */
TPCircularBufferInit(&p_sys->circular_buffer, AUDIO_BUFFER_SIZE_IN_SECONDS *
fmt->i_rate * fmt->i_bytes_per_frame);
i_param_size = sizeof(p_sys->i_hog_pid);
p_sys->i_hog_pid = getpid() ;
+ /*
+ * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
+ * directly in the same thread. This call needs to be ignored to avoid endless restarting.
+ */
+ p_sys->b_ignore_streams_changed_callback = true;
err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
+ p_sys->b_ignore_streams_changed_callback = false;
if (err != noErr) {
msg_Err(p_aout, "failed to set hogmode [%4.4s]", (char *)&err);
return false;
}
- /* Check for the difference between the Device clock and mdate */
- p_sys->clock_diff = - (mtime_t)
- AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000;
- p_sys->clock_diff += mdate();
-
/* Start device */
err = AudioDeviceStart(p_sys->i_selected_dev, p_sys->i_procID);
if (err != noErr) {
verify_noerr(AudioComponentInstanceDispose(p_sys->au_unit));
}
- vlc_mutex_lock(&p_sys->var_lock);
if (p_sys->b_digital) {
/* Stop device */
err = AudioDeviceStop(p_sys->i_selected_dev,
if (p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3) {
int b_mix;
Boolean b_writeable = false;
+ i_param_size = sizeof(int);
/* Revert mixable to true if we are allowed to */
AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { kAudioDevicePropertySupportsMixing , kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
err = AudioObjectIsPropertySettable(p_sys->i_selected_dev, &audioDeviceSupportsMixingAddress, &b_writeable);
AudioObjectPropertyAddress audioDeviceHogModeAddress = { kAudioDevicePropertyHogMode,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster };
+
+ /*
+ * HACK: On 10.6, auhal will trigger the streams changed callback when calling below line,
+ * directly in the same thread. This call needs to be ignored to avoid endless restarting.
+ */
+ p_sys->b_ignore_streams_changed_callback = true;
err = AudioObjectSetPropertyData(p_sys->i_selected_dev, &audioDeviceHogModeAddress, 0, NULL, i_param_size, &p_sys->i_hog_pid);
+ p_sys->b_ignore_streams_changed_callback = false;
if (err != noErr)
msg_Err(p_aout, "Failed to release hogmode [%4.4s]", (char *)&err);
}
msg_Warn(p_aout, "failed to remove audio device life checker [%4.4s]", (char *)&err);
}
- vlc_mutex_unlock(&p_sys->var_lock);
-
p_sys->i_bytes_per_sample = 0;
p_sys->b_digital = false;
static int VolumeSet(audio_output_t * p_aout, float volume)
{
struct aout_sys_t *p_sys = p_aout->sys;
- OSStatus ostatus;
+ OSStatus ostatus = 0;
if(p_sys->b_digital)
return VLC_EGENERIC;
aout_VolumeReport(p_aout, volume);
/* Set volume for output unit */
- ostatus = AudioUnitSetParameter(p_sys->au_unit,
- kHALOutputParam_Volume,
- kAudioUnitScope_Global,
- 0,
- volume * volume * volume,
- 0);
+ if(!p_sys->b_mute) {
+ ostatus = AudioUnitSetParameter(p_sys->au_unit,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Global,
+ 0,
+ volume * volume * volume,
+ 0);
+ }
if (var_InheritBool(p_aout, "volume-save"))
config_PutInt(p_aout, "auhal-volume", lroundf(volume * AOUT_VOLUME_DEFAULT));
/*
* Callback when device list changed
*/
-static OSStatus DevicesListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus DevicesListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{
VLC_UNUSED(inObjectID);
VLC_UNUSED(inNumberAddresses);
/*
* Callback when current device is not alive anymore
*/
-static OSStatus DeviceAliveListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus DeviceAliveListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{
VLC_UNUSED(inObjectID);
VLC_UNUSED(inNumberAddresses);
}
/*
- * Callback when streams of any audio device changed (e.g. SPDIF gets (un)available)
+ * Callback when current device is not alive anymore
+ */
+static OSStatus DefaultDeviceChangedListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
+{
+ VLC_UNUSED(inObjectID);
+ VLC_UNUSED(inNumberAddresses);
+ VLC_UNUSED(inAddresses);
+
+ audio_output_t *p_aout = (audio_output_t *)inClientData;
+ if (!p_aout)
+ return -1;
+
+ if (!p_aout->sys->b_selected_dev_is_default)
+ return noErr;
+
+ AudioObjectID defaultDeviceID = 0;
+ UInt32 propertySize = sizeof(AudioObjectID);
+ AudioObjectPropertyAddress defaultDeviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+ propertySize = sizeof(AudioObjectID);
+ OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress, 0, NULL, &propertySize, &defaultDeviceID);
+ if (err != noErr) {
+ msg_Err(p_aout, "could not get default audio device [%4.4s]", (char *)&err);
+ return -1;
+ }
+
+ msg_Dbg(p_aout, "default device changed to %i", defaultDeviceID);
+
+ /*
+ * The default device id changes to 0 when switching to SPDIF for whatever reason.
+ * We need to ignore that.
+ */
+ if(defaultDeviceID == 0)
+ return noErr;
+
+ /* Also ignore events which announce the same device id */
+ if(defaultDeviceID == p_aout->sys->i_selected_dev)
+ return noErr;
+
+ msg_Dbg(p_aout, "default device actually changed, resetting aout");
+ aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
+
+ return noErr;
+}
+
+
+/*
+ * Callback when default audio device changed
*/
-static OSStatus StreamsChangedListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus StreamsChangedListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{
OSStatus err = noErr;
UInt32 i_param_size = 0;
return -1;
aout_sys_t *p_sys = p_aout->sys;
+ if(unlikely(p_sys->b_ignore_streams_changed_callback == true))
+ return 0;
msg_Dbg(p_aout, "available physical formats for audio device changed");
RebuildDeviceList(p_aout);
/*
* StreamListener: check whether the device's physical format changes on-the-fly (unlikely)
*/
-static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData)
{
OSStatus err = noErr;
struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;