#!/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 step host/pipeline.rs inserts for a non-Source preset: # AFTER videoconvert (scaling system-memory NV12, not the raw source format) and # pinned to an exact even WxH computed from the source size. A 16:9 source @ # 480p wants width 853.3 -> 852 even. Asserts the negotiated size matches and # the encoder accepts it. Guards both the "even-width caveat" and the # negotiation/placement regression that squashed the picture. SCALED="${TMPDIR:-/tmp}/pixelpass-smoke-scaled-$$.ts" trap 'rm -f "$OUT" "$SCALED"' EXIT echo "[smoke] downscale check: 1920x1080 -> 852x480 (videoscale after videoconvert)" 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 \ ! queue ! videoconvert ! video/x-raw,format=NV12 \ ! videoscale ! video/x-raw,format=NV12,width=852,height=480 \ ! 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