README: document multi-viewer + bandwidth pre-flight

Updates the status section to move multi-viewer out of "not yet
working", adds a Configuration section pointing at the new TOML config
at ~/.config/pixelpass/config.toml, and a Multi-viewer section
covering the lazy-sticky lifecycle, the --max-viewers cap, the
bandwidth-bitrate tradeoff, and how to fit more viewers by dropping
--bitrate. Known-limitations section gains "late joiners see ~2 s of
garbage" (expected behavior) and drops the now-stale "single viewer
per host" line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 17:00:58 -04:00
parent 153febe078
commit f939441e31
+51 -5
View File
@@ -22,11 +22,15 @@ Working:
- iroh QUIC bi-stream tunnel, direct-UDP and relay paths both verified - iroh QUIC bi-stream tunnel, direct-UDP and relay paths both verified
- Interactive Host/View menu with clipboard auto-copy and mpv/VLC picker - Interactive Host/View menu with clipboard auto-copy and mpv/VLC picker
- Headless mode for scripts (`pixelpass <ticket>`) - Headless mode for scripts (`pixelpass <ticket>`)
- Multi-viewer fanout (default 2, configurable via `--max-viewers`;
shared gst pipeline, one broadcast channel per host)
- First-run upstream bandwidth pre-flight, persisted to
`~/.config/pixelpass/config.toml` and used to auto-size the default
viewer cap
Not yet working: Not yet working:
- X11 capture (stubbed, returns an error) - X11 capture (stubbed, returns an error)
- Per-app audio routing (`--app <name>` is a flag stub) - Per-app audio routing (`--app <name>` is a flag stub)
- Multi-viewer (single viewer per host by design right now)
- `--repair` (PipeWire orphan cleanup) is a stub - `--repair` (PipeWire orphan cleanup) is a stub
## Quick start ## Quick start
@@ -39,7 +43,14 @@ pixelpass
On the host machine: pick "Host", share a monitor via the portal dialog, On the host machine: pick "Host", share a monitor via the portal dialog,
the ticket lands on your clipboard. Send it to your viewer however you the ticket lands on your clipboard. Send it to your viewer however you
like (chat, email, paste in a note). like (chat, email, paste in a note). The same ticket works for multiple
viewers up to your `--max-viewers` cap.
The very first host launch offers a one-time upstream bandwidth test
(~5 s, ~5 MB to Cloudflare's open speed-test endpoint) so it can pick
a sensible default for the viewer cap. You can skip it and a
conservative default (2 viewers) is used; re-run it later with
`pixelpass --reconfigure`.
On the viewer machine: run `pixelpass`, pick "View", paste the ticket, On the viewer machine: run `pixelpass`, pick "View", paste the ticket,
pick mpv or VLC. The player launches detached and the stream starts. pick mpv or VLC. The player launches detached and the stream starts.
@@ -154,6 +165,42 @@ relay path otherwise. Both have been verified end-to-end.
- **VAAPI H.264 over x264**: ~5% of one CPU core instead of ~50% on - **VAAPI H.264 over x264**: ~5% of one CPU core instead of ~50% on
the host's hardware. the host's hardware.
## Configuration
`pixelpass` keeps a small TOML config at `~/.config/pixelpass/config.toml`
(or the XDG equivalent). Right now it only stores the result of the
bandwidth pre-flight:
```toml
[bandwidth]
status = "measured" # measured | skipped | failed | unmeasured
upstream_mbps = 8.78 # safe estimate (raw * 0.8)
measured_at = "2026-05-21T20:41:16Z"
```
- `pixelpass --reconfigure` re-runs the test (e.g. after an ISP change).
- Deleting the file resets pixelpass to first-run state.
- Skip is sticky — once you skip the test, pixelpass won't ask again
unless you reconfigure.
## Multi-viewer
One gst capture pipeline fans out to N concurrent viewers via a
`tokio::sync::broadcast` channel. The same ticket is reusable: as long
as a viewer is connected, capture stays alive; when the last one
leaves, the pipeline tears down and the portal stops streaming. A new
viewer connecting after that re-triggers the portal dialog.
Capacity is bounded by upstream bandwidth (each viewer is its own
encrypted egress). The default cap comes from the bandwidth pre-flight
result; `--max-viewers <N>` overrides it. When the cap is hit,
additional connections are politely refused with a "host is full"
message and the host keeps running.
For more viewers, drop the per-viewer bitrate: e.g. `pixelpass
--bitrate 2500 --max-viewers 4` fits four 2.5 Mbps streams in roughly
12 Mbps of upstream.
## Known limitations and gotchas ## Known limitations and gotchas
- **VLC needs `vlc-plugin-dvb` and `vlc-plugin-ffmpeg`** on Arch-family - **VLC needs `vlc-plugin-dvb` and `vlc-plugin-ffmpeg`** on Arch-family
@@ -164,9 +211,8 @@ relay path otherwise. Both have been verified end-to-end.
- **Audio echo** if the host plays the stream through speakers and - **Audio echo** if the host plays the stream through speakers and
captures system audio — expected, the mic / monitor picks up the captures system audio — expected, the mic / monitor picks up the
playback. Headphones bypass it. playback. Headphones bypass it.
- **Single viewer per host** by design right now. Restarting the player - **Late joiners see ~2 s of garbage** before the next keyframe lets
against the same URL fails with "connection refused"; restart the their decoder lock. Expected behavior, not a bug.
viewer too.
- **VAAPI driver must be package-tracked**, not an orphaned `.so` on - **VAAPI driver must be package-tracked**, not an orphaned `.so` on
disk. mpv's `--hwdec=auto` silently falls back to software decode disk. mpv's `--hwdec=auto` silently falls back to software decode
otherwise, which then chokes on a low-power viewer. otherwise, which then chokes on a low-power viewer.