X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fauhal.c;h=50f5411e1cd80fb950f9a3a0a565d71a78c8f28d;hb=626174f3b9d86559db9c66ede56abd15a3d7ea5e;hp=ff2cc796ac1f6a97e31c155c6967a5a0cb8fe3e6;hpb=d9431a2392b1864ee1069445bfdff2a4216bc4e4;p=vlc diff --git a/modules/audio_output/auhal.c b/modules/audio_output/auhal.c index ff2cc796ac..50f5411e1c 100644 --- a/modules/audio_output/auhal.c +++ b/modules/audio_output/auhal.c @@ -80,6 +80,8 @@ struct aout_sys_t 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? */ @@ -147,6 +149,7 @@ static OSStatus RenderCallbackSPDIF (AudioDeviceID, const AudioTimeStamp *, 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 *); @@ -189,6 +192,7 @@ static int Open(vlc_object_t *obj) 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; @@ -198,12 +202,31 @@ static int Open(vlc_object_t *obj) p_aout->device_select = SwitchAudioDevice; p_sys->device_list = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL); + /* + * Force an own run loop for callbacks. + * + * According to rtaudio, this is absolutely necessary since 10.6 to get correct notifications. + * It might fix issues when using the module as a library where a proper loop is not setup already. + */ + CFRunLoopRef theRunLoop = NULL; + AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster }; + err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); + if (err != noErr) { + msg_Err(p_aout, "failed to set the run loop property [%4.4s]", (char *)&err); + } + /* 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 */ @@ -232,6 +255,12 @@ static void Close(vlc_object_t *obj) 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); @@ -309,9 +338,12 @@ static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt) 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); @@ -1043,6 +1075,7 @@ static void Stop(audio_output_t *p_aout) 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); @@ -1255,7 +1288,7 @@ static int SwitchAudioDevice(audio_output_t *p_aout, const char *name) 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; @@ -1264,12 +1297,14 @@ static int VolumeSet(audio_output_t * p_aout, float volume) 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)); @@ -1532,7 +1567,52 @@ static OSStatus DeviceAliveListener(AudioObjectID inObjectID, UInt32 inNumberAd } /* - * Callback when streams of any audio device changed (e.g. SPDIF gets (un)available) + * Callback when default audio device changed + */ +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); + + /* Default device is changed by the os to allow other apps to play sound while in digital + mode. But this should not affect ourself. */ + if (p_aout->sys->b_digital) { + msg_Dbg(p_aout, "ignore, as digital mode is active"); + 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 physical formats for device change */ static OSStatus StreamsChangedListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData) { @@ -1591,7 +1671,7 @@ static OSStatus StreamsChangedListener(AudioObjectID inObjectID, UInt32 inNumbe } /* - * StreamListener: check whether the device's physical format changes on-the-fly (unlikely) + * StreamListener: check whether the device's physical format change is complete */ static OSStatus StreamListener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData) {