fix(ios): keep multitasking camera access across capture sessions - #88
Draft
renefloor wants to merge 1 commit into
Draft
fix(ios): keep multitasking camera access across capture sessions#88renefloor wants to merge 1 commit into
renefloor wants to merge 1 commit into
Conversation
A capture session is created per getUserMedia call, and a new one starts without multitasking camera access — so toggling the camera off and on lost it, and the camera stopped producing frames once the app was backgrounded. The requested state is now remembered and applied whenever capture starts, including when it was requested before a capture session existed. Adds isIOSMultitaskingCameraAccessSupported, which reports whether the capture session in use supports it, so callers can tell an unsupported device from access that was never asked for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
renefloor
added a commit
to GetStream/stream-video-flutter
that referenced
this pull request
Sep 11, 2026
…state The mute decision now reads isIOSMultitaskingCameraAccessSupported from the capture session in use instead of inferring it from whether the SDK managed to turn multitasking camera access on, which also reads false when it was never asked for. Requires the plugin fix in GetStream/webrtc-flutter#88, pinned here through a git override until it ships. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
getUserMediaallocates a newRTCCameraVideoCaptureron every video request (FlutterRTCMediaStream.m:606), and therefore a newAVCaptureSession. A fresh session starts withmultitaskingCameraAccessEnabled == NO.enableIOSMultitaskingCameraAccessonly ever wrote to the session that happened to exist when it was called, so the setting was lost as soon as the camera was toggled off and on — which is exactly what the video SDK does when muting the camera (it stops the track and recreates it on unmute). After the first toggle, iOS suspends capture the moment the app goes to the background, and remote participants are left looking at a frozen frame.The same call also dropped the request entirely when no capture session existed yet (
result(@NO)and nothing remembered), so asking before the camera started never took effect.The fix
Remember what was asked for, and apply it whenever a capture session starts.
FlutterWebRTCPlugingains amultitaskingCameraAccessRequestedflag, set byenableMultitaskingCameraAccess:before the nil-session check.-applyMultitaskingCameraAccessToCaptureSessionapplies the remembered request to the current session, no-op when it was never requested, when the device doesn't support it, or when it is already enabled.getUserVideo:calls it from thestartCaptureWithDevicecompletion handler, once the new session is configured and running.Camera switching (
CameraUtils.m:346) reuses the existing capturer and session, so it is unaffected and needs no change.New API
Helper.isIOSMultitaskingCameraAccessSupported()→Future<bool?>, backed by theisIOSMultitaskingCameraAccessSupportedmethod channel. It reportssession.isMultitaskingCameraAccessSupportedfor the capture session in use, andnullwhen there is no session to ask.This exists because
enableIOSMultitaskingCameraAccessreturnsfalsefor four different situations — unsupported device, iOS 15, no capture session, and "you asked me to disable it" — which leaves callers unable to tell a device that cannot do this from access that was simply never requested. The Stream Video Flutter SDK needs that distinction to decide whether to mute the video track while backgrounded (stream-video-flutter#1346), and today has to infer it.isMultitaskingCameraAccessSupportedis anAVCaptureSessioninstance property, not a device-wide one, hencenullrather thanfalsewhen no session exists —falsewould be a claim about the device that this cannot actually make.macOS gets a stub returning
NO, matching the existingenableMultitaskingCameraAccessstub there.Testing
flutter build ios --simulator --debugonexample/: succeeds, no new warnings.flutter build macos --debugonexample/: succeeds, no new warnings.flutter analyzeon the changed Dart files: clean.Not verified on device. The behaviour this fixes only shows up on hardware that supports multitasking camera access (M1 iPads and later) — the simulator reports
isMultitaskingCameraAccessSupported == false, so the new apply path is a no-op there. Confirming it needs an M1-or-later iPad in a call: enable camera, disable it, enable it again, background the app, and check that a second participant still receives frames. Before this change they would not.No CHANGELOG entry — this repo writes those in the release PR.
🤖 Generated with Claude Code