7483b9aae8
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>
89 lines
4.3 KiB
Bash
Executable File
89 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Pipeline-only smoke test: mirrors host/wayland.rs's gst pipeline but
|
|
# substitutes videotestsrc/audiotestsrc for pipewiresrc/pulsesrc and writes
|
|
# to a file instead of fdsink, so it runs without the portal/PipeWire and
|
|
# without an HTTP/iroh client. Catches AV1 encode + mpegtsmux + AAC mux
|
|
# regressions in ~5 seconds without any user interaction.
|
|
#
|
|
# Usage: scripts/smoke-pipeline.sh
|
|
# Exits 0 on pass, non-zero with diagnostic on fail.
|
|
|
|
set -euo pipefail
|
|
|
|
OUT="${TMPDIR:-/tmp}/pixelpass-smoke-$$.ts"
|
|
trap 'rm -f "$OUT"' EXIT
|
|
|
|
echo "[smoke] running gst pipeline (videotestsrc + audiotestsrc, ~2s) -> $OUT"
|
|
gst-launch-1.0 -q \
|
|
mpegtsmux name=mux \
|
|
! queue ! filesink location="$OUT" \
|
|
videotestsrc num-buffers=60 is-live=false \
|
|
! video/x-raw,width=640,height=360,framerate=30/1 \
|
|
! videoconvert ! video/x-raw,format=NV12 \
|
|
! vah264enc rate-control=cbr bitrate=2000 key-int-max=60 \
|
|
! h264parse config-interval=-1 \
|
|
! video/x-h264,stream-format=byte-stream,alignment=au \
|
|
! mux. \
|
|
audiotestsrc num-buffers=94 is-live=false \
|
|
! audioconvert ! audioresample \
|
|
! audio/x-raw,rate=48000,channels=2 \
|
|
! avenc_aac bitrate=128000 ! aacparse ! mux.
|
|
|
|
[[ -s "$OUT" ]] || { echo "[smoke] FAIL: output file empty or missing"; exit 1; }
|
|
echo "[smoke] output size: $(stat -c %s "$OUT") bytes"
|
|
|
|
echo "[smoke] ffprobe stream listing"
|
|
ffprobe -v error -show_entries stream=index,codec_type,codec_name "$OUT" \
|
|
| sed 's/^/ /'
|
|
|
|
# Real gate: mpv must actually decode video. The previous --frames=10 check was
|
|
# a false positive — if there's no video stream, --frames silently does nothing
|
|
# and mpv exits 0 after playing the audio. We instead grep mpv -v output for
|
|
# "video=playing"; "video=eof" means mpv saw no video track.
|
|
echo "[smoke] mpv video-decode probe (-v, asserting 'video=playing')"
|
|
MPV_LOG=$(mpv --no-config -v --vo=null --ao=null --frames=10 "$OUT" 2>&1)
|
|
echo "$MPV_LOG" | grep -E 'video=(playing|eof)' | sed 's/^/ /'
|
|
if echo "$MPV_LOG" | grep -q 'video=playing'; then
|
|
echo "[smoke] PASS — pipeline produces an mpv-decodable video + audio stream"
|
|
else
|
|
echo "[smoke] FAIL: mpv did not decode video (saw video=eof or no video state)"
|
|
echo "[smoke] last 30 lines of mpv output:"
|
|
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
|