feat(quality): resolution/quality presets + Auto from pre-flight

Add a host-global quality knob (Discord-style) so the sharer can trade
resolution + bitrate for upload bandwidth. Quality is host-global by
design: one encode pipeline fans out to every viewer, so per-viewer
quality is out of scope (it would kill the broadcast fanout).

- New `--quality source|high|medium|low|auto` (ValueEnum) bundling a
  (max-height, bitrate, fps) tuple per preset; `auto` derives the preset
  from the saved bandwidth pre-flight (safe_mbps / viewer cap), falling
  back to `medium` when unmeasured. Default is auto; the interactive
  Host branch shows a picker when --quality is omitted (mirrors pick_app).
- `--max-height N` raw override; `--bitrate`/`--framerate` changed to
  Option so an explicit flag overrides just that field of the preset
  (precedence rule), leaving the rest of the preset intact.
- host/quality.rs: Preset table + resolve(); pure resolve_auto() split
  from the config read for testability. 5 unit tests lock preset
  pass-through, the Auto ladder, the unmeasured fallback, and override
  precedence.
- pipeline::build_args inserts `videoscale ! video/x-raw,height=N,
  pixel-aspect-ratio=1/1,width=[2,8192,2]` only for non-Source presets.
  PAR 1/1 forces a proportional downscale (without it videoscale keeps
  full width and squashes PAR — no bandwidth win); the even-stepped width
  range + even-rounded height satisfy H.264 4:2:0. EffectiveQuality is
  threaded capture -> wayland/x11 -> pipeline; max_viewers is now sized
  against the effective (post-preset) bitrate.
- Banner gains a quality line (preset label + ≤Np/kbps/fps + provenance).
- deps.rs checks `videoscale`; smoke-pipeline.sh adds a 1080->480
  downscale check asserting an even width below source.
- README: --quality preset table, Auto behavior, host-global note,
  --max-height/--bitrate/--framerate override precedence.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 15:03:14 -04:00
parent 45e5d7ef37
commit 7483b9aae8
11 changed files with 503 additions and 33 deletions
+34 -1
View File
@@ -31,6 +31,9 @@ Working:
- First-run upstream bandwidth pre-flight, persisted to
`~/.config/pixelpass/config.toml` and used to auto-size the default
viewer cap
- Quality presets (`--quality source|high|medium|low|auto`) that trade
resolution + bitrate for upload bandwidth, plus an `Auto` mode that
derives quality from the bandwidth pre-flight
Not yet built (deferred, not blocking):
- Per-monitor selection on a multi-monitor X11 host — `ximagesrc` grabs the
@@ -242,7 +245,37 @@ 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.
12 Mbps of upstream. The `--quality` presets below are the friendlier
way to do the same thing.
## Quality
`--quality <preset>` bundles a max video height, bitrate, and framerate —
resolution is a quality-per-bitrate knob, so the three only make sense
together. Quality is **host-global**: one encode pipeline fans out to every
viewer, so the sharer picks one quality for everyone (per-viewer quality
would need per-viewer encodes, which kills the fanout).
| Preset | Max height | Bitrate | fps |
|----------|-------------------|-----------|-----|
| `source` | native (no scale) | 6000 kbps | 30 |
| `high` | 1080p | 4000 kbps | 30 |
| `medium` | 720p | 2500 kbps | 30 |
| `low` | 480p | 1000 kbps | 30 |
| `auto` | derived (below) | derived | 30 |
`auto` (the default) picks the highest preset whose bitrate fits your
measured safe upstream divided by the viewer cap — so quality is sized for
the worst case, since it's baked in when capture starts and can't drop when
a second viewer joins. With no `--max-viewers`, it sizes for a single
viewer. If there's no bandwidth measurement yet, `auto` falls back to
`medium` (run `pixelpass --reconfigure` to measure). In the interactive
menu, omitting `--quality` shows a picker instead of assuming `auto`.
Downscaling preserves the source aspect ratio with square pixels and snaps
to even dimensions (H.264 requires them). Power users can override
individual fields: `--max-height N`, `--bitrate N`, and `--framerate N`
each take precedence over the chosen preset's value for that field.
## Known limitations and gotchas