How to handle audio device switching on macOS with LiveKit

macOS Audio Device Switching Issues with Bluetooth Headsets (CMF Buds) in LiveKit

Hi team,

We’re experiencing several audio device management issues specifically on macOS and would appreciate guidance on the recommended LiveKit approach for handling microphone and speaker switching.

Environment

  • LiveKit React SDK

  • Self-hosted LiveKit Server

  • macOS (MacBook Air)

  • Safari browser

  • Bluetooth headset: CMF Buds


Issue 1: No Audio Until User Clicks Unmute

Steps to Reproduce

  1. Connect CMF Buds to MacBook Air.

  2. Join a LiveKit meeting.

  3. Open device settings.

Observed Behavior

  • Both “CMF Buds” and “MacBook Microphone” are listed.

  • No microphone/speaker appears selected by default.

  • User cannot hear remote participants immediately after joining.

  • Audio starts working only after clicking the Unmute button.

Expected Behavior

  • Similar to Zoom’s macOS experience.

  • Audio devices should initialize correctly after the required user gesture.

  • User should be able to hear remote participants without manually triggering device selection.

Question

Is there a recommended LiveKit pattern for handling audio initialization on macOS where browser autoplay/user-gesture restrictions are involved?


Issue 2: User-Selected Microphone Automatically Reverts

Steps to Reproduce

  1. Join meeting using CMF Buds.

  2. Unmute and confirm audio works.

  3. Switch microphone from CMF Buds to MacBook Microphone.

Observed Behavior

  • Device switch throws an error.

  • Application automatically switches back to CMF Buds.

  • User’s selection is not preserved.

Expected Behavior

  • If the user selects MacBook Microphone, it should remain selected.

  • The application should not automatically revert to another device unless the selected device becomes unavailable.

Question

Does LiveKit emit any automatic device recovery logic that could cause this behavior, or should the application fully control device selection persistence?


Issue 3: Distorted Incoming Audio on Bluetooth Headset

Steps to Reproduce

  1. Join using CMF Buds.

  2. Audio initially works correctly.

  3. Continue the meeting for some time.

Observed Behavior

  • Other participants can hear the MacBook user correctly.

  • Incoming audio becomes distorted/crackling through the CMF Buds.

  • Remote participants become difficult to hear.

  • Disconnecting and reconnecting the CMF Buds temporarily resolves the issue.

Expected Behavior

  • Incoming audio should remain stable throughout the session.

  • Bluetooth headset audio quality should not degrade during device switching or prolonged usage.

Question

Has anyone encountered Bluetooth profile switching issues (A2DP ↔ HFP/HSP) on macOS when using LiveKit?


Issue 4: Audio Output Not Routed to MacBook Speakers After Bluetooth Disconnect

Steps to Reproduce

  1. Join meeting using CMF Buds.

  2. Disconnect CMF Buds while remaining in the meeting.

Observed Behavior

  • Microphone automatically switches to MacBook Microphone.

  • However, remote participant audio is not routed to MacBook speakers.

  • User cannot hear anyone until reconnecting devices or taking additional actions.

Expected Behavior

  • After Bluetooth disconnection:

    • Microphone should switch to MacBook Microphone.

    • Speaker output should switch to MacBook Speakers.

    • Remote audio should immediately become audible through the laptop speakers.

Question

Is there a recommended way to handle audiooutput fallback when a Bluetooth device is removed during an active LiveKit session?


Additional Information

We’re currently using:

  • room.switchActiveDevice('audioinput', deviceId)

  • room.switchActiveDevice('audiooutput', deviceId)

  • Device change listeners

  • Automatic device restoration logic

Before we continue debugging, we’d like to understand:

  1. What is the recommended LiveKit approach for audio device management on macOS?

  2. Are there any known issues with Bluetooth headsets and device switching?

  3. How does Zoom typically avoid these problems, and are there LiveKit best practices to achieve similar behavior?

  4. Is there sample code or guidance for robust microphone/speaker failover handling on macOS?

Any suggestions or references would be greatly appreciated.

I don’t think this would be unique to self-hosted LiveKit, I imagine any behaviour with audio device switching would be consistent across both self-hosted and LiveKit cloud.

This is all only Safari, is it? Do you also see this behaviour on Chromium-based browsers? And, is this reproducible with the react starter: GitHub - livekit-examples/agent-starter-react: A complete voice AI frontend app for LiveKit Agents with Next.js · GitHub ?

I imagine also that the behaviour is consistent with other bluetooth headsets, and this isn’t a specific issue with ‘CMF Buds’.

For your first issue, it reminds me of this: Seeking React patterns to gracefully handle Safari's strict autoplay policies and background audio suspension for iOS users. which required a tap on the screen.

Yes, these issues are currently reproducible only on Safari (macOS). On Chromium-based browsers (Chrome/Edge), audio device switching works as expected and I don’t see the same problems.

The main issues I’m observing are:

  1. When joining with a Bluetooth headset connected, no audio device is selected by default and I cannot hear remote participants until I interact with the page (e.g., click Unmute).

  2. When switching the microphone from the Bluetooth headset to the MacBook microphone, the speaker/output device does not switch accordingly, resulting in audio routing issues.

  3. After disconnecting the Bluetooth headset, Safari does not always update the active audio path correctly, causing audio playback issues.

I haven’t yet tested with the React starter app, but I can do that to verify whether this is application-specific or a Safari/WebKit limitation. I also expect the behavior is not specific to CMF Buds and would likely affect other Bluetooth headsets as well.

The first issue does seem related to Safari’s autoplay/user-gesture restrictions, similar to the thread you referenced.Hi,

Yes, these issues are currently reproducible only on Safari (macOS). On Chromium-based browsers (Chrome/Edge), audio device switching works as expected and I don’t see the same problems.

The main issues I’m observing are:

  1. When joining with a Bluetooth headset connected, no audio device is selected by default and I cannot hear remote participants until I interact with the page (e.g., click Unmute).

  2. When switching the microphone from the Bluetooth headset to the MacBook microphone, the speaker/output device does not switch accordingly, resulting in audio routing issues.

  3. After disconnecting the Bluetooth headset, Safari does not always update the active audio path correctly, causing audio playback issues.

I haven’t yet tested with the React starter app, but I can do that to verify whether this is application-specific or a Safari/WebKit limitation. I also expect the behavior is not specific to CMF Buds and would likely affect other Bluetooth headsets as well.

The first issue does seem related to Safari’s autoplay/user-gesture restrictions, similar to the thread you referenced.

I apologise, but I gave you some bad information. The React starter app is for our agentic use cases, but you are looking at a streaming use case.

Are you able to see these issues with Meet, https://meet.livekit.io/?tab=demo. ?

When switching the microphone from the Bluetooth headset to the MacBook microphone, the speaker/output device does not switch accordingly, resulting in audio routing issues.

The audio output settings are separate from the microphone settings (accessible through the settings button)


Hi Darryn,

Yes, we are able to reproduce the same behavior in Safari on a MacBook Air.

When joining from Safari with Bluetooth devices (CMF Buds), the audio output devices are not being enumerated. We only see the system default output, and users cannot explicitly select a speaker/output device. This appears to be the same limitation observed in Safari where speaker selection support is restricted.Hi,

Yes, we are able to reproduce the same behavior in Safari on a MacBook Air.

When joining from Safari with Bluetooth devices (CMF Buds), the audio output devices are not being enumerated. We only see the system default output, and users cannot explicitly select a speaker/output device. This appears to be the same limitation observed in Safari where speaker selection support is restricted.

I only have a single BT headset and it’s behaving the same, I wonder if this is audio profiles, or Claude is also saying Safari 16.4 changed the behaviour of this, though I doubt you are on a version from March 2023(!).

Under the covers, we are calling navigator.mediaDevices.enumerateDevices() for audio output devices.

What is your output from:

(async () => {
  const d = await navigator.mediaDevices.enumerateDevices();
  console.table(d.map(x => ({ kind: x.kind, label: x.label, groupId: x.groupId.slice(0, 6) })));
  console.log('setSinkId supported:', 'setSinkId' in HTMLMediaElement.prototype);
})();

Mine is: