Skip to content

FCE-3392: Fix Android screen-share keyframe starvation#51

Merged
MiloszFilimowski merged 1 commit into
masterfrom
mfilimowski/fce-3392-fix-android-key-frame
Jun 8, 2026
Merged

FCE-3392: Fix Android screen-share keyframe starvation#51
MiloszFilimowski merged 1 commit into
masterfrom
mfilimowski/fce-3392-fix-android-key-frame

Conversation

@MiloszFilimowski

@MiloszFilimowski MiloszFilimowski commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Description

  • Add FrameRepeatingCapturerObserver, a CapturerObserver decorator that re-delivers the last captured frame at ~1fps so the encoder always has an input frame to attach a keyframe to.
  • Caches an I420 memory copy of the frame (never the single-buffered capture texture) and runs on the capturer handler thread.
  • Wire it into GetUserMediaImpl, scoped to screencast tracks only.

Motivation and Context

Android's ScreenCapturerAndroid only emits frames on screen content change, so on a static screen the encoder can't satisfy a keyframe request (PLI/FIR) from a late-joining receiver — leaving WHEP/WebRTC viewers black on reconnect. This mirrors libwebrtc's native zero-hertz screencast behaviour, which isn't reachable from the Android API.

…reen

Android's ScreenCapturerAndroid (MediaProjection/VirtualDisplay) only emits a
frame when the screen content changes — the startCapture framerate argument is
ignored. On a static screen no frames flow, so the encoder has no input frame to
encode and cannot satisfy a keyframe request (PLI/FIR) from a newly-joined
receiver (a keyframe is always attached to the next encoded frame). The result is
that late-joining WHEP/WebRTC viewers stay black for a long time, reproducible on
every reconnect.

Add FrameRepeatingCapturerObserver, a CapturerObserver decorator that re-delivers
the last captured frame at ~1fps so a keyframe is always producible on demand. It
caches an I420 memory copy (never the capture texture, which is single-buffered and
would freeze capture) and runs on the capturer handler thread. This mirrors
libwebrtc's native zero-hertz screencast behaviour, which cannot be engaged from
the Android API. Scoped to screencast tracks only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 3, 2026 15:18
@linear

linear Bot commented Jun 3, 2026

Copy link
Copy Markdown

FCE-3392

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Android screen-share “keyframe starvation” by ensuring that screencast sources keep providing frames even when the screen is static, allowing the encoder to respond to PLI/FIR keyframe requests from late-joining/reconnecting receivers.

Changes:

  • Introduces FrameRepeatingCapturerObserver, a CapturerObserver decorator that re-delivers the last captured frame at ~1fps.
  • Caches an I420 copy of the last frame (avoiding holding the single-buffer capture texture) and schedules repeats on the capturer handler thread.
  • Wires the decorator into GetUserMediaImpl for screencast tracks only.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java Wraps the VideoSource capturer observer for screencast capturers to enable idle-frame repeating.
android/src/main/java/com/oney/WebRTCModule/FrameRepeatingCapturerObserver.java Adds the repeating/caching observer implementation to keep keyframes producible on static screens.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@MiloszFilimowski MiloszFilimowski merged commit 339ddc2 into master Jun 8, 2026
8 checks passed
@MiloszFilimowski MiloszFilimowski deleted the mfilimowski/fce-3392-fix-android-key-frame branch June 8, 2026 10:48
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.

3 participants