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:
133
README.md
133
README.md
@@ -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`
|
||||
- `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 base grid: `]`/`[` add/remove a column, `}`/`{` add/remove a row
|
||||
- `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
|
||||
|
||||
@@ -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 layout ls # list layouts (* marks active)
|
||||
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)
|
||||
|
||||
```sh
|
||||
make pi
|
||||
scp bin/rtsp-streamer-arm64 pi@display:/tmp/
|
||||
# on the Pi, put the binary where install.sh expects it and run the installer:
|
||||
# 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.:
|
||||
First-time setup (run `install.sh` **as the user you'll run the wall as** — pass
|
||||
your own username; letting it default to a `kiosk` user is a common footgun,
|
||||
because the daemon's control socket lives in that user's runtime dir and must
|
||||
match your SSH user):
|
||||
|
||||
```sh
|
||||
# ~/.bash_profile (kiosk user)
|
||||
[ -f ~/.config/rtsp-streamer/env ] && . ~/.config/rtsp-streamer/env
|
||||
# >>> rtsp-streamer kiosk launcher >>> (added by install.sh)
|
||||
# get the binary onto the Pi — build there (needs Go 1.23, see Build), or copy:
|
||||
git clone <repo> ~/RTSP-Streamer && cd ~/RTSP-Streamer && make build
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
- **`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:
|
||||
`mpv --rtsp-transport=tcp 'rtsps://HOST:7441/ALIAS?enableSrtp'`. If that
|
||||
fails, RTSP probably isn't enabled for that camera (see above).
|
||||
- **`swaymsg not found` / windows not placed** — the daemon must run inside a
|
||||
sway session (started by the kiosk launcher). Check `echo $SWAYSOCK`.
|
||||
- **High CPU / dropped frames on a 3x3** — switch those cameras to substreams
|
||||
(`discover --low-res`) and/or add `player.extra_args: ["--vf=fps=15"]`.
|
||||
- **`login failed`** — use a *local* Protect account (not a Ubiquiti SSO
|
||||
login) and confirm `controller.host` reaches the UniFi OS console.
|
||||
`mpv --rtsp-transport=tcp 'rtsps://HOST:7441/ALIAS?enableSrtp'`. If it fails,
|
||||
RTSP isn't enabled for that camera (`discover --enable-rtsp=high,low`).
|
||||
- **A tile's video is cut off / overflows** — should not happen (mpv is told
|
||||
`--keepaspect-window=no` and the daemon re-squares tiles every second); if it
|
||||
does, confirm `rtsp-streamer version` is a recent build.
|
||||
- **`login failed` / `403 settings:edit` on enable** — use a *local* Protect
|
||||
account; enabling RTSP needs an **admin** account (view-only 403s).
|
||||
- **`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
|
||||
|
||||
@@ -198,19 +252,28 @@ grids (3x3+), which greatly cuts Pi decode load.
|
||||
controller.
|
||||
- **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.
|
||||
- **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
|
||||
|
||||
```
|
||||
cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, status, tui, config
|
||||
internal/config/ YAML load/save/validate, schema, XDG paths
|
||||
internal/protect/ UniFi Protect client (login, bootstrap, RTSPS URLs)
|
||||
cmd/rtsp-streamer/ CLI (cobra): daemon, discover, layout, 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
|
||||
internal/compositor/ sway control + NxM grid geometry
|
||||
internal/compositor/ sway control + tile geometry
|
||||
internal/daemon/ orchestrator + control socket + health loop
|
||||
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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user