FCE-3392: Fix Android screen-share keyframe starvation#51
Merged
MiloszFilimowski merged 1 commit intoJun 8, 2026
Merged
Conversation
…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>
There was a problem hiding this comment.
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, aCapturerObserverdecorator 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
GetUserMediaImplfor 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.
czerwiukk
approved these changes
Jun 8, 2026
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.
Description
FrameRepeatingCapturerObserver, aCapturerObserverdecorator that re-delivers the last captured frame at ~1fps so the encoder always has an input frame to attach a keyframe to.GetUserMediaImpl, scoped to screencast tracks only.Motivation and Context
Android's
ScreenCapturerAndroidonly 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.