Three changes to make the stream survive constrained links: Adaptive quality: the sender pipeline now tracks the PLI rate from the receiver. Every 5 seconds it evaluates: >0.5 PLI/s means the link is saturated (the receiver is dropping frames), so the CRF increases by 2 (lower quality, fewer bits) and the encoder restarts with a keyframe. <0.1 PLI/s means the link is stable, so the CRF decreases by 1 (better quality) and the encoder probes upward. Clamped to [user CRF, user CRF + 10] so quality never degrades below what the link can handle, and never exceeds what the user asked for. The adaptation is logged to stderr for visibility. Frame rate capping (--fps N): throttles the capture loop to N frames per second (0 = no cap; monitor rate). At 15fps instead of 60fps, the bandwidth requirement drops 4x at the same quality level. Desktop content is still smooth at 15-20fps. Tighter VBV: one frame period of buffer instead of two. A two-frame buffer lets a keyframe spike to twice the target rate in one burst, which overflows any constrained hop (Wi-Fi hotspot, slow switch) and cascades into PLI storms. One frame period keeps bursts within what the link can absorb in real time.
screen_cast
A native Linux peer-to-peer screencast application.
- Send your desktop or a window to another Linux machine.
- Receive a stream and render it in a window.
- Discover receivers on the LAN via mDNS/Avahi; negotiate sessions with JSON signaling; stream H.264 over RTP/UDP.
Built with C++20, Meson, PipeWire, FFmpeg, and SDL3.
Quick start
Requirements:
- C++20 compiler with
<format>(GCC 13+, Clang 18+) - Meson >= 0.63, Ninja
- FFmpeg development packages (
libavcodec,libavutil,libswscale) - SDL3 development package (
sdl3) - nlohmann JSON (
nlohmann_json) and Avahi client (avahi-client) - For the sender only: PipeWire dev (
libpipewire-0.3) and libportal
Build and run tests:
meson setup build
meson compile -C build
meson test -C build --print-errorlogs
Stream between two machines:
screencast --receive # machine A: announces itself, opens a window
screencast --send # machine B: discovers A, negotiates, streams
See docs/RUNBOOK.md for all modes, flags, and validation procedures.
Receiver on a small ARM board (e.g. Raspberry Pi Zero 2 W)
The receiver does not need the sender's PipeWire/portal capture stack. On the board, run:
sudo ./scripts/install-receiver.sh
screencast --receive
The script installs the dependencies, builds a receiver-only binary
(-Dsender=false), runs the test suite, and installs to /usr/local/bin
(override with SC_RECEIVER_INSTALL_DIR). It needs Raspberry Pi OS Trixie
or newer (GCC 13+ for C++20 <format>), builds SDL3 from source when the
distribution does not package it, and enables avahi-daemon. It also
installs and enables a systemd service so the receiver starts at boot —
systemctl status screencast-receiver to check on it.
Performance note: decoding is software H.264; on very small boards expect smooth playback for modest resolutions and reduced frame rates at high resolutions. Hardware decode is planned for Phase 7. On a headless console the receiver runs fullscreen automatically with aspect-preserving letterboxing.
Architecture
See docs/ARCHITECTURE.md for module boundaries and design rules.
Roadmap
Development is split into phases in docs/PHASES.md.
Current phase: Phase 7 — Resilience and polish.
License
MIT — see LICENSE (to be added).