docs: update README for tiles, mouse, per-tile quality, deploy, roadmap

Document the grid+tiles model, mouse/quality editing, git-based deploy
(run as your user, reboot not getty), Pi 4 hwdec/H.265/substream notes,
and the real troubleshooting lessons. Add a Roadmap incl. importing
pre-made UniFi Protect views.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Woodard
2026-07-01 21:58:52 -05:00
parent 8d95c944bd
commit 9da5ace150

133
README.md
View File

@@ -116,13 +116,25 @@ In the **layout editor** the grid is drawn live as you edit:
... ...
``` ```
- **mouse (over SSH):** click a cell to assign a camera; **press on a tile and
drag to resize** its span, tmux-style. Click to select on the menus/lists too.
- move cursor: arrows or `h`/`j`/`k`/`l` - move cursor: arrows or `h`/`j`/`k`/`l`
- `enter` assign a camera to the cell (pick `(empty)` to clear) · `c` clear - `enter` assign a camera to the cell (pick `(empty)` to clear) · `c` clear
- `q` cycle this tile's **stream quality** (auto → high → low …); the choice is
shown in the tile. Small tiles → low substream, big tiles → high.
- resize the tile under the cursor: `L`/`H` wider/narrower, `J`/`K` taller/shorter - resize the tile under the cursor: `L`/`H` wider/narrower, `J`/`K` taller/shorter
- resize the base grid: `]`/`[` add/remove a column, `}`/`{` add/remove a row - resize the base grid: `]`/`[` add/remove a column, `}`/`{` add/remove a row
- `esc` back - `esc` back
Elsewhere: arrows / `j`/`k` move, `enter` selects, `esc` goes back, `q` quits. Elsewhere: arrows / `j`/`k` move (or click), `enter` selects, `esc` back, `q` quits.
### Stream quality per tile
Discovery records every RTSP-enabled channel per camera (`streams: {high, low, …}`),
and each tile picks which to pull. Put big tiles on `high` and small tiles on the
`low` substream — that's the main lever for keeping a dense wall smooth on a Pi 4.
Enable both channels at once with `discover --enable-rtsp=high,low` (or the TUI's
"Discover + enable RTSP (hi+lo)").
## Run ## Run
@@ -131,33 +143,43 @@ rtsp-streamer daemon # normally launched by sway, not by hand
rtsp-streamer status # slot health from the running daemon rtsp-streamer status # slot health from the running daemon
rtsp-streamer layout ls # list layouts (* marks active) rtsp-streamer layout ls # list layouts (* marks active)
rtsp-streamer layout set quad # switch live (persists the choice) rtsp-streamer layout set quad # switch live (persists the choice)
rtsp-streamer version # baked-in git commit / build date
``` ```
## Deploy to a Pi (boot straight to the wall) ## Deploy to a Pi (boot straight to the wall)
```sh First-time setup (run `install.sh` **as the user you'll run the wall as** — pass
make pi your own username; letting it default to a `kiosk` user is a common footgun,
scp bin/rtsp-streamer-arm64 pi@display:/tmp/ because the daemon's control socket lives in that user's runtime dir and must
# on the Pi, put the binary where install.sh expects it and run the installer: match your SSH user):
# mv /tmp/rtsp-streamer-arm64 <repo>/bin/rtsp-streamer
sudo ./deploy/install.sh kiosk
```
`install.sh` installs the binary, drops the kiosk sway config at
`/etc/rtsp-streamer/sway/config`, creates the `kiosk` user, enables tty1
autologin, and appends a launcher to the user's `~/.bash_profile` that starts
sway on the physical console only (SSH sessions still get a normal shell).
Reboot and the wall comes up.
Provide the controller password to the daemon by sourcing an env file from the
kiosk user's `~/.bash_profile` **before** the sway launcher, e.g.:
```sh ```sh
# ~/.bash_profile (kiosk user) # get the binary onto the Pi — build there (needs Go 1.23, see Build), or copy:
[ -f ~/.config/rtsp-streamer/env ] && . ~/.config/rtsp-streamer/env git clone <repo> ~/RTSP-Streamer && cd ~/RTSP-Streamer && make build
# >>> rtsp-streamer kiosk launcher >>> (added by install.sh) sudo ./deploy/install.sh "$USER"
``` ```
`install.sh` installs the binary to `/usr/local/bin`, drops the sway config at
`/etc/rtsp-streamer/sway/config`, adds you to the `video/render/input` groups,
enables **tty1 autologin**, and appends a launcher to `~/.bash_profile` that
starts sway on the physical console only (SSH still gets a normal shell). The
running wall needs **no controller password** — the config already holds the
resolved stream URLs; the password is only for `discover`.
Then `sudo reboot` and the wall comes up on boot.
### Updating
```sh
git pull && make install && sudo reboot
```
`make install` builds natively and copies to `/usr/local/bin` (auto-sudo — run
it *without* `sudo` so `go build` keeps your `PATH`). Use **`sudo reboot`**, not
`systemctl restart getty@tty1`: a getty restart tends to leave the old sway +
daemon running alongside the new one (duplicate mpv, windows fighting). Confirm
what's live with `rtsp-streamer version`.
## Enabling RTSP in UniFi Protect ## Enabling RTSP in UniFi Protect
Protect does not expose RTSP until you turn it on **per camera**. You can do it Protect does not expose RTSP until you turn it on **per camera**. You can do it
@@ -177,17 +199,49 @@ it just skips cameras with no RTSP-enabled channel and reports which ones.
Use `discover --low-res` (or `--enable-rtsp=low`) to prefer substreams for dense Use `discover --low-res` (or `--enable-rtsp=low`) to prefer substreams for dense
grids (3x3+), which greatly cuts Pi decode load. grids (3x3+), which greatly cuts Pi decode load.
## Performance & latency (Pi 4)
The Pi 4 hardware-decodes **H.264 only** (no HEVC). If streams lag or CPU is
pegged, work through:
- **Confirm hardware decode is on.** Probe a running player:
```sh
S=$XDG_RUNTIME_DIR/rtsp-streamer/mpv-slot-0.sock
printf '{"command":["get_property_string","hwdec-current"]}\n' | socat - "$S"
printf '{"command":["get_property_string","video-codec"]}\n' | socat - "$S"
```
`hwdec-current: no` means software decoding. `--hwdec=auto-safe` often won't
pick the Pi's decoder — set `player.hwdec: v4l2m2m-copy` explicitly.
- **H.265 cameras can't hardware-decode on a Pi 4.** If `video-codec` is
`hevc`, set that camera to H.264 in Protect, or use its (often H.264) low
substream.
- **Use substreams for dense walls.** Full-res (4K/4MP) × many tiles exceeds the
decoder's budget → fallback to software → growing latency. Give small tiles
the `low` quality (per-tile `q` in the TUI, or `quality: low` in config).
- **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.
## Troubleshooting ## Troubleshooting
- **`status` can't reach the daemon** (`control.sock: no such file`) — the wall
is running as a *different user* than your SSH session (check
`ps -o user= -p "$(pgrep -x sway)"` vs `id`). Re-run `install.sh <your-user>`
and reboot so autologin/sway/daemon all run as you.
- **Duplicate windows / one stream floating loose** — two sway sessions are
running (from a getty restart). `sudo reboot` for one clean session.
- **Blank/black tile** — verify the stream directly: - **Blank/black tile** — verify the stream directly:
`mpv --rtsp-transport=tcp 'rtsps://HOST:7441/ALIAS?enableSrtp'`. If that `mpv --rtsp-transport=tcp 'rtsps://HOST:7441/ALIAS?enableSrtp'`. If it fails,
fails, RTSP probably isn't enabled for that camera (see above). RTSP isn't enabled for that camera (`discover --enable-rtsp=high,low`).
- **`swaymsg not found` / windows not placed** — the daemon must run inside a - **A tile's video is cut off / overflows** — should not happen (mpv is told
sway session (started by the kiosk launcher). Check `echo $SWAYSOCK`. `--keepaspect-window=no` and the daemon re-squares tiles every second); if it
- **High CPU / dropped frames on a 3x3** — switch those cameras to substreams does, confirm `rtsp-streamer version` is a recent build.
(`discover --low-res`) and/or add `player.extra_args: ["--vf=fps=15"]`. - **`login failed` / `403 settings:edit` on enable** — use a *local* Protect
- **`login failed`** — use a *local* Protect account (not a Ubiquiti SSO account; enabling RTSP needs an **admin** account (view-only 403s).
login) and confirm `controller.host` reaches the UniFi OS console. - **`swaymsg not found` / windows not placed** — the daemon must run inside the
sway session; check `echo $SWAYSOCK`.
- **Build error `package log/slog is not in GOROOT`** — you're on Debian's Go
1.19; install Go 1.23 into `~/.local/go` and prepend it to `PATH` (see Build).
## Caveats & scope ## Caveats & scope
@@ -198,19 +252,28 @@ grids (3x3+), which greatly cuts Pi decode load.
controller. controller.
- **Linux/Wayland only.** "Other platforms" means other Linux devices (x86 mini - **Linux/Wayland only.** "Other platforms" means other Linux devices (x86 mini
PCs, other SBCs) — anywhere sway + mpv run. It is not a macOS/Windows app. PCs, other SBCs) — anywhere sway + mpv run. It is not a macOS/Windows app.
- **First cut** ships preset grids. Auto-cycling/rotation and free-form
positioning are natural next steps (the daemon already re-tiles on demand). ## Roadmap
- **Import views from the UniFi Protect UI** — read Protect's pre-made
multi-camera views/layouts and copy them into rtsp-streamer layouts, so you
don't rebuild an arrangement you already made in Protect.
- 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.
## Layout ## Layout
``` ```
cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, status, tui, config cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, status, tui, config, version
internal/config/ YAML load/save/validate, schema, XDG paths internal/config/ YAML load/save/validate, schema (tiles, streams), XDG paths
internal/protect/ UniFi Protect client (login, bootstrap, RTSPS URLs) internal/protect/ UniFi Protect client (login, bootstrap, enable RTSP, URLs)
internal/player/ one supervised mpv per stream, JSON IPC, restart/backoff internal/player/ one supervised mpv per stream, JSON IPC, restart/backoff
internal/compositor/ sway control + NxM grid geometry internal/compositor/ sway control + tile geometry
internal/daemon/ orchestrator + control socket + health loop internal/daemon/ orchestrator + control socket + health loop
internal/ipc/ control-socket protocol shared by daemon and CLI/TUI internal/ipc/ control-socket protocol shared by daemon and CLI/TUI
internal/tui/ Bubble Tea configurator internal/tui/ Bubble Tea configurator (grid editor, mouse, quality)
deploy/ sway kiosk config, autologin, install.sh deploy/ sway kiosk config, autologin, install.sh
``` ```