[bug] Cannot change audio output to earpiece if a bluetooth device is connected.
Describe the bug
When a Bluetooth audio device (e.g., AirPods, earbuds) is connected, it becomes impossible to route audio to the phone's earpiece using Room.setAudioOutputDevice() or Room.setSpeakerOn(false). The audio continues to route to the Bluetooth device instead of the earpiece, even after explicit device selection.
This appears to be due to the mobile OS (iOS/Android) prioritizing Bluetooth audio routing over earpiece routing. While switching from Bluetooth to speaker works correctly via setSpeakerOn(true), switching from Bluetooth to earpiece does not work as expected.
To Reproduce
- Connect Bluetooth earbuds/headphones to the device
- Join a LiveKit room with audio enabled
- Get available audio output devices using
Hardware.instance.audioOutputs() - Attempt to switch to earpiece using either:
-
room.setAudioOutputDevice(earpiece_device) -
room.setSpeakerOn(false)after Bluetooth was active
-
- Observe that audio continues playing through Bluetooth device instead of earpiece
Expected behavior
When selecting the earpiece device from the available audio outputs, the audio should route to the phone's earpiece/receiver, not continue playing through the Bluetooth device. The behavior should be:
- Bluetooth → Speaker: ✅ Works (via
setSpeakerOn(true)) - Bluetooth → Earpiece: ❌ Fails (audio stays on Bluetooth)
- Speaker → Earpiece: ✅ Works
- Earpiece → Bluetooth: ✅ Works
Platform information
- Flutter version: 3.35.1
- Plugin version: livekit_client: ^2.4.9
- Flutter target OS: Android (Model: Pixel 6a, Real Device)
- Flutter target OS version: Android 16 Baklava
Additional context
We've tested various workarounds including:
- Multiple calls to
setAudioOutputDevice() - Toggling
setSpeakerOn(true)thensetSpeakerOn(false)with delays - Attempting audio session resets via microphone disable/enable cycles
None of these approaches reliably route audio from Bluetooth to earpiece. This seems to be a limitation of how mobile operating systems handle audio routing priorities (Bluetooth > Speaker > Earpiece), but it would be valuable if LiveKit could provide a way to override this behavior or document the limitation.
Workaround
Currently hiding the earpiece option in the UI when Bluetooth devices are detected to prevent user confusion.
Can you please check on this? @bcherry
Can you please check on this? @cloudwebrtc
setAudioOutputDevice onlyfor desktop or web
Future
I have the same question. I'm trying to develop a feature: when a Bluetooth headset is connected, I still want to choose the phone's earpiece (not the headset) to play audio. But currently, I can only use the simple method setSpeakerOn, which only allows turning the speaker off or on. It doesn't enable the earpiece.
Hardware.selectAudioOutput only allows this method on desktop, but flutter-webrtc implements this method for all platforms:
/// Note: This method is only used for Flutter native,
/// supported on iOS/Android/macOS/Windows.
///
/// Android/macOS/Windows: Can be used to switch all output devices.
/// iOS: you can only switch directly between the
/// speaker and the preferred device
/// web: flutter web can use RTCVideoRenderer.audioOutput instead
static Future<void> selectAudioOutput(String deviceId) async {
@cloudwebrtc is there any reason for this restriction?
I believe mobile devices should let the system manage audio routing. This is because input/output devices on mobile devices typically appear in pairs. For example, with AirPods, you can't simultaneously use the AirPods microphone and the phone's earpiece for output, and using selectAudioOutput also changes the input, making management confusing. Therefore, mobile devices don't have the ability to independently modify input or output.