iOS: `audioDeviceModule` becomes permanently `nil`/unavailable after backgrounding, even though `AVAudioSession` is never interrupted
Los mantenedores suelen responder en 1 día
Evaluación
Este issue todavía no se ha evaluado.
Descripción
On iOS, after a Room stays connected through an app background → foreground cycle, the WebRTC audio device module (ADM) becomes unavailable and never recovers within the process lifetime — even though a native-level trace shows AVAudioSession itself is completely undisturbed by the transition (no interruption, no route change, no media services reset). This affects both:
AudioManager.instance.setEngineAvailability(...), which throwsPlatformException(setEngineAvailability, audio device module is unavailable, null, null).localParticipant.setMicrophoneEnabled(true), which throwsAudioProcessingException(platformUnavailable): audio device module is unavailableand does not recover on retry (tested up to 4 retries / ~2s).
Environment
livekit_client: 2.12.0- Flutter: 3.38.10
- Platform: iOS (physical device), deployment target 15.0
WebRTC-SDKpod: 144.7559.09 (as pinned bylivekit_client/flutter_webrtc)Info.plistalready declaresUIBackgroundModes: [audio]
Repro steps
- Join a
Roomas a participant who is not publishing audio/video (listen-only), so the app never callssetMicrophoneEnabled/setCameraEnabledbefore backgrounding. - Background the app (Home button / swipe up) and leave it backgrounded for roughly 10–30+ seconds. Do not disconnect the
Room— it stays connected the whole time (AudioSessionManagementMode.automatic, no manual session calls before this point). - Bring the app back to the foreground.
- From the app's resume/
AppLifecycleListener.onRestarthandler, callAudioManager.instance.setEngineAvailability(AudioEngineAvailability.none), or calllocalParticipant.setMicrophoneEnabled(true)to open the mic for the first time.
Actual behavior
Both calls fail immediately with the native side reporting the audio device module is unavailable:
PlatformException(setEngineAvailability, audio device module is unavailable, null, null)
AudioProcessingException(platformUnavailable): audio device module is unavailable
- Retrying
setMicrophoneEnabled(500ms backoff, 4 attempts) never succeeds. - Calling
setEngineAvailability(AudioEngineAvailability.none)first (in an attempt to "kick" the engine back awake, mirroring the CallKit gate pattern) also fails outright, because — perLiveKitPlugin.swift'shandleSetEngineAvailability— that call itself requires a liveaudioDeviceModuleto operate on:
guard let adm = FlutterWebRTCPlugin.sharedSingleton()?.peerConnectionFactory?.audioDeviceModule else {
result(FlutterError(code: "setEngineAvailability", message: "audio device module is unavailable", details: nil))
return
}
So it can't be used to recover a already-nil ADM — only to gate one that's already alive.
- We also tried
AudioManager.instance.setAudioSessionOptions(const AudioSessionOptions.communication())(switching toAudioSessionManagementMode.manualand immediately re-applying/activating the Apple audio session configuration) right before resuming, on the theory that proactively reconfiguringAVAudioSessionbefore WebRTC touches it again might help. It completed without throwing, but had no effect — the ADM was still unavailable afterward. - This is a permanent condition for the rest of the process lifetime, not a transient timing issue: we've seen it persist across multiple minutes and multiple foreground interactions in the same session, with no self-recovery observed. (We have not yet fully confirmed whether it ever self-heals given a much longer wait — but several minutes was not enough.)
Expected behavior
Either:
- The ADM should survive an app background/foreground cycle when the
Roomstays connected (no explicitdisconnect()), especially sinceUIBackgroundModes: audiois declared andAVAudioSessionitself shows no signs of interruption; or - There should be a supported, documented way to force-recreate/recover the ADM from the Dart API surface after this happens, distinct from
setEngineAvailability(which requires a live ADM as a precondition and can't be used to fix a dead one).
Diagnostic evidence: AVAudioSession is not the problem
To rule out an OS-level audio session interruption as the cause, we added native observers (in our own AppDelegate, not modifying any plugin) for:
UIApplication.willResignActiveNotification/didEnterBackgroundNotification/willEnterForegroundNotification/didBecomeActiveNotificationAVAudioSession.interruptionNotificationAVAudioSession.routeChangeNotificationAVAudioSession.mediaServicesWereResetNotification
At each app-lifecycle event we log AVAudioSession.sharedInstance()'s category, mode, isOtherAudioPlaying, secondaryAudioShouldBeSilencedHint, recordPermission, and currentRoute. Across a full background → foreground cycle that reproduced the ADM failure, the output was:
[AudioDebug] willResignActive | category=AVAudioSessionCategoryPlayback mode=AVAudioSessionModeSpokenAudio isOtherAudioPlaying=false secondaryAudioShouldBeSilenced=false recordPermission=granted route=<... outputs=(Speaker) inputs=() >
[AudioDebug] didEnterBackground | category=AVAudioSessionCategoryPlayback mode=AVAudioSessionModeSpokenAudio isOtherAudioPlaying=false secondaryAudioShouldBeSilenced=false recordPermission=granted route=<... outputs=(Speaker) inputs=() >
[AudioDebug] willEnterForeground | category=AVAudioSessionCategoryPlayback mode=AVAudioSessionModeSpokenAudio isOtherAudioPlaying=false secondaryAudioShouldBeSilenced=false recordPermission=granted route=<... outputs=(Speaker) inputs=() >
[AudioDebug] didBecomeActive | category=AVAudioSessionCategoryPlayback mode=AVAudioSessionModeSpokenAudio isOtherAudioPlaying=false secondaryAudioShouldBeSilenced=false recordPermission=granted route=<... outputs=(Speaker) inputs=() >
category/mode/isOtherAudioPlaying/route are byte-for-byte identical across all four events. No interruption, routeChange, or mediaServicesWereReset notification fired at any point. This strongly suggests the ADM teardown is not driven by any observable AVAudioSession-level event, but happens internally inside the WebRTC-SDK binary's own engine/session management, which we don't have source access to (it ships as a precompiled WebRTC-SDK pod, version 144.7559.09).
We also confirmed LKAudioEngineObserver (LiveKitPlugin.swift) — the Swift-side glue that owns iOS audio session activation timing — is purely reactive to RTCAudioDeviceModuleDelegate callbacks coming from the WebRTC-SDK binary; it does not itself observe UIApplication lifecycle notifications. So whatever decides to tear the engine/ADM down on backgrounding is a decision made inside the closed-source binary, not in anything on the Dart or Swift-glue side we can inspect or patch.
What we've already ruled out
- Upgrading to 2.12.0: we were originally on an older
livekit_clientand upgraded specifically hoping this would fix it (required a coordinatedfreezed/json_serializable/json_annotationbump in our app to unblock the version). It did not change the behavior. - PR #1182 / #1165: fixes iOS engine error
-9001for pre-connect audio / pre-join mic preview / mic-only scenarios, where the session policy hadn't been pushed to native yet because it's normally only pushed fromRoom.connect(). Our repro is post-connect, mid-session, well after the room is connected and the policy has been pushed — different code path. - PR #1179: same
-9001root cause as #1182, folded into it. - PR #1183 (
ensureMicrophoneAccess): addresses microphone permission requests silently stalling when triggered while the app isn't active. Not our case — our test device already has microphone permission.authorized(confirmed viaAVAudioSession.recordPermissionin the log above), so this permission-check path is a no-op for us. - We confirmed via direct inspection of the installed
livekit_client-2.12.0package source that all three of the above are already present in what we're running.
Question for maintainers
- Is this a known limitation of how the
WebRTC-SDKfork (144.7559.09) manages its audio engine/ADM across iOS app backgrounding, even whenUIBackgroundModes: audiois declared and the app never disconnects theRoom? - Is there a supported way (public API or documented pattern) to force the ADM to be recreated/recovered after this happens, short of a full app restart?
- Would adding instrumentation/logging around ADM creation/teardown inside the
WebRTC-SDKbinary (or exposing an event for it) be something the team would consider, givenAVAudioSession-level signals don't correlate with the failure at all?
Happy to provide a minimal reproduction project, a full device log, or test a patched build if that would help narrow this down further.
- Lenguaje dominante
- Dart
- Estrellas
- 421
- Forks
- 260
- Merge medio
- 2 d 5 h
- PR fusionados (30 d)
- 17
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de livekit/client-sdk-flutter
-
`jwtPayload.video` returns null grants for tokens from LiveKit server SDKsPosiblemente ocupada @RaphaelFakhri la tomó hace 3 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
livekit/client-sdk-flutter#1236 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
livekit/client-sdk-flutter#1227 ·
Los mantenedores suelen responder en 1 día
-
Inbound data packets never delivered while media flows normally (2.13.0, LiveKit Cloud 1.13.7)Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
livekit/client-sdk-flutter#1221 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 30/100
livekit/client-sdk-flutter#1219 · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
[bug]Early reconnect during the initial room connectionQuizá libre de nuevo @hiroshihorie la tomó hace 40 días y no hay ningún pull request abierto. Abierto
livekit/client-sdk-flutter#1189 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de livekit/client-sdk-flutter
Issues similares
-
documentation good first issue
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
djazairdev/wilayas#22 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 80/100
rajput-hemant/calculator#199 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
leancodepl/app-rating#35 ·
-
Mobile login: "Sign In" button is squashed to 16 px with its label cut while the keyboard is openAbiertoai-driven-qa bug claude
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
linagora/tmail-flutter#4976 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
aaronified/fluenough#503 ·
Los mantenedores suelen responder en 1 día