install: default upstream camera to mjpeg

This commit is contained in:
Brad Stein 2026-05-13 02:14:35 -03:00
parent d9cc0d2237
commit 0ef34da971
7 changed files with 61 additions and 21 deletions

6
Cargo.lock generated
View File

@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "lesavka_client" name = "lesavka_client"
version = "0.22.19" version = "0.22.20"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1686,7 +1686,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_common" name = "lesavka_common"
version = "0.22.19" version = "0.22.20"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -1698,7 +1698,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_server" name = "lesavka_server"
version = "0.22.19" version = "0.22.20"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",

View File

@ -4,7 +4,7 @@ path = "src/main.rs"
[package] [package]
name = "lesavka_client" name = "lesavka_client"
version = "0.22.19" version = "0.22.20"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lesavka_common" name = "lesavka_common"
version = "0.22.19" version = "0.22.20"
edition = "2024" edition = "2024"
build = "build.rs" build = "build.rs"

View File

@ -39,6 +39,22 @@ normalize_uvc_codec() {
esac esac
} }
normalize_cam_codec() {
local raw=${1:-mjpeg}
case "${raw,,}" in
mjpeg|mjpg|jpeg|"")
echo "mjpeg"
;;
hevc|h265|h.265)
echo "hevc"
;;
*)
echo "❌ unsupported upstream camera codec '${raw}'. Use mjpeg or hevc." >&2
exit 1
;;
esac
}
uvc_env_value() { uvc_env_value() {
local key=$1 local key=$1
local default=$2 local default=$2
@ -61,7 +77,7 @@ if [[ "${REQUESTED_UVC_CODEC,,}" != "${INSTALL_UVC_CODEC}" ]]; then
echo "⚠️ UVC gadget output codec '${REQUESTED_UVC_CODEC}' is not supported by the MJPEG UVC helper; using '${INSTALL_UVC_CODEC}' for the host-facing gadget." echo "⚠️ UVC gadget output codec '${REQUESTED_UVC_CODEC}' is not supported by the MJPEG UVC helper; using '${INSTALL_UVC_CODEC}' for the host-facing gadget."
echo " Use LESAVKA_INSTALL_CAM_CODEC=hevc to choose HEVC for the client-to-server upstream transport." echo " Use LESAVKA_INSTALL_CAM_CODEC=hevc to choose HEVC for the client-to-server upstream transport."
fi fi
INSTALL_CAM_CODEC=${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-hevc}} INSTALL_CAM_CODEC=$(normalize_cam_codec "${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-mjpeg}}")
INSTALL_UPLINK_AUDIO_CODEC=${LESAVKA_INSTALL_UPLINK_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}} INSTALL_UPLINK_AUDIO_CODEC=${LESAVKA_INSTALL_UPLINK_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}}
INSTALL_UVC_FRAME_META=${LESAVKA_INSTALL_UVC_FRAME_META:-${LESAVKA_UVC_FRAME_META:-0}} INSTALL_UVC_FRAME_META=${LESAVKA_INSTALL_UVC_FRAME_META:-${LESAVKA_UVC_FRAME_META:-0}}
INSTALL_UVC_FRAME_META_LOG_PATH=${LESAVKA_INSTALL_UVC_FRAME_META_LOG_PATH:-${LESAVKA_UVC_FRAME_META_LOG_PATH:-/tmp/lesavka-uvc-frame-meta.jsonl}} INSTALL_UVC_FRAME_META_LOG_PATH=${LESAVKA_INSTALL_UVC_FRAME_META_LOG_PATH:-${LESAVKA_UVC_FRAME_META_LOG_PATH:-/tmp/lesavka-uvc-frame-meta.jsonl}}
@ -214,7 +230,7 @@ ensure_hevc_decode_support() {
else else
echo "❌ no hardware HEVC decoder exposed to GStreamer; Lesavka will not fall back to avdec_h265 in production." >&2 echo "❌ no hardware HEVC decoder exposed to GStreamer; Lesavka will not fall back to avdec_h265 in production." >&2
if [[ "$INSTALL_UVC_CODEC" == "hevc" || "$INSTALL_CAM_CODEC" == "hevc" ]]; then if [[ "$INSTALL_UVC_CODEC" == "hevc" || "$INSTALL_CAM_CODEC" == "hevc" ]]; then
echo " Install/repair v4l2slh265dec or set a non-HEVC UVC codec before running the server installer." >&2 echo " Install/repair v4l2slh265dec or set LESAVKA_INSTALL_CAM_CODEC=mjpeg before running the server installer." >&2
exit 1 exit 1
fi fi
fi fi
@ -232,11 +248,19 @@ ensure_hevc_decode_support() {
>"$hevc_smoke_log" 2>&1; then >"$hevc_smoke_log" 2>&1; then
echo "✅ hardware HEVC decoder passed a real 1280x720 decode smoke: $hevc_decoder" echo "✅ hardware HEVC decoder passed a real 1280x720 decode smoke: $hevc_decoder"
else else
if [[ "$INSTALL_CAM_CODEC" == "hevc" ]]; then
echo "❌ hardware HEVC decoder is exposed but failed a real 1280x720 decode smoke: $hevc_decoder" >&2
echo " smoke log: $hevc_smoke_log" >&2
sed -n '1,120p' "$hevc_smoke_log" >&2 || true
echo " Refusing HEVC upstream install because production video decode must be hardware-accelerated and proven." >&2
echo " Use LESAVKA_INSTALL_CAM_CODEC=mjpeg while the HEVC decoder stack is repaired." >&2
exit 1
fi
echo "⚠️ hardware HEVC decoder is exposed but the synthetic 1280x720 decode smoke failed: $hevc_decoder" >&2 echo "⚠️ hardware HEVC decoder is exposed but the synthetic 1280x720 decode smoke failed: $hevc_decoder" >&2
echo " smoke log: $hevc_smoke_log" >&2 echo " smoke log: $hevc_smoke_log" >&2
sed -n '1,120p' "$hevc_smoke_log" >&2 || true sed -n '1,120p' "$hevc_smoke_log" >&2 || true
echo " Continuing because gst-launch synthetic streams can be a false negative on the Pi stateless decoder." >&2 echo " Continuing because this install is not selecting HEVC upstream." >&2
echo " Runtime still requires a buildable hardware HEVC decoder and will not use software fallback in production." >&2 echo " Explicit HEVC installs require a passing hardware decode smoke and will not use software fallback in production." >&2
echo " Use scripts/manual/run_hardware_media_smoke.sh for artifact-backed follow-up evidence." >&2 echo " Use scripts/manual/run_hardware_media_smoke.sh for artifact-backed follow-up evidence." >&2
fi fi
fi fi

View File

@ -10,7 +10,7 @@ bench = false
[package] [package]
name = "lesavka_server" name = "lesavka_server"
version = "0.22.19" version = "0.22.20"
edition = "2024" edition = "2024"
autobins = false autobins = false

View File

@ -60,7 +60,10 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
assert!(SERVER_INSTALL.contains("${LESAVKA_CAM_HEIGHT:-1080}")); assert!(SERVER_INSTALL.contains("${LESAVKA_CAM_HEIGHT:-1080}"));
assert!(SERVER_INSTALL.contains("${LESAVKA_CAM_FPS:-30}")); assert!(SERVER_INSTALL.contains("${LESAVKA_CAM_FPS:-30}"));
assert!(SERVER_INSTALL.contains("${LESAVKA_INSTALL_CAM_OUTPUT:-uvc}")); assert!(SERVER_INSTALL.contains("${LESAVKA_INSTALL_CAM_OUTPUT:-uvc}"));
assert!(SERVER_INSTALL.contains("${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-hevc}}")); assert!(SERVER_INSTALL.contains("normalize_cam_codec()"));
assert!(SERVER_INSTALL.contains(
"INSTALL_CAM_CODEC=$(normalize_cam_codec \"${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-mjpeg}}\")"
));
assert!( assert!(
SERVER_INSTALL SERVER_INSTALL
.contains("${LESAVKA_INSTALL_UPLINK_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}}") .contains("${LESAVKA_INSTALL_UPLINK_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}}")
@ -194,7 +197,7 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
); );
assert!( assert!(
SERVER_INSTALL.contains("ensure_hevc_decode_support"), SERVER_INSTALL.contains("ensure_hevc_decode_support"),
"install script should prepare HEVC decode dependencies for the default uplink codec" "install script should prepare HEVC decode dependencies when HEVC is selected"
); );
assert!( assert!(
SERVER_INSTALL.contains("rpi_hevc_dec"), SERVER_INSTALL.contains("rpi_hevc_dec"),
@ -215,10 +218,17 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
); );
assert!( assert!(
SERVER_INSTALL.contains("synthetic 1280x720 decode smoke failed") SERVER_INSTALL.contains("synthetic 1280x720 decode smoke failed")
&& SERVER_INSTALL.contains("false negative on the Pi stateless decoder") && SERVER_INSTALL
&& SERVER_INSTALL.contains("Runtime still requires a buildable hardware HEVC decoder") .contains("Continuing because this install is not selecting HEVC upstream")
&& SERVER_INSTALL
.contains("Explicit HEVC installs require a passing hardware decode smoke")
&& SERVER_INSTALL.contains("scripts/manual/run_hardware_media_smoke.sh"), && SERVER_INSTALL.contains("scripts/manual/run_hardware_media_smoke.sh"),
"install script should keep HEVC smoke failures diagnostic instead of blocking known-good Pi runtime paths" "install script should keep non-HEVC installs safe while warning about a broken HEVC stack"
);
assert!(
SERVER_INSTALL.contains("Refusing HEVC upstream install because production video decode must be hardware-accelerated and proven")
&& SERVER_INSTALL.contains("Use LESAVKA_INSTALL_CAM_CODEC=mjpeg while the HEVC decoder stack is repaired"),
"explicit HEVC installs should fail loud instead of producing a black UVC webcam feed"
); );
assert!( assert!(
!SERVER_INSTALL !SERVER_INSTALL
@ -349,8 +359,11 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
); );
assert!( assert!(
SERVER_INSTALL.contains("1920x1080) frame_root=\"$function_root/streaming/mjpeg/m/1080p\"") SERVER_INSTALL.contains("1920x1080) frame_root=\"$function_root/streaming/mjpeg/m/1080p\"")
&& SERVER_INSTALL.contains("1280x720) frame_root=\"$function_root/streaming/mjpeg/m/720p\"") && SERVER_INSTALL
&& SERVER_INSTALL.contains("grep -qx \"${LESAVKA_UVC_INTERVAL:-333333}\" \"$frame_root/dwFrameInterval\""), .contains("1280x720) frame_root=\"$function_root/streaming/mjpeg/m/720p\"")
&& SERVER_INSTALL.contains(
"grep -qx \"${LESAVKA_UVC_INTERVAL:-333333}\" \"$frame_root/dwFrameInterval\""
),
"live descriptor matching should recognize all supported MJPEG UVC profiles instead of collapsing to one 720p frame" "live descriptor matching should recognize all supported MJPEG UVC profiles instead of collapsing to one 720p frame"
); );
assert!( assert!(

View File

@ -1,10 +1,11 @@
// Regression contract for preserving codec settings across upgrades. // Regression contract for preserving codec settings across upgrades.
// //
// Scope: keep HEVC ingress and MJPEG UVC output defaults explicit, while still // Scope: keep safe MJPEG ingress and MJPEG UVC output defaults explicit, while still
// allowing operator-provided install overrides. // allowing operator-provided install overrides.
// Targets: server/client install scripts and client camera capture defaults. // Targets: server/client install scripts and client camera capture defaults.
// Why: Lesavka now supports both MJPEG and HEVC upstream media, and installer // Why: Lesavka now supports both MJPEG and HEVC upstream media, and installer
// reruns must not silently revert the working profile. // reruns must not silently select a HEVC profile that produces black frames
// when hardware decode is not proven.
const SERVER_INSTALL: &str = include_str!(concat!( const SERVER_INSTALL: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
@ -16,13 +17,14 @@ const CLIENT_CAMERA: &str = include_str!(concat!(
)); ));
#[test] #[test]
fn server_install_defaults_to_hevc_ingress_and_mjpeg_uvc_output() { fn server_install_defaults_to_mjpeg_ingress_and_mjpeg_uvc_output() {
for marker in [ for marker in [
"PERSISTED_UVC_CODEC=$(persisted_uvc_value LESAVKA_UVC_CODEC || true)", "PERSISTED_UVC_CODEC=$(persisted_uvc_value LESAVKA_UVC_CODEC || true)",
"normalize_uvc_codec()", "normalize_uvc_codec()",
"normalize_cam_codec()",
"REQUESTED_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}", "REQUESTED_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}",
"INSTALL_UVC_CODEC=$(normalize_uvc_codec \"$REQUESTED_UVC_CODEC\")", "INSTALL_UVC_CODEC=$(normalize_uvc_codec \"$REQUESTED_UVC_CODEC\")",
"INSTALL_CAM_CODEC=${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-hevc}}", "INSTALL_CAM_CODEC=$(normalize_cam_codec \"${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-mjpeg}}\")",
"printf 'LESAVKA_CAM_CODEC=%s\\n' \"${INSTALL_CAM_CODEC}\"", "printf 'LESAVKA_CAM_CODEC=%s\\n' \"${INSTALL_CAM_CODEC}\"",
"printf 'LESAVKA_UVC_CODEC=%s\\n' \"${INSTALL_UVC_CODEC}\"", "printf 'LESAVKA_UVC_CODEC=%s\\n' \"${INSTALL_UVC_CODEC}\"",
"\"LESAVKA_UVC_CODEC=${INSTALL_UVC_CODEC}\"", "\"LESAVKA_UVC_CODEC=${INSTALL_UVC_CODEC}\"",
@ -79,6 +81,7 @@ fn hevc_prerequisites_are_rechecked_idempotently() {
"/etc/modules-load.d/lesavka-hevc.conf", "/etc/modules-load.d/lesavka-hevc.conf",
"gst-inspect-1.0 v4l2slh265dec", "gst-inspect-1.0 v4l2slh265dec",
"will not fall back to avdec_h265 in production", "will not fall back to avdec_h265 in production",
"Refusing HEVC upstream install because production video decode must be hardware-accelerated and proven",
] { ] {
assert!( assert!(
SERVER_INSTALL.contains(marker), SERVER_INSTALL.contains(marker),