Skip to content

fix(llc): mute camera in background on devices without multitasking camera access - #1346

Draft
renefloor wants to merge 2 commits into
v2from
fix/background-video-mute-unsupported-devices
Draft

fix(llc): mute camera in background on devices without multitasking camera access#1346
renefloor wants to merge 2 commits into
v2from
fix/background-video-mute-unsupported-devices

Conversation

@renefloor

@renefloor renefloor commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

🎯 Goal

On iOS, the OS suspends camera capture as soon as the app goes to the background unless the device supports multitasking camera access (effectively M1 iPads and later). On every other device — older iPads, and any device on iOS 15 — the outgoing video track stays "on" while no frames are produced, so the other participants are left staring at a frozen frame for as long as the app is backgrounded. This is easy to hit with Picture-in-Picture, where backgrounding the app is the whole point.

The Swift SDK fixed this in 1.15.0 with ApplicationLifecycleVideoMuteAdapter: on devices where isMultitaskingCameraAccessSupported is false, it mutes the video track on background and unmutes on foreground, so remote participants see a clear camera-off state instead of a stuck image. This ports that behaviour to Flutter.

🛠 Implementation details

The decision lives in a new pure function, shouldMuteCameraInBackground in packages/stream_video/lib/src/internal/_background_mute_policy.dart:

if (!isVideoEnabled) return false;
if (muteVideoWhenInBackground) return true;

if (platform != PlatformType.ios) return false;

return multitaskingCameraAccessSupported != true;

StreamVideo._onAppState calls it in place of the previous _options.muteVideoWhenInBackground && isVideoEnabled check, passing CurrentPlatform.type and the device's multitasking camera access support, read once per background transition. The resume path is unchanged — _mutedCameraByStateChange already restores the camera and does not care why it was muted.

muteVideoWhenInBackground keeps its meaning as the explicit, all-platforms override and now has a doc comment noting that iOS may mute regardless. internal/ is not exported from the barrel, so no public API is added.

Two deliberate departures from the Swift implementation:

  1. Swift toggles only the SFU mute flag and leaves the capture session alone. This reuses setCameraEnabled(enabled: false), which also tears down capture. On iOS that costs nothing extra — the OS has already suspended the session — and it keeps a single resume path rather than two.
  2. Swift probes isMultitaskingCameraAccessSupported directly, and so does this now — through Helper.isIOSMultitaskingCameraAccessSupported(), added in GetStream/webrtc-flutter#88. null (support could not be read) is treated as "mute", so the frozen frame is never the fallback.

⚠️ Depends on a plugin change

This needs GetStream/webrtc-flutter#88, pinned here through a dependency_overrides entry on commit 3bb8787. The override must be dropped once a stream_webrtc_flutter release carries the fix.

That PR also fixes the bug underneath this one: a capture session is created per getUserMedia call and starts without multitasking camera access, so toggling the camera off and on lost it — even on an M1 iPad. Without it, this PR would correctly decide not to mute on a capable device that had silently lost the capability, and the frozen frame would come back.

🎨 UI Changes

No UI changes in this repo. The visible effect is on the remote side: a backgrounded participant on an unsupported device now renders as camera-off (avatar) rather than a frozen frame.

🧪 Testing

  • 7 new unit tests in packages/stream_video/test/src/internal/background_mute_policy_test.dart cover the platform gate, the multitasking-supported case, support that could not be read, the explicit option on every platform, and the camera-already-off cases.
  • Full stream_video suite: 688 tests passing.
  • flutter build ios --simulator --debug on dogfooding succeeds against the git-sourced plugin, so the override resolves end to end.

On-device behaviour is not verified. Reproducing it needs a pre-M1 iPad (or an iOS 15 device) in a call with a second participant watching: background the app and check whether the remote tile freezes or goes to camera-off. The device this was found on cannot currently run debug builds.

☑️Contributor Checklist

General

  • Assigned a person / code owner group (required)
  • Thread with the PR link started in a respective Slack channel (#flutter-team) (required)
  • PR is linked to the GitHub issue it resolves

☑️Reviewer Checklist

  • Sample runs & works
  • UI Changes correct (before & after images)
  • Bugs validated (bugfixes)
  • New feature tested and works
  • All code we touched has new or updated Documentation

🤖 Generated with Claude Code

…amera access

iOS suspends camera capture in the background unless the device supports
multitasking camera access, leaving other participants with a frozen frame.
On those devices the camera track is now muted while the app is backgrounded
and restored on resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.27273% with 8 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (v2@ad336b1). Learn more about missing BASE report.

Files with missing lines Patch % Lines
packages/stream_video/lib/src/stream_video.dart 0.00% 8 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##             v2    #1346   +/-   ##
=====================================
  Coverage      ?   31.16%           
=====================================
  Files         ?      375           
  Lines         ?    28799           
  Branches      ?        0           
=====================================
  Hits          ?     8975           
  Misses        ?    19824           
  Partials      ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant