2f0b8589207def1c4744ecb8b582896e651defd0
Paradigm shift: stop treating KWin's damage-driven frame delivery as an anomaly. Static content = no new frames is correct Wayland behavior. Root causes (combined #15 + #18): #15: stall detection threshold was 100ms (max(100ms, 3*frame_interval)). KWin/PipeWire damage-driven delivery meant normal static periods triggered WARN 'compositor frame delivery stalled' continuously. Test3 data showed 55% stall rate during active streaming (38 stalls in 69s). User perceived severe stutter pattern 'cardboard-effect freeze-release-freeze'. #18: filler mechanism (maybe_send_filler_frame) cloned 3MB NV12 data and sent to encode thread during stalls. Encode thread hashed Y plane, found duplicate, skipped via dedup. Net: wasted CPU + channel bandwidth with zero visual benefit (the dedup path was already catching it). Oracle review revealed the two issues are causally linked: filler is the failed response to the false stall alarm. Removing both together is correct. Fixes (all in state_portal.rs): 1. Remove filler mechanism entirely: - Remove fields: last_fillable_frame, next_filler_at, filler_frames_sent - Remove method: maybe_send_filler_frame (49 lines) - Remove constant: MAX_FILLER_DURATION - Remove fillable_frame clone cascade in handle_pw_frame (10 lines of 3MB NV12 cloning per frame, the largest CPU/memory win) - Remove filler_frames_sent from stats output 2. Redefine stall as idle (Oracle-revised): - Rename: stall_start -> idle_log_start (semantic clarity) - Change threshold: 100ms -> 5s (CAPTURE_IDLE_LOG_THRESHOLD) - Change log level: WARN -> DEBUG - Change wording: 'compositor frame delivery stalled' -> 'portal capture idle; no damage frames received (normal Wayland behavior)' - One-shot log per idle episode (not repeated every second) - Use last_capture_arrival as idle start for accurate elapsed duration (old code set stall_start=now at first detection, undercounting by threshold value) Explicit product decision (Oracle flagged trade-off): Static-content PLI repair is deferred. When WebRTC client sends PLI during static content: - Server sets force_keyframe_pending in encode thread - Encode thread blocks on input_rx.recv() (no frames coming) - Client may send more PLIs (all rate-limited by #23 to 1/sec) - When user interacts -> KWin delivers frame -> encode thread produces IDR This means during fully static content, client may wait for next damage to receive keyframe. Acceptable because static content is by definition unchanged - the last received frame is still visually accurate. If user reports unacceptable PLI latency on static screens, follow-up with event-driven one-shot IDR mechanism (Option B per Oracle). Verification expectation: - Zero WARN 'stalled' messages during normal session - Optional DEBUG 'portal capture idle' after 5s of no frames - Optional DEBUG 'portal capture resumed after idle period' on recovery - capture_fps will still vary with content activity (this is correct) - encoded_fps will only count real frames (filler no longer inflates it) Out of scope: - Event-driven cached-frame IDR (Option B): follow-up if needed - PipeWire CursorFromCache negotiation: separate enhancement - capture_fps expectations documentation: defer to #20 stats rework Tests: - cargo build --release: clean, no new warnings - cargo test: 91 passed + 3 passed + 0 failed - SAFETY comments preserved verbatim - Net change: -80 lines (20 insertions, 100 deletions) Closes #15, closes #18.
wl-webrtc
Wayland screen capture and encoding tool.
Prerequisites
- Rust toolchain (1.70+):
rustup default stable - FFmpeg 6.0+ dev libraries with VAAPI support:
- Arch:
pacman -S ffmpeg - Ubuntu/Debian:
apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libva-dev - Fedora:
dnf install ffmpeg-devel libva-devel
- Arch:
- Wayland dev libraries:
- Arch:
pacman -S wayland-protocols - Ubuntu/Debian:
apt install libwayland-dev wayland-protocols - Fedora:
dnf install wayland-devel wayland-protocols-devel
- Arch:
- DRM dev libraries:
- Arch:
pacman -S libdrm - Ubuntu/Debian:
apt install libdrm-dev - Fedora:
dnf install libdrm-devel
- Arch:
Build
cargo build --release
Run
# Basic capture to file
wl-webrtc --output output.mp4
# With custom FPS and bitrate
wl-webrtc --output output.mp4 --fps 60 --bitrate 8000000
# Specify DRM device for hardware encoding
wl-webrtc --output output.mp4 --drm-device /dev/dri/renderD128
# Verbose mode
wl-webrtc --output output.mp4 -v
CLI Arguments
| Argument | Default | Description |
|---|---|---|
-o, --output |
(required) | Output file path (e.g., output.mp4) |
--output-name |
auto | Wayland output name to capture |
--fps |
30 | Target frames per second |
--codec |
h264 | Video codec (h264 only for MVP) |
--hw-accel |
vaapi | Hardware acceleration method |
--drm-device |
auto | DRM render device path |
--bitrate |
auto | Target bitrate in bps |
--gop-size |
auto | Group of Pictures size |
-v, --verbose |
false | Enable verbose logging |
--port |
0 | WebTransport server port (unused in MVP) |
Languages
Rust
98.7%
Shell
1.2%