Smoother daemon: no-op reloads, batched placement, muted audio, graceful teardown

- Skip stream restarts when a reload resolves to the identical wall
  (same URLs, tile geometry, player settings) — saving an unrelated
  config edit no longer blanks the screen.
- Replace per-tile placement polling (a swaymsg fork per tile per second,
  plus 150ms get_tree polling per tile at startup) with one shared loop:
  a single get_tree snapshot per tick, re-placing only drifted windows,
  relaxing to a 2s tick once settled.
- Mute streams by default (--no-audio) to skip an audio decoder per
  stream; opt back in with player.audio: true.
- Graceful, parallel mpv teardown via cmd.Cancel/WaitDelay, fixing the
  double-Wait race between Stop and Supervise and cutting worst-case
  layout switches from ~2s x N streams to ~2s total.
- status: probe mpv IPC outside the daemon mutex so a slow probe can't
  block layout switches.
- View sync: reuse the Protect session across ticks (re-login only on
  expiry) and pick up view_refresh_seconds changes without a restart.
- Health strikes keyed by player, not slot, so they never carry across
  layout switches; prune stale entries.
- New `rtsp-streamer reload` CLI; fix dead sort in `layout ls`; back off
  on persistent control-socket accept errors; fsync config before the
  atomic rename (SD-card power-cut safety); bump IPC client deadline;
  gofmt stragglers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Woodard
2026-07-02 07:38:01 -05:00
parent 291d37ec83
commit 98ddf1645c
11 changed files with 325 additions and 140 deletions

View File

@@ -143,9 +143,15 @@ rtsp-streamer daemon # normally launched by sway, not by hand
rtsp-streamer status # slot health from the running daemon
rtsp-streamer layout ls # list layouts (* marks active)
rtsp-streamer layout set quad # switch live (persists the choice)
rtsp-streamer reload # apply hand-edits to the config live
rtsp-streamer version # baked-in git commit / build date
```
Reloads (and TUI saves) are minimal-impact: the daemon compares the resolved
wall — stream URLs, tile geometry, player settings — against what's already
running and leaves the streams untouched when nothing material changed, so
saving an unrelated edit never blanks the screen.
## UniFi Protect live views
Copy a saved Protect "Live View" (its cameras and grid) straight into a layout:
@@ -251,6 +257,8 @@ pegged, work through:
- **Latency drift** is handled by `--framedrop=decoder+vo` + low-delay demuxer
flags (built in), so a briefly-behind stream drops frames to catch up instead
of accumulating a backlog. Capping `player.max_fps` trims render load too.
- **Audio is off by default** (`--no-audio`), which skips one audio decoder per
stream. Set `player.audio: true` if you actually want camera sound.
## Troubleshooting
@@ -293,13 +301,12 @@ pegged, work through:
- Layout rotation / cycling on a timer (the daemon already re-tiles on demand).
- TUI create / delete / rename layouts (today the TUI only *edits* existing
ones; new layouts are added in YAML).
- `rtsp-streamer reload` CLI (so hand-edits apply live without the TUI or a
restart); optional git tags so `version` shows a release rather than a hash.
- Optional git tags so `version` shows a release rather than a hash.
## Layout
```
cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, status, tui, config, version
cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, reload, status, tui, config, version
internal/config/ YAML load/save/validate, schema (tiles, streams), XDG paths
internal/protect/ UniFi Protect client (login, bootstrap, enable RTSP, URLs)
internal/player/ one supervised mpv per stream, JSON IPC, restart/backoff