[iOS] No audio in either direction after a call resumes from CallKit hold: the AVAudioEngine ADM is never told about CallKit audio session (de)activation
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Ambito
- audio-video-rtc, mobile
Direzione di ricerca
Start by reading StreamVideoCallkitManager.provider(_:didActivate:) and provider(_:didDeactivate:), then trace how the Flutter SDK reaches the RTCAudioDeviceModule in stream_webrtc_flutter. Check how the React Native implementation handles engine availability for comparison. Done means CallKit hold and resume restore audio playback and microphone recording, including when the call has already connected.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Environment
stream_video_flutter/stream_video/stream_video_push_notification: git ref164832a0f29d1c0f6dd1edb9f2458bc568ad56ad(SDK version 1.7.2)stream_webrtc_flutter: 4.0.1 (StreamWebRTC 145.17.0)- iOS 26.6, iPhone 15 Pro
- Audio-only calls, reported to CallKit with
PushNotificationManager.startOutgoingCallafter join, thensetCallConnected
Steps to reproduce
- Join an audio call on iOS with CallKit integration.
- Receive a regular phone call and answer it, so CallKit puts the Stream call on hold (
ActionCallToggleHold(isOnHold: true)). - End the phone call, so the Stream call resumes from hold (from the system UI, or by the app).
Expected
Audio plays and the microphone records again, as before the hold.
Actual
The call's UI and state look normal, but nothing is heard either way. Calling Call.suspendAudio() on hold and Call.resumeAudio() after CallKit re-activates the audio session (ActionCallToggleAudioSession(isActive: true)) does not help: the tracks are re-enabled, and the engine still produces no audio.
Root cause
StreamWebRTC 145 uses an AVAudioEngine-based RTCAudioDeviceModule. With CallKit, the engine has to be told when CallKit takes the audio session away and gives it back, through -[RTCAudioDeviceModule setEngineAvailability:]. Nothing in the Flutter SDK calls it:
stream_webrtc_flutterdoesn't callsetEngineAvailabilityand doesn't expose it.StreamVideoCallkitManager.provider(_:didActivate:), for a call thathasConnected, only posts a syntheticAVAudioSession.interruptionNotification(type.ended, option.shouldResume) throughsendDefaultAudioInterruptionNofificationToStartAudioResource(). That restarted the older audio-unit ADM, but the AVAudioEngine ADM doesn't restart from it.provider(_:didDeactivate:)returns early while the call is on hold (it only printsCall is on hold), so nothing downstream learns the session went away.ActionCallToggleHoldandActionCallToggleAudioSessionare declared, but no SDK package acts on them.
The React Native SDK already handles this. GetStream/stream-video-js#2328 waits for CallKit's audio activation before starting the engine, and toggles engine availability on activation and deactivation (with GetStream/react-native-webrtc#53).
When it likely regressed
Probably in stream_video_flutter 1.4.0 / stream_webrtc_flutter 3.0.0 (2026-05-14). From the changelogs:
stream_webrtc_flutter |
StreamWebRTC (iOS) | stream_video_flutter |
|---|---|---|
| 1.0.5 (2025-05-20) to 2.2.7 (2026-04-27) | 125.6422.070 | up to 1.3.3 |
| 3.0.0 (2026-05-14) | 145.9.0 | 1.4.0 (per-call factory and ADM) |
| 3.2.0, 4.0.x | 145.17.0 | 1.5.0 to 1.7.2 |
There's no StreamWebRTC version between 125 and 145 in the Flutter plugin, so the ADM's behaviour can only have changed in that one jump. The synthetic "interruption ended" notification in didActivate was written for the older audio-unit ADM. Stream's AVAudioEngine-based ADM is already in @stream-io/react-native-webrtc 137.2.0, and the React Native SDK added engine availability for it.
We haven't verified that hold/resume works on 1.3.x: our app adopted Stream Video on 1.5+ / 3.2.0. If it helps, we can run the same hold test on 1.3.3 to confirm.
Workaround we use
We expose setEngineAvailability through a small app-side method channel. The plugin resolves the call's factory through FlutterWebRTCPlugin.sharedSingleton().resolveFactory(forId:) and runs on factory.admQueue:
factory.admQueue.async {
let code = adm.setEngineAvailability(RTCAudioEngineAvailability(
isInputAvailable: available,
isOutputAvailable: available
))
DispatchQueue.main.async { result(code) }
}
Then on the Dart side:
// On ActionCallToggleHold(isOnHold: true):
await call.suspendAudio();
await CallAudioEngine.setAvailable(call, available: false);
// Once CallKit re-activated the audio session (ActionCallToggleAudioSession(isActive: true)):
await CallAudioEngine.setAvailable(call, available: true);
await call.resumeAudio();
With this, the engine comes back running, playing and recording, and audio works both ways after the hold. Logs from our native state dump:
before suspending: engine running: true, playing: true, recording: true, available input/output: true/true
suspended: engine running: false, playing: false, available input/output: false/false
before resuming: engine running: false, playing: false, available input/output: false/false
resumed: engine running: true, playing: true, recording: true, available input/output: true/true
Suggested fix
The same thing the React Native SDK does: mark the engine unavailable in didDeactivate (including while the call is on hold), and available again in didActivate (including for a call that hasConnected). Ideally the SDK also exposes setEngineAvailability (for example on Call or the native factory) for apps that manage CallKit themselves.
- Lingua principale
- Dart
- Stelle
- 79
- Fork
- 55
- Merge medio
- 2g 11h
- PR unite (30g)
- 143
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di GetStream/stream-video-flutter
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 42/100
GetStream/stream-video-flutter#1430 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
GetStream/stream-video-flutter#1293 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
documentation
Difficoltà 4/5 3-5 giorni Idoneità per principianti 38/100
GetStream/stream-video-flutter#1066 · 8 commenti · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
enhancement
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
GetStream/stream-video-flutter#1047 · 4 commenti ·
I maintainer di solito rispondono entro 2 giorni
-
enhancement
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
GetStream/stream-video-flutter#1036 ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di GetStream/stream-video-flutter
Issue simili
-
bug to check
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
patarapolw/lingua_srs#103 ·
I maintainer di solito rispondono entro 1 giorno
-
[Feature]Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
I maintainer di solito rispondono entro 5 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
desktop linux photos
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno