Merge kiosk-hdmi-audio-fix: PipeWire env, in-process-gpu, asound.conf tracking

Conflict resolution: keep the branch's CPUQuota=200% (75% was the proven
cause of choppy HDMI audio — the file's own comment says so; the 75% on main
was an accidental regression bundled into an unrelated UI commit) and keep
main's newer MemoryMax=2800M/MemoryHigh=2200M tuning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-07-10 20:19:47 +01:00
commit 3878644fe8
5 changed files with 75 additions and 8 deletions

View File

@ -83,25 +83,33 @@ xset s noblank 2>/dev/null || true
pkill -u archipelago -f 'chromium.*localhost' 2>/dev/null || true pkill -u archipelago -f 'chromium.*localhost' 2>/dev/null || true
sleep 1 sleep 1
# GPU vs headless (#36). On a real kiosk display with a GPU, GPU rasterization is # GPU vs headless (#36, choppy-audio incident 2026-06-28). --enable-gpu-rasterization
# fast. On a GPU-less / headless server (no /dev/dri), --enable-gpu-rasterization # spins a dedicated GPU process at 55-92% CPU even on real GPU hardware (Intel HD 5500)
# forces GPU paths that fall back to software compositing and SPIN a full core at # because under X11 it falls back to software compositing anyway — that CPU
# ~92% CPU, saturating the node. Detect the GPU and pick safe flags accordingly. # starvation is what caused choppy HDMI audio. --in-process-gpu avoids the
# separate process; GpuRasterization is also disabled via --disable-features below.
# On a GPU-less / headless server (no /dev/dri), disable GPU entirely instead.
if [ -e /dev/dri/card0 ] || [ -e /dev/dri/renderD128 ]; then if [ -e /dev/dri/card0 ] || [ -e /dev/dri/renderD128 ]; then
GPU_FLAGS="--enable-gpu-rasterization --num-raster-threads=2" GPU_FLAGS="--in-process-gpu --num-raster-threads=1"
else else
GPU_FLAGS="--disable-gpu --num-raster-threads=1" GPU_FLAGS="--disable-gpu --num-raster-threads=1"
fi fi
ARCHIPELAGO_UID=$(id -u archipelago)
while true; do while true; do
sudo -u archipelago env DISPLAY=:0 HOME=/home/archipelago chromium --kiosk \ # XDG_RUNTIME_DIR must be passed explicitly — without it Chromium's audio
# backend can't find PipeWire-Pulse's socket at /run/user/<uid>/pulse/native,
# falls back to raw ALSA "default", fails to connect, and produces no audio
# at all with no visible error (--noerrdialogs suppresses it).
sudo -u archipelago env DISPLAY=:0 HOME=/home/archipelago XDG_RUNTIME_DIR=/run/user/$ARCHIPELAGO_UID chromium --kiosk \
--app=http://localhost/kiosk?safe_area_x=${KIOSK_SAFE_AREA_X_PX:-0}\&safe_area_y=${KIOSK_SAFE_AREA_Y_PX:-0} \ --app=http://localhost/kiosk?safe_area_x=${KIOSK_SAFE_AREA_X_PX:-0}\&safe_area_y=${KIOSK_SAFE_AREA_Y_PX:-0} \
--noerrdialogs \ --noerrdialogs \
--disable-infobars \ --disable-infobars \
--disable-translate \ --disable-translate \
--no-first-run \ --no-first-run \
--check-for-update-interval=31536000 \ --check-for-update-interval=31536000 \
--disable-features=TranslateUI,MetricsReporting,AutofillServerCommunication,PasswordManagerEnabled \ --disable-features=TranslateUI,MetricsReporting,AutofillServerCommunication,PasswordManagerEnabled,GpuRasterization \
--disable-session-crashed-bubble \ --disable-session-crashed-bubble \
--disable-save-password-bubble \ --disable-save-password-bubble \
--disable-suggestions-service \ --disable-suggestions-service \

View File

@ -25,8 +25,11 @@ RestartSec=5
# backend (it caused the .198 receive timeout + deploy storms). Cap CPU + memory # backend (it caused the .198 receive timeout + deploy storms). Cap CPU + memory
# so a runaway kiosk can never take the whole machine down; Delegate so the cap # so a runaway kiosk can never take the whole machine down; Delegate so the cap
# also binds the chromium/Xorg children in this unit's cgroup. # also binds the chromium/Xorg children in this unit's cgroup.
# CPUQuota=75% (0.75 cores) was too tight even for normal playback — the kiosk
# was throttled ~40% of the time, which is what caused choppy HDMI audio on
# archy-x250-exp (2026-06-28 incident). 200% (2 cores) gives enough headroom.
Delegate=yes Delegate=yes
CPUQuota=75% CPUQuota=200%
# Raised from 1500M/1200M: a Framework (Tiger Lake) kiosk sat at 806M used / # Raised from 1500M/1200M: a Framework (Tiger Lake) kiosk sat at 806M used /
# 1.1G peak, riding the old MemoryHigh reclaim-throttle line — the throttling # 1.1G peak, riding the old MemoryHigh reclaim-throttle line — the throttling
# itself was the perceived UI lag. Keep Max well above real peaks; High stays # itself was the perceived UI lag. Keep Max well above real peaks; High stays

View File

@ -0,0 +1,7 @@
pcm.!default {
type pulse
hint.description "Default ALSA Device (via PulseAudio)"
}
ctl.!default {
type pulse
}

View File

@ -425,6 +425,22 @@ deploy_node() {
' 2>/dev/null || true ' 2>/dev/null || true
fi fi
# ── Deploy ALSA default-device config ────────────────────────────
ASOUND_CONF="$PROJECT_DIR/image-recipe/configs/asound.conf"
if [ -f "$ASOUND_CONF" ]; then
step "Deploying ALSA default-device config"
scp $SSH_OPTS "$ASOUND_CONF" "$TARGET:/tmp/asound.conf" 2>/dev/null || true
ssh $SSH_OPTS "$TARGET" '
if ! diff -q /tmp/asound.conf /etc/asound.conf >/dev/null 2>&1; then
sudo cp /tmp/asound.conf /etc/asound.conf
echo " Installed"
else
echo " Unchanged"
fi
rm -f /tmp/asound.conf
' 2>/dev/null || true
fi
# ── Step 18: NTP + swap ────────────────────────────────────────── # ── Step 18: NTP + swap ──────────────────────────────────────────
step "Ensuring NTP + swap" step "Ensuring NTP + swap"
ssh $SSH_OPTS "$TARGET" ' ssh $SSH_OPTS "$TARGET" '

View File

@ -463,6 +463,22 @@ deploy_secondary() {
' 2>/dev/null || true ' 2>/dev/null || true
fi fi
# Deploy ALSA default-device config (routes ALSA "default" through PulseAudio/PipeWire)
ASOUND_CONF="$PROJECT_DIR/image-recipe/configs/asound.conf"
if [ -f "$ASOUND_CONF" ]; then
echo " Syncing ALSA default-device config to .$SEC_LABEL..."
scp $SSH_OPTS "$ASOUND_CONF" "$SEC_TARGET:/tmp/asound.conf" 2>/dev/null || true
ssh $SSH_OPTS "$SEC_TARGET" '
if ! diff -q /tmp/asound.conf /etc/asound.conf >/dev/null 2>&1; then
sudo cp /tmp/asound.conf /etc/asound.conf
echo " ALSA default-device config installed"
else
echo " ALSA default-device config unchanged"
fi
rm -f /tmp/asound.conf
' 2>/dev/null || true
fi
# Dev mode + FileBrowser # Dev mode + FileBrowser
ssh $SSH_OPTS "$SEC_TARGET" ' ssh $SSH_OPTS "$SEC_TARGET" '
# Dev mode # Dev mode
@ -805,6 +821,23 @@ if [ "$LIVE" = true ]; then
' 2>/dev/null || true ' 2>/dev/null || true
fi fi
# Deploy ALSA default-device config (routes ALSA "default" through
# PulseAudio/PipeWire — without it Chromium's raw ALSA fallback can't
# reach the HDMI sink and kiosk HDMI audio is silent).
ASOUND_CONF="$PROJECT_DIR/image-recipe/configs/asound.conf"
if [ -f "$ASOUND_CONF" ]; then
scp $SSH_OPTS "$ASOUND_CONF" "$TARGET_HOST:/tmp/asound.conf" 2>/dev/null || true
ssh $SSH_OPTS "$TARGET_HOST" '
if ! diff -q /tmp/asound.conf /etc/asound.conf >/dev/null 2>&1; then
sudo cp /tmp/asound.conf /etc/asound.conf
echo " ALSA default-device config installed"
else
echo " ALSA default-device config unchanged"
fi
rm -f /tmp/asound.conf
' 2>/dev/null || true
fi
# Deploy Claude API proxy (auto-install if missing) # Deploy Claude API proxy (auto-install if missing)
progress "Setting up Claude API proxy" progress "Setting up Claude API proxy"
ssh $SSH_OPTS "$TARGET_HOST" ' ssh $SSH_OPTS "$TARGET_HOST" '