diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index b91d5abfee..f3df5b86ca 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -163,9 +163,8 @@ impl AudioStack { sample_rate: u32, num_channels: u32, ) -> Result<()> { - let mut default_change_listener = DeviceChangeListener::new(false)?; - loop { + let mut device_change_listener = DeviceChangeListener::new(false)?; let (output_device, output_config) = default_device(false)?; let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>(); let mixer = mixer.clone(); @@ -225,7 +224,7 @@ impl AudioStack { end_on_drop_rx.recv().ok(); }); - default_change_listener.next().await; + device_change_listener.next().await; drop(end_on_drop_tx) } } @@ -236,8 +235,8 @@ impl AudioStack { sample_rate: u32, num_channels: u32, ) -> Result<()> { - let mut default_change_listener = DeviceChangeListener::new(true)?; loop { + let mut device_change_listener = DeviceChangeListener::new(true)?; let (device, config) = default_device(true)?; let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>(); let apm = apm.clone(); @@ -310,7 +309,7 @@ impl AudioStack { .log_err(); }); - default_change_listener.next().await; + device_change_listener.next().await; drop(end_on_drop_tx) } } @@ -646,6 +645,7 @@ mod macos { rx: UnboundedReceiver<()>, callback: Box, input: bool, + device_id: AudioObjectID, // Store the device ID to properly remove listeners } trait _AssertSend: Send {} @@ -672,7 +672,9 @@ mod macos { tx.unbounded_send(()).ok(); }))); - unsafe { + // Get the current default device ID + let device_id = unsafe { + // Listen for default device changes coreaudio::Error::from_os_status(AudioObjectAddPropertyListener( kAudioObjectSystemObject, &AudioObjectPropertyAddress { @@ -687,12 +689,78 @@ mod macos { Some(property_listener_handler_shim), &*callback as *const _ as *mut _, ))?; - } + + // Also listen for changes to the device configuration + let device_id = if input { + let mut input_device: AudioObjectID = 0; + let mut prop_size = std::mem::size_of::() as u32; + let result = coreaudio::sys::AudioObjectGetPropertyData( + kAudioObjectSystemObject, + &AudioObjectPropertyAddress { + mSelector: kAudioHardwarePropertyDefaultInputDevice, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMaster, + }, + 0, + std::ptr::null(), + &mut prop_size as *mut _, + &mut input_device as *mut _ as *mut _, + ); + if result != 0 { + log::warn!("Failed to get default input device ID"); + 0 + } else { + input_device + } + } else { + let mut output_device: AudioObjectID = 0; + let mut prop_size = std::mem::size_of::() as u32; + let result = coreaudio::sys::AudioObjectGetPropertyData( + kAudioObjectSystemObject, + &AudioObjectPropertyAddress { + mSelector: kAudioHardwarePropertyDefaultOutputDevice, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMaster, + }, + 0, + std::ptr::null(), + &mut prop_size as *mut _, + &mut output_device as *mut _ as *mut _, + ); + if result != 0 { + log::warn!("Failed to get default output device ID"); + 0 + } else { + output_device + } + }; + + if device_id != 0 { + // Listen for format changes on the device + coreaudio::Error::from_os_status(AudioObjectAddPropertyListener( + device_id, + &AudioObjectPropertyAddress { + mSelector: coreaudio::sys::kAudioDevicePropertyStreamFormat, + mScope: if input { + coreaudio::sys::kAudioObjectPropertyScopeInput + } else { + coreaudio::sys::kAudioObjectPropertyScopeOutput + }, + mElement: kAudioObjectPropertyElementMaster, + }, + Some(property_listener_handler_shim), + &*callback as *const _ as *mut _, + ))?; + } + + device_id + }; Ok(Self { rx, callback, input, + device_id, }) } } @@ -700,6 +768,7 @@ mod macos { impl Drop for CoreAudioDefaultDeviceChangeListener { fn drop(&mut self) { unsafe { + // Remove the system-level property listener AudioObjectRemovePropertyListener( kAudioObjectSystemObject, &AudioObjectPropertyAddress { @@ -714,6 +783,24 @@ mod macos { Some(property_listener_handler_shim), &*self.callback as *const _ as *mut _, ); + + // Remove the device-specific property listener if we have a valid device ID + if self.device_id != 0 { + AudioObjectRemovePropertyListener( + self.device_id, + &AudioObjectPropertyAddress { + mSelector: coreaudio::sys::kAudioDevicePropertyStreamFormat, + mScope: if self.input { + coreaudio::sys::kAudioObjectPropertyScopeInput + } else { + coreaudio::sys::kAudioObjectPropertyScopeOutput + }, + mElement: kAudioObjectPropertyElementMaster, + }, + Some(property_listener_handler_shim), + &*self.callback as *const _ as *mut _, + ); + } } } }