Skip to content

Audio routing issue #322

@hehooleehoo

Description

@hehooleehoo

On iOS, when joining a LiveKit room without the local microphone enabled, remote audio plays through the physical earpiece (receiver) instead of the bottom loudspeaker, despite using configureAudio({ ios: { defaultOutput: "speaker" } }), selectAudioOutput("force_speaker"), and setAppleAudioConfiguration. The only reliable workaround is briefly enabling the microphone, which causes an audible bleep and risks transmitting audio when users should not speak (listen-only sessions).

To Reproduce

Steps to reproduce the behavior:

  1. Create an Expo app with @livekit/react-native, @livekit/react-native-webrtc, call registerGlobals() at startup
  2. Connect to a LiveKit room with remote audio (e.g. host speaking) using the connect flow below
  3. Join without enabling the microphone
  4. Hold phone at arm's length — audio is inaudible (routed to earpiece)
  5. Enable microphone — audio immediately switches to loudspeaker ✅

Minimal connect flow:

await AudioSession.configureAudio({ ios: { defaultOutput: 'speaker' } });
await AudioSession.startAudioSession();
// selectAudioOutput before connect throws OSStatus -50
await room.connect(url, token);
// After connect: selectAudioOutput + setAppleAudioConfiguration have no effect
if (Platform.OS === 'ios') {
  await AudioSession.setAppleAudioConfiguration({
    audioCategory: 'playAndRecord',
    audioCategoryOptions: ['defaultToSpeaker', 'allowBluetooth', 'allowBluetoothA2DP', 'allowAirPlay'],
    audioMode: 'videoChat',
  });
  await AudioSession.selectAudioOutput('force_speaker');
}

Expected behavior

  • Audio should route to loudspeaker when defaultOutput: "speaker" is set
  • selectAudioOutput("force_speaker") should work after connect without errors
  • selectAudioOutput before connect should not throw OSStatus -50

Screenshots

N/A — audio routing behavior.

Device Info:

  • Device: testing on iPhone 13
  • OS: iOS 26.2.1

Dependencies Info (please reference your package-lock.json or yarn.lock file, not just your package.json):

  • @livekit/react-native: 2.9.6
  • livekit-client: 2.16.1
  • react-native-webrtc: @livekit/react-native-webrtc 137.0.2

Additional context

What we've tried (none work):

  • selectAudioOutput("force_speaker") before connect → throws OSStatus -50 (paramErr)
  • selectAudioOutput + setAppleAudioConfiguration at 100ms, 300ms, 800ms, 2s, 4s after connect → no error, no effect
  • room.on("trackSubscribed", ...) → apply config when first remote audio subscribes → no effect
  • Patched selectAudioOutput to use RTCAudioSession instead of AVAudioSession → OSStatus -50, connection fails
  • showAudioRoutePicker() shows Speaker selected, but physical output is still earpiece → suggests session/config mismatch

Behavior summary:

Scenario Result
Join (no mic) ❌ Earpiece
Join with Bluetooth ❌ Earpiece
Bluetooth disconnect mid-call ❌ Earpiece (expected: loudspeaker)
Enable mic ✅ Loudspeaker
If speaker already on → BT connect → BT disconnect ✅ Loudspeaker

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions