Repository navigation
macOS screen capture still freezes on the first frame while the cursor moves (0.17.0) #344
Description
Describe the bug
The frozen screen from #80 is still happening for us on 0.17.0. We ship a desktop app built on webrtc-java, and one of our macOS users shared their whole screen with VideoDesktopSource (a ScreenCapturer source). Everyone else saw the first frame of their screen and nothing after that. Their mouse cursor kept moving around on top of it, but the windows underneath stayed frozen, including an animation that was stuck halfway through. Sharing a single window (WindowCapturer source) didn't work for them at all.
That's the same thing luokeith described in #80 in June 2024 on macOS 14.2.1 ("the screen displays only the content of the first frame, but the cursor is movable"). It looks like d1f1035 didn't fully fix it.
To Reproduce
- On macOS, list sources with
ScreenCapturerand pick a screen. VideoDesktopSource,setSourceId(id, false),start(), and send the track to a peer.- Move some windows around on the shared screen. The far side keeps showing the first frame, with only the cursor moving.
Expected behavior
The far side sees the screen as it changes.
Desktop
- OS: macOS (we're still getting the exact version from the user and will add it here)
- webrtc-java: 0.17.0
Additional context
We don't have a Mac on hand, so this comes from reading the 0.17.0 source and libwebrtc at branch-heads/7977, not from a debugger. Here's what we think is going on:
DesktopCaptureOptions::allow_sck_capturer()defaults to false, andVideoTrackDesktopSource::capture()never turns it on. Soscreen_capturer_darwin.mmnever tries the ScreenCaptureKit capturer, and you always getScreenCapturerMac, the old CGDisplayStream one.ScreenCapturerMaconly gets new pixels when its CGDisplayStream handler fires.RegisterRefreshAndMoveHandlersadds that stream's source toCFRunLoopGetCurrent(), the capture thread's run loop, which the capture loop pumps withCFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true)once per frame. If the handler stops firing,CaptureFrame()keeps copying the previous frame and still reportsSUCCESS.DesktopAndCursorComposerdraws the cursor fresh on every frame. So a stale frame with a live cursor is exactly what you'd expect to see, and frames keep flowing, so nothing on our side notices.- Window capture goes through
WindowCapturerMac→DesktopFrameCGImage::CreateForWindow→CGWindowListCreateImage, which Apple deprecated in favour of ScreenCaptureKit. When it returns nothing, the capture is justERROR_TEMPORARYevery frame, so no frames go out at all.
What we'd suggest is setting options.set_allow_sck_capturer(true) on macOS in VideoTrackDesktopSource::capture(), and probably in DesktopCapturer for source listing too. libwebrtc already falls back to ScreenCapturerMac where ScreenCaptureKit isn't available. For windows it might be worth looking at allow_sck_system_picker as well, so macOS's own picker handles screens and windows the way the Wayland portal does now.
Chromium uses ScreenCaptureKit for its own screen sharing on recent macOS, so that seems like the right direction. We're happy to test a build, or to send a PR if that helps.
Activity
devopvoid commentedon Oct 11, 2026
Hi, thanks for reporting the issue and for your suggestions. I'll take a look at it over the next few days. I have an Apple Silicon M2 device available for testing.
devopvoid commentedon Oct 11, 2026
Thanks again for the detailed analysis. It matches what I found in WebRTC: until its CGDisplayStream calls back,
ScreenCapturerMacuses a snapshot of the whole screen. After that it only copies the areas the stream reports as changed. So if the stream never calls back, you get the first frame for good, with the cursor drawn live on top.I couldn't reproduce it on macOS 14.5 on an M2. The current capturer followed a window flashing in another process every 400 ms, and window capture worked too. So I'd like to know a bit more about your user's setup:
#354 switches screen capture to ScreenCaptureKit on macOS 14 and newer, as you suggested, and keeps the old capturer on older releases. I've left source listing on the old capturer, because without the system picker ScreenCaptureKit returns an empty screen list. Window capture is unchanged for now, because this WebRTC branch has no ScreenCaptureKit window capturer outside the system picker. I'll look at the picker separately. A test build of #354 on your user's machine would help a lot.
fix: capture screens with ScreenCaptureKit on macOS (#354)