From 74b3f040821b25afe68ff8d131c78a7add1e9f61 Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Tue, 8 Sep 2026 16:54:09 +0200 Subject: [PATCH] docs: record Phase 7 resilience state and re-scope --- .agents/MEMORY.md | 16 ++++++++++++++++ docs/PHASES.md | 22 +++++++++++++++------- docs/RUNBOOK.md | 17 +++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/.agents/MEMORY.md b/.agents/MEMORY.md index 7d4a94c..d8fa3cc 100644 --- a/.agents/MEMORY.md +++ b/.agents/MEMORY.md @@ -71,6 +71,22 @@ loopback; current phase is Phase 7. No application changes needed — the receiver already announces on all interfaces. nmcli property syntax validated against NM 1.58 with a disposable profile; AP bring-up itself can only be validated on the Pi. +- **Phase 7 resilience shipped** (pending Pi-side hw-decode run): + - PLI over signaling: `SessionPli` message; the depacketizer now returns + `DepacketizeResult{access_unit, frame_dropped}`; the receiver + rate-limits PLIs to 1/500 ms; the sender keeps the signaling channel + open and calls `SenderPipeline::request_keyframe()` (thread-safe + atomic → run-loop → encoder). Validated end-to-end with a probe that + drops a mid-keyframe packet: receiver logs "frame damaged", + PLI arrives with the session id. + - `RtpJitterBuffer` (rtp_packet.h): sequence reordering, 16 pkt/60 ms, + straggler discard via serial-number arithmetic, overflow flush for + genuine loss. Zero added latency on in-order streams. + - Hardware decode probe in DecoderFactory (`DecoderConfig.hardware_accel`, + default true): h264_v4l2m2m first, software fallback, `--swdecode` opts + out. On the desktop the probe fails cleanly ("Could not find a valid + device") and falls back; on the Pi it should pick the VideoCore m2m + device — NEEDS THE USER'S PI RUN to confirm. - **REAL-HARDWARE VALIDATION (desktop → Pi Zero 2 W over Wi-Fi)**: the full chain works on two machines: mDNS discovery → signaling negotiation → RTP over Wi-Fi → software H.264 decode → fullscreen KMSDRM letterboxed diff --git a/docs/PHASES.md b/docs/PHASES.md index 9fbc8d8..7990127 100644 --- a/docs/PHASES.md +++ b/docs/PHASES.md @@ -82,16 +82,24 @@ discovered addresses in reachability order (private IPv4 first). ## Phase 7 — Resilience and Polish -**Goal**: loss recovery, hardware acceleration, and packaging. +**Goal**: loss recovery, hardware acceleration, and polish. -- NACK / PLI feedback loop. -- Jitter buffer on the receiver. -- VAAPI/NVENC hardware encode probes and fallback. -- Optional GUI target behind `meson -Dgui=true`. -- `.desktop` file, icon, packaging notes. +- [x] PLI keyframe feedback over signaling (receiver asks, sender + re-encodes a keyframe; validated end-to-end with an induced loss). +- [x] Jitter/reorder window on the receiver (16 packets / 60 ms). +- [x] Hardware H.264 decode probe with software fallback (h264_v4l2m2m — + the Pi's VideoCore path; --swdecode opts out). +- [ ] GTK4 sender GUI behind `meson -Dgui=true` + a waybar widget + (requested; plan approved scope: sender panel, state file, + `screencast waybar` subcommand). +- [ ] VAAPI hardware encode probe on the sender (deferred: software + encode is not the bottleneck). +- Deferred: .desktop file, packaging. **Validation**: sustained streaming under packet loss; hardware accel smoke -where available. +where available. PLI + jitter validated on the desktop (induced-loss probe); +hardware decode validated as clean-fallback on the desktop, hardware path +pending a run on the Pi. ## Current phase diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index e333520..b0e1b5c 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -134,6 +134,23 @@ A window manager only comes with the desktop-session alternative, where the receiver runs inside it (uncomment the `Environment=` lines in the service file as described above). +## Under the hood: resilience (Phase 7) + +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 — recovery takes one frame time instead of + waiting out the rest of the GOP. + +**Hardware decode**: the receiver probes `h264_v4l2m2m` (the VideoCore +path on Raspberry Pi) and falls back to software automatically; the +journal says which path is active. `--swdecode` forces software. + ## Sender / receiver loopback (Phase 5, manual) Two terminals on the same desktop session: