config reshape

This commit is contained in:
legop3
2026-06-30 20:17:43 -04:00
parent 2eb6c00f9c
commit 31eacea7bc
21 changed files with 520 additions and 200 deletions
+60 -10
View File
@@ -2,20 +2,70 @@
set -euo pipefail
set +H
ENV_FILE="${VIDEO_ENV_FILE:-/var/lib/roverd/video.env}"
ENV_FILE="${ROVERD_MEDIA_ENV_FILE:-/var/lib/roverd/media.env}"
if [[ ! -f "$ENV_FILE" ]]; then
echo "Environment file ${ENV_FILE} missing; cannot start audio forward listener" >&2
exit 1
fi
# shellcheck disable=SC1090
source "$ENV_FILE"
# Load KEY=VALUE pairs from media.env without evaluating shell syntax. The
# forward URL is data produced by roverd, and treating it as shell code would
# break on normal SRT query-string characters such as '&'.
load_env_file() {
local content=""
: "${AUDIO_FORWARD_URL:?AUDIO_FORWARD_URL not set in ${ENV_FILE}}"
PLAYBACK_DEVICE="${AUDIO_PLAYBACK_DEVICE:-forward}"
AUDIO_NORMALIZE_ENABLE="${AUDIO_NORMALIZE_ENABLE:-1}"
AUDIO_NORMALIZE_FILTER="${AUDIO_NORMALIZE_FILTER:-dynaudnorm=f=75:g=15:m=10:p=0.9,alimiter=limit=0.85:level=disabled}"
if [[ -r "$ENV_FILE" ]]; then
content="$(cat "$ENV_FILE")"
elif command -v sudo >/dev/null 2>&1; then
content="$(sudo -n cat "$ENV_FILE" 2>/dev/null || true)"
fi
if [[ -z "$content" ]]; then
echo "Cannot read ${ENV_FILE} (permission denied). Run as a user that can read it, or allow sudo -n for cat." >&2
exit 1
fi
local line key val
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
if [[ "$line" =~ ^[[:space:]]*export[[:space:]]+([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
elif [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
else
continue
fi
val="${val#${val%%[![:space:]]*}}"
val="${val%${val##*[![:space:]]}}"
if [[ "$val" =~ ^\".*\"$ ]]; then
val="${val:1:${#val}-2}"
elif [[ "$val" =~ ^\'.*\'$ ]]; then
val="${val:1:${#val}-2}"
fi
printf -v "$key" '%s' "$val"
export "$key"
done <<< "$content"
}
load_env_file
: "${ROVERD_AUDIO_PLAYBACK_ENABLE:?ROVERD_AUDIO_PLAYBACK_ENABLE not set in ${ENV_FILE}}"
if [[ "${ROVERD_AUDIO_PLAYBACK_ENABLE}" -ne 1 ]]; then
echo "Audio playback disabled by roverd media config; skipping audio forward listener" >&2
exit 0
fi
: "${ROVERD_AUDIO_PLAYBACK_FORWARD_URL:?ROVERD_AUDIO_PLAYBACK_FORWARD_URL not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_PLAYBACK_DEVICE:?ROVERD_AUDIO_PLAYBACK_DEVICE not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_PLAYBACK_NORMALIZE:?ROVERD_AUDIO_PLAYBACK_NORMALIZE not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER:?ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER not set in ${ENV_FILE}}"
PLAYBACK_DEVICE="${ROVERD_AUDIO_PLAYBACK_DEVICE}"
if [[ -n "${FFMPEG_BIN:-}" ]]; then
FFMPEG_BIN_PATH="$FFMPEG_BIN"
@@ -45,12 +95,12 @@ run_pipeline() {
-flags low_delay
-analyzeduration 200k
-probesize 32k
-i "${AUDIO_FORWARD_URL}"
-i "${ROVERD_AUDIO_PLAYBACK_FORWARD_URL}"
-vn
)
if [[ "${AUDIO_NORMALIZE_ENABLE}" -ne 0 ]]; then
ffmpeg_args+=(-af "${AUDIO_NORMALIZE_FILTER}")
if [[ "${ROVERD_AUDIO_PLAYBACK_NORMALIZE}" -ne 0 ]]; then
ffmpeg_args+=(-af "${ROVERD_AUDIO_PLAYBACK_NORMALIZE_FILTER}")
fi
ffmpeg_args+=(
+68 -20
View File
@@ -2,26 +2,74 @@
set -euo pipefail
set +H
ENV_FILE="${VIDEO_ENV_FILE:-/var/lib/roverd/video.env}"
ENV_FILE="${ROVERD_MEDIA_ENV_FILE:-/var/lib/roverd/media.env}"
if [[ ! -f "$ENV_FILE" ]]; then
echo "Environment file ${ENV_FILE} missing; cannot publish audio" >&2
exit 1
fi
# shellcheck disable=SC1090
source "$ENV_FILE"
AUDIO_ENABLE="${AUDIO_ENABLE:-0}"
if [[ "${AUDIO_ENABLE}" -ne 1 ]]; then
# Load KEY=VALUE pairs from media.env without evaluating shell syntax. SRT URLs
# contain characters such as '&' and '#!', so sourcing this file would treat a
# data file as code and can split a valid URL into shell control operators.
load_env_file() {
local content=""
if [[ -r "$ENV_FILE" ]]; then
content="$(cat "$ENV_FILE")"
elif command -v sudo >/dev/null 2>&1; then
content="$(sudo -n cat "$ENV_FILE" 2>/dev/null || true)"
fi
if [[ -z "$content" ]]; then
echo "Cannot read ${ENV_FILE} (permission denied). Run as a user that can read it, or allow sudo -n for cat." >&2
exit 1
fi
local line key val
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
if [[ "$line" =~ ^[[:space:]]*export[[:space:]]+([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
elif [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
else
continue
fi
val="${val#${val%%[![:space:]]*}}"
val="${val%${val##*[![:space:]]}}"
if [[ "$val" =~ ^\".*\"$ ]]; then
val="${val:1:${#val}-2}"
elif [[ "$val" =~ ^\'.*\'$ ]]; then
val="${val:1:${#val}-2}"
fi
printf -v "$key" '%s' "$val"
export "$key"
done <<< "$content"
}
load_env_file
: "${ROVERD_AUDIO_CAPTURE_ENABLE:?ROVERD_AUDIO_CAPTURE_ENABLE not set in ${ENV_FILE}}"
if [[ "${ROVERD_AUDIO_CAPTURE_ENABLE}" -ne 1 ]]; then
echo "Audio capture disabled; skipping audio-only publisher" >&2
exit 0
fi
: "${AUDIO_PUBLISH_URL:?AUDIO_PUBLISH_URL not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_CAPTURE_PUBLISH_URL:?ROVERD_AUDIO_CAPTURE_PUBLISH_URL not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_CAPTURE_DEVICE:?ROVERD_AUDIO_CAPTURE_DEVICE not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_CAPTURE_SAMPLE_RATE:?ROVERD_AUDIO_CAPTURE_SAMPLE_RATE not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_CAPTURE_CHANNELS:?ROVERD_AUDIO_CAPTURE_CHANNELS not set in ${ENV_FILE}}"
: "${ROVERD_AUDIO_CAPTURE_BITRATE:?ROVERD_AUDIO_CAPTURE_BITRATE not set in ${ENV_FILE}}"
AUDIO_DEVICE="${AUDIO_DEVICE:-hw:0,0}"
CAPTURE_DEVICE="${ROVERD_AUDIO_CAPTURE_DEVICE}"
# The old 65,536-byte ALSA buffer represented about 171 ms before ffmpeg could
# even publish the microphone audio:
# At the default 48 kHz stereo S32_LE capture format, the old 65,536-byte ALSA
# buffer represented about 171 ms before ffmpeg could publish microphone audio:
# 65,536 / (48,000 samples * 2 channels * 4 bytes) = 0.1707 seconds
# Keep the defaults much smaller because this publisher feeds an interactive
# rover stream, where late-but-smooth audio is less useful than fresher audio.
@@ -51,8 +99,8 @@ run_pipeline() {
# format and so the published audio stays full-band stereo before the
# Opus encoder sees it.
-f s32le
-ar 48000
-ac 2
-ar "${ROVERD_AUDIO_CAPTURE_SAMPLE_RATE}"
-ac "${ROVERD_AUDIO_CAPTURE_CHANNELS}"
-i pipe:0
# Keep the known-required microphone boost, but remove the old
@@ -64,9 +112,9 @@ run_pipeline() {
# the highest practical quality browsers and MediaMTX can carry without
# trying to push raw PCM through the live path.
-c:a libopus
-b:a 510000
-ar:a 48000
-ac:a 2
-b:a "${ROVERD_AUDIO_CAPTURE_BITRATE}"
-ar:a "${ROVERD_AUDIO_CAPTURE_SAMPLE_RATE}"
-ac:a "${ROVERD_AUDIO_CAPTURE_CHANNELS}"
# Use the audio profile for quality. Latency is still controlled by the
# 20 ms Opus frame size and the low-buffering capture/publish options
@@ -82,13 +130,13 @@ run_pipeline() {
-muxdelay 0
-muxpreload 0
-f mpegts
"${AUDIO_PUBLISH_URL}"
"${ROVERD_AUDIO_CAPTURE_PUBLISH_URL}"
)
# The Google Voice HAT microphone path is intentionally fixed instead of
# configurable. The previous env-driven sample rate/channel knobs made it
# easy for the rover config and the actual ffmpeg pipeline to drift apart,
# while the hardware path we install is always 48 kHz stereo capture.
# The capture format comes from roverd's normalized media config. Keeping
# arecord and ffmpeg on the same env-backed values prevents the publisher
# script from quietly disagreeing with roverd.yaml about sample rate or
# channel count.
#
# The buffer and period are byte counts because arecord interprets -B/-F in
# microseconds only when the value has an explicit time suffix. Keeping them
@@ -96,7 +144,7 @@ run_pipeline() {
# defaults are roughly 43 ms total buffer and 2.7 ms wakeup periods at
# 48 kHz stereo S32_LE, which removes about 128 ms of avoidable capture
# latency compared with the old 65,536-byte buffer.
arecord -D "${AUDIO_DEVICE}" -f S32_LE -c 2 -r 48000 -B "${AUDIO_ALSA_BUFFER_BYTES}" -F "${AUDIO_ALSA_PERIOD_BYTES}" -q -t raw \
arecord -D "${CAPTURE_DEVICE}" -f S32_LE -c "${ROVERD_AUDIO_CAPTURE_CHANNELS}" -r "${ROVERD_AUDIO_CAPTURE_SAMPLE_RATE}" -B "${AUDIO_ALSA_BUFFER_BYTES}" -F "${AUDIO_ALSA_PERIOD_BYTES}" -q -t raw \
| "${FFMPEG_BIN_PATH}" "${ffmpeg_args[@]}"
}
+22 -18
View File
@@ -4,7 +4,7 @@ set -euo pipefail
# Keep history expansion off so values containing "!" are safe.
set +H
ENV_FILE="${VIDEO_ENV_FILE:-/var/lib/roverd/video.env}"
ENV_FILE="${ROVERD_MEDIA_ENV_FILE:-/var/lib/roverd/media.env}"
if [[ ! -f "$ENV_FILE" ]]; then
echo "Environment file ${ENV_FILE} missing; cannot publish" >&2
@@ -64,25 +64,29 @@ load_env_file() {
}
load_env_file
: "${PUBLISH_URL:?PUBLISH_URL not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_ENABLE:?ROVERD_VIDEO_ENABLE not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_PUBLISH_URL:?ROVERD_VIDEO_PUBLISH_URL not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_WIDTH:?ROVERD_VIDEO_WIDTH not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_HEIGHT:?ROVERD_VIDEO_HEIGHT not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_FPS:?ROVERD_VIDEO_FPS not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_BITRATE:?ROVERD_VIDEO_BITRATE not set in ${ENV_FILE}}"
: "${ROVERD_VIDEO_INVERT:?ROVERD_VIDEO_INVERT not set in ${ENV_FILE}}"
# Defaults tuned for OV5647: use 4:3 output and force the common 2x2 binned full-FOV mode.
VIDEO_WIDTH="640"
VIDEO_HEIGHT="480"
VIDEO_FPS="30"
VIDEO_BITRATE="${VIDEO_BITRATE:-3000000}"
VIDEO_INVERT="${VIDEO_INVERT:-1}"
VIDEO_SENSOR_MODE="${VIDEO_SENSOR_MODE:-1296:972}"
if [[ "${ROVERD_VIDEO_ENABLE}" -ne 1 ]]; then
echo "Video publisher disabled by roverd media config; skipping" >&2
exit 0
fi
# Flip the camera 180deg by default; allow upright camera mounts via VIDEO_INVERT=0.
# The inversion decision is made in roverd.yaml and written into media.env, so
# this script only converts the configured logical value into libcamera flags.
FLIP_ARGS=()
if [[ "${VIDEO_INVERT}" -ne 0 ]]; then
if [[ "${ROVERD_VIDEO_INVERT}" -ne 0 ]]; then
FLIP_ARGS=(--rotation 180)
fi
MODE_ARGS=()
if [[ -n "${VIDEO_SENSOR_MODE}" ]]; then
MODE_ARGS=(--mode "${VIDEO_SENSOR_MODE}")
if [[ -n "${ROVERD_VIDEO_SENSOR_MODE:-}" ]]; then
MODE_ARGS=(--mode "${ROVERD_VIDEO_SENSOR_MODE}")
fi
if [[ -n "${LIBCAMERA_BIN:-}" ]]; then
@@ -110,11 +114,11 @@ run_pipeline() {
--inline \
--timeout 0 \
"${MODE_ARGS[@]}" \
--width "${VIDEO_WIDTH}" \
--height "${VIDEO_HEIGHT}" \
--width "${ROVERD_VIDEO_WIDTH}" \
--height "${ROVERD_VIDEO_HEIGHT}" \
"${FLIP_ARGS[@]}" \
--framerate "${VIDEO_FPS}" \
--bitrate "${VIDEO_BITRATE}" \
--framerate "${ROVERD_VIDEO_FPS}" \
--bitrate "${ROVERD_VIDEO_BITRATE}" \
--codec h264 \
--profile baseline \
--denoise auto \
@@ -138,7 +142,7 @@ run_pipeline() {
-muxdelay 0 \
-muxpreload 0 \
-f mpegts \
"${PUBLISH_URL}"
"${ROVERD_VIDEO_PUBLISH_URL}"
}
while true; do