f38adf70f95b4926deb509c66b1aeb362ea31828
Root cause (verified via code reading + Oracle design review): - webrtc_paused IS correctly initialized to true in StatePortal::new() - encode_cpu_frame DOES respect paused via early return - BUT encode_thread_loop unconditionally sent timing on every Ok(()), causing stats.record_encode_thread to tick encoded_frames even for paused-dropped frames -> phantom encoded_fps=29.7 during idle - Real waste: handle_pw_frame imports DMA-BUF + VAAPI scale + NV12 clone + crossbeam send at 60fps even when no WebRTC client is connected Fix (Option B - surgical bugfix): - Add EncodeOutcome enum (Encoded/SkippedPaused/SkippedDisconnected/ SkippedDuplicate) to encode_cpu_frame return type - encode_thread_loop only reports timing on Ok(Encoded), not on skips -> encoded_fps naturally stays at 0 during idle (also helps #20) - handle_pw_frame entry: early return on paused, skipping ALL frame processing (DMA-BUF import, VAAPI scale, NV12 clone, channel send) - MP4 mode unchanged (webrtc_paused is None, gate is no-op) - Side benefit: last_fillable_frame stays None during initial idle, so maybe_send_filler_frame also early-returns -> no filler waste during the pre-connect idle window (partial mitigation for #18) Out of scope (TODO comment added at state_portal.rs:178): - Encoder still initializes on first PipeWire frame (one-time ~50ms SwEncEncode::new_webrtc cost). Full deferral (Option A) requires splitting WebRTC signaling lifecycle from media lifecycle - deferred until startup cost becomes user-perceptible - Bitrate formula unchanged (5*W*H*fps/100) - tracked by #21 Verification (34s idle + 60s connected session): - 0 'skipping duplicate frame' events during idle (was ~30/sec before) - 0 BWE bitrate updates during idle - 0 IDR production during idle (was 180 frames into the void) - First IDR produced 178ms after connect (ForceKeyframe -> IDR in 10ms) - cargo test transform/fps_limit/backend_detect: 34 passed - SAFETY comments preserved verbatim
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%