Skip to content

fix(video): stop the once-a-second hitch and play frames without a jitter buffer - #78

Merged
Rust-KVM merged 2 commits into
devfrom
claude/project-thread-nvyxx0
Oct 20, 2026
Merged

Rust-KVM merged 2 commits into
devfrom
claude/project-thread-nvyxx0

Conversation

@Rust-KVM

@Rust-KVM Rust-KVM commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Requested by 4c · project thread

Summary

Before: on a LAN the remote cursor didn't keep up with the hand and the picture drifted and caught up in jumps. The encoder sent a 4K IDR (~350 KB, ~28 ms on the wire) every second, so the next 2-3 frames arrived late each second, and the browser's jitter buffer grew to absorb the bursts.

After: periodic IDRs come every 10 s, viewers still get one immediately on join or loss, and the browser plays video frames as soon as they decode.

Measured on the T6 (4K60 H.264, static desktop, 60 s, RTP-level client on the LAN, no input sent):

GOP 60 (old default) GOP 600
keyframes 62 6
frame-gap p99 46.3 ms 16.6 ms
gaps > 50 ms 23 0

How

  • pipeline.rs: the dynamic GOP is fps × GOP_SECONDS (10). PLI/FIR-driven keyframes are spaced at least 500 ms apart (KEYFRAME_MIN_INTERVAL); a misbehaving client produced ~2,600 IDRs a minute before. Resuming encoding for a viewer uses force_keyframe_now, so the limit can never leave a new viewer waiting a whole GOP.
  • net/peer.rs: play_out_immediately sets jitterBufferTarget = 0 and Chrome's playoutDelayHint = 0 on the video receiver only (audio keeps its buffer), via Reflect, so browsers without them ignore it.

Remaining: with motion a 4K IDR reaches ~1.5 MB and arrives in ~128 ms, so there is still one hitch per 10 s. The send path delivers only ~95 Mbps on a gigabit LAN; that is the next fix.

Testing

  • cargo +nightly fmt --all
  • cargo +nightly clippy -p rustkvm --release --all-targets -- -D warnings, and rkvm-web clippy for wasm32
  • cargo +nightly test -p rustkvm
  • GOP effect measured on an RK3588 device via RUSTKVM_VIDEO_GOP=600 (table above)
  • Same change built and run on the device 2026-10-20 (closed duplicate fix(video): stop the once-a-second keyframe stall #79; differs only in audio keeping its buffer and the resume path): "Set dynamic GOP to 600 (10s)", 4K60 capture, 0 errors; 60 s with motion: 59.8 fps, 4 IDRs, frame-gap p50 16.0 ms, p99 27.3 ms

Checklist

  • No #[allow(...)] suppressions and no new unwrap() / expect() in production paths
  • Shared protocol types live in rkvm-proto
  • RPC method set changed → not changed
  • Module tree or build invariants changed → not changed

🤖 Generated with Claude Code

https://claude.ai/code/session_01GRSn3XsjYLc494XwPez2mF

WebRTC buffers received video by default to smooth playback, which on
a LAN only puts the host's cursor further behind the user's hand. Set
jitterBufferTarget (standard) and playoutDelayHint (Chrome) to 0 on the
video receiver; audio keeps its buffer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GRSn3XsjYLc494XwPez2mF
@github-actions github-actions Bot added the rkvm-web Leptos / WASM frontend label Oct 19, 2026
…rames

With no GOP configured the encoder emitted an IDR every second. At 4K a
keyframe is ~350 KB and takes ~28 ms to send, so the 2-3 frames behind
it arrive late: a hitch every second that makes the cursor feel like it
lags and catches up. Measured on the T6 with a RTP-level client over
60 s of a static desktop: 1 s GOP gave frame-gap p99 46.3 ms and 23 gaps
over 50 ms; 10 s GOP gave p99 16.6 ms and none.

The dynamic GOP is now 10 s of frames. Viewers still get an IDR at once
when they join or report loss (PLI/FIR), but PLI-driven keyframes are
spaced at least 500 ms apart: a misbehaving client was able to make the
encoder emit ~2,600 IDRs a minute. Resuming encoding for a viewer
bypasses the limit, since it needs an IDR to start decoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GRSn3XsjYLc494XwPez2mF
@github-actions github-actions Bot added the area: video GStreamer, MPP encoding, WebRTC media label Oct 19, 2026
@Rust-KVM Rust-KVM changed the title fix(web): play video frames without a jitter buffer fix(video): stop the once-a-second hitch and play frames without a jitter buffer Oct 19, 2026
@Rust-KVM
Rust-KVM marked this pull request as ready for review October 20, 2026 14:49
@Rust-KVM
Rust-KVM merged commit 9c3938a into dev Oct 20, 2026
13 checks passed
@Rust-KVM
Rust-KVM deleted the claude/project-thread-nvyxx0 branch October 20, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: video GStreamer, MPP encoding, WebRTC media rkvm-web Leptos / WASM frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants