Repository navigation
Screen stream (WebRTC): skip unchanged frames (Petal's dirty-rect gate), 1 s keep-alive - #36065
teamleaderleo wants to merge 8 commits into
Conversation
…eep-alive Petal's dirty-rect skip gate (session/share.rs, Apache-2.0): a complete SCK frame with zero dirty rects, the same size and the pointer where it was stays out of the encoder; unknown dirty rects count as changed. The idle refresh that re-sent the last frame every 500 ms now does so once a second. The host's 3 s video log line counts the skipped frames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
…et before a capture starts Review of #36065: the pointer is read as a frame arrives, so the frame where the cursor came to rest could be skipped and the viewer kept it a few pixels off. A frame now also goes out when the pointer differs from the frame just before. The first-frame reset moves before startCapture, so a new stream's first frame is never compared with the old one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
…er the old stream stops Review at 5fbe987: a frame captured mid-move but read after the pointer settled let the gate skip the frame that showed the cursor at rest, and the keep-alive resent the stale one. The backend now keeps the newest skipped frame and the keep-alive sends it, so a missed cursor shows within a second. A display switch also clears the gate again once the old stream stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (claude-opus-5-5): SHIP at 34ecf05
Independent review subagent (Claude Opus 5.5, medium): SHIP at c92fb1b after two rounds (stale-cursor finding fixed by sending the newest held frame on the keep-alive; display-switch reset after the old stream stops; held buffer pins at most one SCK surface of 4–6; locks never nested). 34ecf05 only moves two doc-comment lines onto takeSkippedUnchangedFrames (delta read directly). Compile check CmuxVNCWebRTC at c92fb1b running; previous heads passed.
|
Review (codex gpt-6-astra high, router-review): BLOCK at 34ecf05 BLOCK at 34ecf05 Visual verdict: no images; Leo tries the build
|
Codex review at 34ecf05: a frame the previous stream sent while it stopped could seed lastDelivered after the reset and make the new display's first unchanged frame skip. The backend now keeps the stream it delivers from and drops frames from any other (restored to the running one when a start fails). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
|
Replies to the router review at 34ecf05:
|
|
Found 3 test failures on Blacksmith runners: Failures
|
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) web and size checks: green |
|
For the owning lane (stream-webrtc-tune): this branch predates #36043, so its head has no |
|
Review (codex gpt-6-astra high, router-review): BLOCK at 50be71a BLOCK at 50be71a Visual verdict: no images; Leo tries the build
|
…sleep Codex review at 50be71a: a capture that failed while an earlier one was still starting restored the running stream's token, so the earlier one, once installed, had its frames dropped. Installing a stream now makes it the delivering one (the gate starts over when that moves it). The keep-alive's Duration.milliseconds also takes an explicit Int64. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
|
Review (codex, router-review): SHIP at 4bb1350 Reviewer: codex gpt-6.1-sol xhigh, fast tier. Follow-ups: 1. They land as checklist items in this PR's one follow-up tracking issue in this repo ( SHIP at 4bb1350 Visual verdict: no images; Leo tries the build
Follow-ups:
|
…n once a second The G0 eval (cmux-austin-mini-0, feat-cmux-next 94aa138, three runs each) showed the 1 s keep-alive cost text sharpness: OCR error 17.8 % against 11.2 % before, while idle fell from 76 to 63 kbps. The encoder sharpens a still picture with each frame, so the picture now goes again every 250 ms for the first 2 s after capture last sent one, then once a second. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-29da0cfaa02255d6b3d5
Numbers, and why this closesFrom the G0 screen-stream eval (
What this shows:
So nothing here is worth landing: the gate has no measured effect and the keep-alive change is a net loss. A low-QP or lossless refresh once the screen goes static (Petal's next step) is the better route to sharp, cheap idle frames. It needs encoder control that the Objective-C SDK doesn't expose, so it stays a note on cmuxterm-hq#7146 for now. The branch stays as it is in case a capture source turns up that sends complete frames with no dirty rects. |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-11-05: "honestly i'd rather us look at lot more at Petal and stuff like we ought to be able to get a lot more efficient and start going towards crazy stuff... after being able to do the basics end to end and adding eval measurements, ofc", and on the Petal list: "obv do all these and more".
HQ#7146 item 3 of the Petal ports (WebRTC path,
CmuxVNCWebRTC): the static-screen half. The low-QP or lossless still once the screen goes static is a follow-up.What changes
ScreenCaptureBackend). A complete ScreenCaptureKit frame whosedirtyRectsattachment is empty, at the same size and with the pointer where it was, no longer goes to the encoder. A missing attachment counts as changed (Petal's "unknown is always pushed"), and so do the first frame after a capture start, a size change and a pointer move (the cursor is drawn into the picture and may not count as dirty). Ported from Petal (https://github.com/kiruna-labs/petal @ 7ac436a, Apache-2.0,apps/desktop/src-tauri/src/session/share.rs:2060-2290).ScreenStreamHost). The idle refresh re-sent the last frame every 500 ms so a lost keyframe or a quality step had a frame to encode; it now does so after 1 s without a frame (Petal's static refresh floor).video:log line addsunchanged-skipped N fps, so a run shows how often the gate fires.Numbers
Before and after from screen-stream-g0's harness (
scripts/measure/screen-stream-eval.sh,webrtctarget; idle kbps and fps are the ones this should move, typing/scroll/latency should not get worse): pending. The harness is cmux#36043, not yet on feat-cmux-next. This PR stays a draft until the numbers are in.Tests
FrameRateTests.unchangedFramesStayOutOfTheEncoder(new).Observed / Not run
Changelog
🤖 Generated with Claude Code
Agent session: agent session
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Skips unchanged frames in the WebRTC screen stream so a still Mac screen stops sending redundant frames, and refreshes it just enough to keep text sharp: every 250 ms for the first 2 s after the screen settles, then once a second (the 1 s floor alone blurred text, 17.8 % vs 11.2 % OCR error).
Written for commit a6718c6. Summary will update on new commits.