From 9ff8e94679af6221853a0e25ee3d909e27146453 Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Wed, 9 Sep 2026 22:25:22 +0200 Subject: [PATCH] docs(readme): document all modes, GUI, resilience, and hotspot --- README.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f50550b..5326501 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ 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. +- **Receive** a stream and render it in a window — or fullscreen headless. - Discover receivers on the LAN via **mDNS/Avahi**; negotiate sessions with **JSON signaling**; stream **H.264 over RTP/UDP**. +- Control it from the **CLI**, a **GTK4 panel**, or a **waybar widget**. Built with **C++20**, **Meson**, **PipeWire**, **FFmpeg**, and **SDL3**. @@ -19,6 +20,7 @@ Requirements: - SDL3 development package (`sdl3`) - nlohmann JSON (`nlohmann_json`) and Avahi client (`avahi-client`) - For the sender only: PipeWire dev (`libpipewire-0.3`) and libportal +- Optional, for the GUI: gtkmm-4.0 Build and run tests: @@ -32,11 +34,69 @@ Stream between two machines: ```sh screencast --receive # machine A: announces itself, opens a window -screencast --send # machine B: discovers A, negotiates, streams +screencast --send # machine B: discovers A, negotiates, streams ``` See `docs/RUNBOOK.md` for all modes, flags, and validation procedures. +## Modes and flags + +```text +screencast --send [--target monitor|window] [--peer HOST[:PORT]] [--bitrate MAX_KBPS] [--crf 0-51] [--fps 1-60] +screencast --receive [--port PORT] [--signaling-port PORT] [--fullscreen] [--swdecode] +screencast --discover [--timeout SECONDS] +screencast waybar [--toggle] # for waybar widgets +``` + +| Mode | Flag | Default | Meaning | +|-------------|------------------|---------------|------------------------------------------------| +| `--send` | `--target` | `monitor` | `monitor` or `window` (portal source picker) | +| | `--peer` | auto-discover | target a receiver directly, `HOST[:PORT]` | +| | `--bitrate` | `4000` | VBV max bitrate (kbps) | +| | `--crf` | `22` | encoder quality (lower = better) | +| | `--fps` | capture rate | frame-rate cap, 1-60 | +| `--receive` | `--port` | `5004` | local RTP UDP port | +| | `--signaling-port` | `5005` | TCP signaling port announced via mDNS | +| | `--fullscreen` | off | force fullscreen (automatic under KMSDRM) | +| | `--swdecode` | off | skip the hardware decode probe | +| `--discover`| `--timeout` | `3` | seconds to wait for mDNS responses | +| `waybar` | `--toggle` | print status | toggle streaming (stop / restart last session) | + +`--send` without `--peer` discovers receivers on the LAN and requires that +exactly one is found; use `--discover` to list them. + +## GUI and waybar widget + +Build the GUI alongside the CLI (`meson configure build -Dgui=true`, then +recompile; installs as `screencast-gui`): + +```sh +screencast-gui # receiver list → pick one → bitrate → Start +screencast waybar # one JSON line for a waybar custom module +screencast waybar --toggle +``` + +The waybar module shows `▶` while streaming (tooltip: receiver, bitrate, +elapsed) and `⏸` while idle; left-click toggles streaming to the last +receiver, right-click opens the panel. All front-ends agree on state because +the sender publishes it to `$XDG_RUNTIME_DIR/screencast/sender.json`. + +## Under the hood: resilience + +The receiver absorbs loss in two stages and recovers actively: + +1. **Jitter window**: RTP packets are re-ordered by sequence number in a + small buffer (16 packets / 60 ms), so Wi-Fi reordering is not misread as + loss. In-order streams release immediately (zero added latency). +2. **PLI feedback**: when a frame arrives genuinely damaged, the receiver + drops it and asks the sender for a keyframe over the signaling channel + (rate-limited to one request per 500 ms); the sender re-encodes a + keyframe immediately, so recovery takes one frame time. + +**Hardware decode**: the receiver probes `h264_v4l2m2m` (the VideoCore path +on Raspberry Pi) and falls back to software automatically; `--swdecode` +forces software. + ## 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 @@ -53,13 +113,28 @@ The script installs the dependencies, builds a receiver-only binary or newer (GCC 13+ for C++20 ``), 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. +`systemctl status screencast-receiver` to check on it (stop with +`sudo systemctl stop screencast-receiver`; always SIGTERM, never `kill -9`, +or stale mDNS records linger). -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. +On a headless console the receiver runs fullscreen automatically with +aspect-preserving letterboxing — no window manager needed (SDL3 KMSDRM; it +renders on its own VT, tty7, so `Ctrl+Alt+F1` returns to the console). +See `docs/RUNBOOK.md` for the headless setup details. + +## Direct link: receiver as a Wi-Fi hotspot + +The receiver can act as a Wi-Fi access point, so a sender connects to the +board directly with no router in between: + +```sh +sudo scripts/pi-hotspot.sh on # prints SSID + generated password +sudo scripts/pi-hotspot.sh status # shows the saved credentials +sudo scripts/pi-hotspot.sh off # back to normal router Wi-Fi +``` + +While active the Pi is reachable at `10.42.0.1`; on the sender, join the +hotspot's Wi-Fi and run `screencast --send`. ## Architecture @@ -69,8 +144,12 @@ See `docs/ARCHITECTURE.md` for module boundaries and design rules. Development is split into phases in `docs/PHASES.md`. -Current phase: **Phase 7 — Resilience and polish**. +Current phase: **Phase 7 — Resilience and polish**. PLI recovery, the +jitter buffer, hardware decode, and the GUI/waybar front-ends are complete +and validated on a real two-machine setup (desktop → Raspberry Pi Zero 2 W +over Wi-Fi); deferred items are the VAAPI hardware encode probe and +packaging. ## License -MIT — see `LICENSE` (to be added). \ No newline at end of file +MIT — see `LICENSE` (to be added).