fix(rtc): pace WebRTC data-channel sends against SCTP buffered_amount (VA-105) #109

Merged
David merged 1 commit from fix/VA-105-rtc-datachannel-backpressure into main 2026-07-04 06:50:13 +02:00
Owner

Problem

The Desktop tab renders for a same-LAN viewer but shows a black screen for remote viewers, even though DRM/KMS capture works everywhere (all hosts ship real tiles). Observed 2026-07-04 on the identical build 0.3.0-12-g92b1f07: desktop-02 (same LAN as the browser, connected via peer-reflexive 172.16.100.120) rendered; desktop-01 (X11 greeter, remote/STUN srflx) and desktop-03 (Wayland, remote) went black.

Root cause: when the WebRTC data channel opens, the switchover re-baseline (state.invalidate()) forces a full keyframe (~2040 tile messages, ~2.4 MB) and RtcUpgrade::send pushed every message straight into SCTP with no regard for the channel's buffered_amount. A same-LAN peer drains the send buffer fast enough to survive the burst; a remote / high-RTT peer does not, so the webrtc-rs SCTP association overruns and breaks (webrtc_sctp::association: failed to handle_inbound: ErrChunk -> DataChannel is not opened -> revert to WS) before the keyframe lands. The local-vs-remote split is the tell: SCTP drain rate, not capture, decides the outcome.

Fix

Gate RtcUpgrade::send on RTCDataChannel::buffered_amount(). At/above a 256 KiB high watermark it waits (5 ms poll, bounded by a 3 s drain timeout) for the buffer to drain below the watermark before pushing the next message, pacing the keyframe burst to the link's real drain rate so every tile still lands on the data channel, just spread over time. If the buffer stays saturated past the timeout, send returns the new RtcError::Backpressure, which DesktopRtc::try_send routes over the WS for that one frame without counting it toward MAX_CONSECUTIVE_SEND_FAILS teardown (the channel is healthy, just slow behind a slow link).

Testing

  • just pre-commit (fmt + clippy --deny warnings + build + test --lib) green in Docker.
  • Needs an on-desktop retest: the remote hosts (desktop-01 / desktop-03) should now paint the keyframe over the data channel instead of going black; same-LAN desktop-02 should continue to render (no regression).

Fixes VA-105.

## Problem The Desktop tab renders for a same-LAN viewer but shows a **black screen for remote viewers**, even though DRM/KMS capture works everywhere (all hosts ship real tiles). Observed 2026-07-04 on the identical build `0.3.0-12-g92b1f07`: desktop-02 (same LAN as the browser, connected via peer-reflexive `172.16.100.120`) rendered; desktop-01 (X11 greeter, remote/STUN srflx) and desktop-03 (Wayland, remote) went black. Root cause: when the WebRTC data channel opens, the switchover re-baseline (`state.invalidate()`) forces a full keyframe (~2040 tile messages, ~2.4 MB) and `RtcUpgrade::send` pushed every message straight into SCTP with no regard for the channel's `buffered_amount`. A same-LAN peer drains the send buffer fast enough to survive the burst; a remote / high-RTT peer does not, so the webrtc-rs SCTP association overruns and breaks (`webrtc_sctp::association: failed to handle_inbound: ErrChunk` -> `DataChannel is not opened` -> revert to WS) before the keyframe lands. The local-vs-remote split is the tell: SCTP drain rate, not capture, decides the outcome. ## Fix Gate `RtcUpgrade::send` on `RTCDataChannel::buffered_amount()`. At/above a 256 KiB high watermark it waits (5 ms poll, bounded by a 3 s drain timeout) for the buffer to drain below the watermark before pushing the next message, pacing the keyframe burst to the link's real drain rate so every tile still lands on the data channel, just spread over time. If the buffer stays saturated past the timeout, `send` returns the new `RtcError::Backpressure`, which `DesktopRtc::try_send` routes over the WS for that one frame **without** counting it toward `MAX_CONSECUTIVE_SEND_FAILS` teardown (the channel is healthy, just slow behind a slow link). ## Testing - `just pre-commit` (fmt + clippy `--deny warnings` + build + `test --lib`) green in Docker. - Needs an on-desktop retest: the remote hosts (desktop-01 / desktop-03) should now paint the keyframe over the data channel instead of going black; same-LAN desktop-02 should continue to render (no regression). Fixes VA-105.
fix(rtc): pace data-channel sends against SCTP buffered_amount
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 11m16s
Create release / Create release from merged PR (pull_request) Has been skipped
6ceafe2ece
The desktop tab blasts a full keyframe (~2040 tile messages, ~2.4 MB) into the WebRTC data channel the instant it opens, because the switchover re-baseline (state.invalidate) forces every tile at once and RtcUpgrade::send pushed each message straight into SCTP with no regard for the channel's buffered_amount. A same-LAN viewer drains the send buffer fast enough to survive the burst; a remote / high-RTT viewer does not, so the webrtc-rs SCTP association overruns and breaks with ErrChunk, the channel dies, and the viewer is left on a black screen while a same-LAN viewer renders fine. Observed 2026-07-04: desktop-02 (same LAN, peer-reflexive candidate) rendered; desktop-01 and desktop-03 (remote, STUN srflx) went black on the identical build.

Gate RtcUpgrade::send on RTCDataChannel::buffered_amount: when the buffer is at/above a 256 KiB high watermark, wait (polling every 5 ms, bounded by a 3 s drain timeout) for it to drain below the watermark before pushing the next message, pacing the burst to the link's real drain rate so every tile still lands on the data channel. If it stays saturated past the timeout, return the new RtcError::Backpressure, which DesktopRtc::try_send routes over the WS for that one frame without counting it toward MAX_CONSECUTIVE_SEND_FAILS teardown (the channel is healthy, just slow).

#VA-105

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David scheduled this pull request to auto merge when all checks succeed 2026-07-04 06:43:07 +02:00
David merged commit 16fa2bbf64 into main 2026-07-04 06:50:13 +02:00
David deleted branch fix/VA-105-rtc-datachannel-backpressure 2026-07-04 06:50:14 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-agent!109
No description provided.