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
+35
View File
@@ -51,3 +51,38 @@ else
echo "$MPV_LOG" | tail -30 | sed 's/^/ /'
exit 1
fi
# ── quality-preset downscale check ────────────────────────────────────────
# Mirrors the videoscale element host/pipeline.rs inserts for a non-Source
# preset. A 16:9 source @ 480p wants width 853.3 — the danger case for H.264,
# which needs even dimensions. Asserts videoscale negotiates an even width
# (the pixel-aspect-ratio=1/1 + stepped-even-range caps) and the encoder
# accepts it. Guards the "even-width caveat" the design plan flagged.
SCALED="${TMPDIR:-/tmp}/pixelpass-smoke-scaled-$$.ts"
trap 'rm -f "$OUT" "$SCALED"' EXIT
echo "[smoke] downscale check: 1920x1080 -> height 480 (videoscale, even-width)"
gst-launch-1.0 -q \
mpegtsmux name=mux ! queue ! filesink location="$SCALED" \
videotestsrc num-buffers=30 is-live=false \
! video/x-raw,width=1920,height=1080,framerate=30/1 \
! videorate ! video/x-raw,framerate=30/1 \
! videoscale ! "video/x-raw,height=480,pixel-aspect-ratio=1/1,width=[2,8192,2]" \
! videoconvert ! video/x-raw,format=NV12 \
! vah264enc rate-control=cbr bitrate=1000 key-int-max=60 \
! h264parse config-interval=-1 \
! video/x-h264,stream-format=byte-stream,alignment=au ! mux. \
audiotestsrc num-buffers=47 is-live=false \
! audioconvert ! audioresample ! audio/x-raw,rate=48000,channels=2 \
! avenc_aac bitrate=128000 ! aacparse ! mux.
SCALED_DIMS=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height \
-of csv=p=0 "$SCALED" | head -1)
SCALED_W=${SCALED_DIMS%,*}
SCALED_H=${SCALED_DIMS#*,}
echo " negotiated ${SCALED_W}x${SCALED_H}"
if [[ "$SCALED_H" == "480" && $((SCALED_W % 2)) -eq 0 && "$SCALED_W" -lt 1920 ]]; then
echo "[smoke] PASS — downscale produced an even width below source (proportional)"
else
echo "[smoke] FAIL: expected even width < 1920 at height 480, got ${SCALED_W}x${SCALED_H}"
exit 1
fi