Over hours the wall drifted ~15s behind real-time: the Pi decodes a hair
slower than real-time, and since RTSP-over-TCP delivers every byte, that
small deficit buffers up instead of being dropped — and a live stream
can't be seeked back to the live edge.
Add player.resync_seconds (0 = off): the daemon reconnects each stream to
the live edge on that interval via mpv loadfile-replace over IPC, cycling
one tile at a time so only a single tile ever blips. Reusing the running
mpv process means no window teardown. At 600s this caps drift to a couple
seconds. config.example.yaml ships it at 600; README documents the knob
and the "also lighten decode load" caveat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two related gaps on the headless kiosk: no way to restart the wall short
of a full reboot, and no way to see why a stream keeps flapping (the
daemon's stderr goes to sway on tty1, and mpv's stderr was discarded).
Restart in place:
- `rtsp-streamer restart` sends a control-socket command; the daemon
tears down its mpv children and syscall.Exec's the on-disk binary.
Same PID, same parent (sway), same env — keeps WAYLAND_DISPLAY/
SWAYSOCK and comes back up on the new binary, no reboot. Handles the
os.Executable() "(deleted)" sentinel from make install's rename.
- sway config now launches the daemon in a relaunch loop, so a crash (or
the restart) auto-recovers instead of leaving a black screen.
- `make deploy` now does `install` + `restart` instead of restarting
getty@tty1 (which left stale duplicate sessions and forced reboots).
Retrievable logs:
- New internal/logbuf ring; the daemon tees slog output into it and
serves the tail over the socket via `rtsp-streamer logs [-n N]` —
readable over SSH, no file wrangling, no reboot.
- Capture the tail of each mpv's stderr and log its last line when the
process exits, so "mpv exited, will restart" now carries the reason
(connection refused, unsupported codec, 401, ...). This is the
diagnostic for a single tile going unhealthy repeatedly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diagnosis: individual tiles flickered when their stream died and mpv
relaunched (confirmed via status: a PID dropping to 0 and coming back).
The restarted window mapped at the video's native size wherever sway
dropped it, and the corrective loop only snapped it into its cell on the
next tick — up to 2s later on the relaxed cadence.
Fix, at the source instead of racing the map:
- Pre-install a per-slot for_window rule (matched on each mpv's unique
window title, anchored so slot-1 never matches slot-10) so sway
positions and sizes the window synchronously the moment it maps.
- Pass --geometry=WxH per tile so mpv opens at the tile size rather
than resizing itself to the video's native size on load.
The placeLoop remains as a corrective backstop for mid-life drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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>
views ls/dump/import copy a Protect live view (cameras + grid) into a
layout; grid inferred from slot count (asymmetric presets TBD, see
views dump). Imported layouts link back via protect_view.
Daemon re-syncs the active linked view every view_refresh_seconds (0=
off), so Protect-side edits appear on the wall. Needs the controller
password at runtime; off by default keeps the wall credential-free.
Shared viewmap package used by CLI and daemon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cameras carry multiple stream URLs (streams: high/medium/low); each
tile picks one via Quality (auto/high/low). Small tiles can pull the
low substream to cut Pi decode load. discover --enable-rtsp takes a
comma list (high,low), enables each channel, records all enabled ones.
TUI: q cycles a tile quality (shown in the tile); discover enables
high+low. Player: --framedrop + low-delay demuxer flags; player.max_fps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Probe mpv-version (always present) instead of time-pos, which live
streams with caching off can report unavailable while playing fine —
that produced false unhealthy verdicts. Also require 3 consecutive
misses (~60s) before force-restarting a hung stream.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mpv 0.35 rejects --auto-window-resize, which crash-looped every player.
Drop that flag and re-assert each tile geometry on a 1s timer in the
daemon, overriding mpv auto-resize on any mpv version.
Add make install (auto-sudo, native build + copy to /usr/local/bin)
and make deploy (install + restart the kiosk getty unit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>