From 38df734d4211710c13b6b48fb4695b5b2475cc7e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 12 May 2026 11:13:47 -0300 Subject: [PATCH] install: keep UVC output on MJPEG helper --- Cargo.lock | 6 ++--- client/Cargo.toml | 2 +- common/Cargo.toml | 2 +- scripts/daemon/lesavka-core.sh | 11 +++++++- scripts/install/server.sh | 27 +++++++++++++++---- server/Cargo.toml | 2 +- .../daemon/server_core_script_contract.rs | 15 +++++++++++ .../install/server_install_script_contract.rs | 17 +++++++++--- ...stall_preserves_codec_settings_contract.rs | 9 ++++++- 9 files changed, 74 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e1fc35..5baf3ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.22.11" +version = "0.22.12" dependencies = [ "anyhow", "async-stream", @@ -1686,7 +1686,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.22.11" +version = "0.22.12" dependencies = [ "anyhow", "base64", @@ -1698,7 +1698,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.22.11" +version = "0.22.12" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index 10ed5a3..845526d 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.22.11" +version = "0.22.12" edition = "2024" [dependencies] diff --git a/common/Cargo.toml b/common/Cargo.toml index 6108e0d..22f5950 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.22.11" +version = "0.22.12" edition = "2024" build = "build.rs" diff --git a/scripts/daemon/lesavka-core.sh b/scripts/daemon/lesavka-core.sh index b7b878a..11729cf 100755 --- a/scripts/daemon/lesavka-core.sh +++ b/scripts/daemon/lesavka-core.sh @@ -174,7 +174,16 @@ UVC_HEIGHT=${LESAVKA_UVC_HEIGHT:-720} UVC_FPS=${LESAVKA_UVC_FPS:-30} UVC_DISABLE_IRQ=${LESAVKA_UVC_DISABLE_IRQ:-} UVC_BULK=${LESAVKA_UVC_BULK:-} -UVC_CODEC=${LESAVKA_UVC_CODEC:-yuyv} +UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg} +case "${UVC_CODEC,,}" in + mjpeg|mjpg|jpeg) + UVC_CODEC=mjpeg + ;; + *) + log "UVC codec '$UVC_CODEC' is not supported by the MJPEG UVC helper; using mjpeg" + UVC_CODEC=mjpeg + ;; +esac uvc_fifo_min() { local path="$1" diff --git a/scripts/install/server.sh b/scripts/install/server.sh index ec54e80..ab5afb5 100755 --- a/scripts/install/server.sh +++ b/scripts/install/server.sh @@ -27,6 +27,18 @@ persisted_uvc_value() { persisted_env_value /etc/lesavka/uvc.env "$1" } +normalize_uvc_codec() { + local raw=${1:-mjpeg} + case "${raw,,}" in + mjpeg|mjpg|jpeg|"") + echo "mjpeg" + ;; + *) + echo "mjpeg" + ;; + esac +} + uvc_env_value() { local key=$1 local default=$2 @@ -43,7 +55,12 @@ REPO_URL=${LESAVKA_REPO_URL:-} INSTALL_SOURCE=${LESAVKA_INSTALL_SOURCE:-auto} USER_HOME=$(getent passwd "$ORIG_USER" | cut -d: -f6) PERSISTED_UVC_CODEC=$(persisted_uvc_value LESAVKA_UVC_CODEC || true) -INSTALL_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") +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 " Use LESAVKA_INSTALL_CAM_CODEC=hevc to choose HEVC for the client-to-server upstream transport." +fi INSTALL_CAM_CODEC=${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-hevc}} 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}} @@ -507,7 +524,7 @@ live_uvc_descriptor_codec() { return 0 fi if [[ -e "$function_root/streaming/header/h/yuyv" ]]; then - echo "hevc" + echo "yuyv" return 0 fi if [[ -d "$function_root/streaming/mjpeg/m" ]]; then @@ -515,7 +532,7 @@ live_uvc_descriptor_codec() { return 0 fi if [[ -d "$function_root/streaming/uncompressed/yuyv" ]]; then - echo "hevc" + echo "yuyv" return 0 fi echo "unknown" @@ -529,8 +546,8 @@ live_uvc_descriptor_matches_request() { [[ "$INSTALL_UVC_CODEC" == "mjpeg" ]] || return 1 frame_root="$function_root/streaming/mjpeg/m/720p" ;; - hevc) - [[ "$INSTALL_UVC_CODEC" != "mjpeg" ]] || return 1 + yuyv) + [[ "$INSTALL_UVC_CODEC" == "yuyv" ]] || return 1 frame_root="$function_root/streaming/uncompressed/yuyv/480p" ;; *) diff --git a/server/Cargo.toml b/server/Cargo.toml index 6740a7b..d08d88e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.22.11" +version = "0.22.12" edition = "2024" autobins = false diff --git a/tests/contract/scripts/daemon/server_core_script_contract.rs b/tests/contract/scripts/daemon/server_core_script_contract.rs index 3cde82e..5972a42 100644 --- a/tests/contract/scripts/daemon/server_core_script_contract.rs +++ b/tests/contract/scripts/daemon/server_core_script_contract.rs @@ -89,3 +89,18 @@ fn core_script_treats_uvc_env_as_defaults_not_overrides() { ); } } + +#[test] +fn core_script_keeps_uvc_output_on_supported_mjpeg_descriptor() { + for expected in [ + "UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg}", + "UVC codec '$UVC_CODEC' is not supported by the MJPEG UVC helper; using mjpeg", + "UVC_CODEC=mjpeg", + "streaming/mjpeg/m/720p", + ] { + assert!( + CORE_SCRIPT.contains(expected), + "lesavka-core should keep UVC output on the supported MJPEG helper path: {expected}" + ); + } +} diff --git a/tests/installer/scripts/install/server_install_script_contract.rs b/tests/installer/scripts/install/server_install_script_contract.rs index 4b7cd09..76bf60f 100644 --- a/tests/installer/scripts/install/server_install_script_contract.rs +++ b/tests/installer/scripts/install/server_install_script_contract.rs @@ -64,10 +64,14 @@ fn server_install_pins_hdmi_camera_and_display_defaults() { .contains("${LESAVKA_INSTALL_UPLINK_AUDIO_CODEC:-${LESAVKA_UPLINK_AUDIO_CODEC:-pcm}}") ); assert!( - SERVER_INSTALL.contains( - "INSTALL_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}" - ), - "plain upgrade installs should preserve the already-installed UVC codec" + SERVER_INSTALL + .contains("PERSISTED_UVC_CODEC=$(persisted_uvc_value LESAVKA_UVC_CODEC || true)") + && SERVER_INSTALL.contains( + "REQUESTED_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}" + ) + && SERVER_INSTALL + .contains("INSTALL_UVC_CODEC=$(normalize_uvc_codec \"$REQUESTED_UVC_CODEC\")"), + "plain upgrade installs should normalize persisted UVC output to the supported MJPEG helper path" ); assert!( SERVER_INSTALL.contains("${LESAVKA_INSTALL_UVC_FRAME_META:-${LESAVKA_UVC_FRAME_META:-0}}") @@ -167,6 +171,11 @@ fn server_install_pins_hdmi_camera_and_display_defaults() { !SERVER_INSTALL.contains("LESAVKA_UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg}"), "install script should not let ambient LESAVKA_UVC_CODEC leak into persisted defaults" ); + assert!( + SERVER_INSTALL + .contains("UVC gadget output codec '${REQUESTED_UVC_CODEC}' is not supported"), + "install script should not keep creating YUYV descriptors under the misleading HEVC UVC label" + ); assert!( SERVER_INSTALL.contains("ensure_hevc_decode_support"), "install script should prepare HEVC decode dependencies for the default uplink codec" diff --git a/tests/regression/install/install_preserves_codec_settings_contract.rs b/tests/regression/install/install_preserves_codec_settings_contract.rs index 031bda0..1f4509d 100644 --- a/tests/regression/install/install_preserves_codec_settings_contract.rs +++ b/tests/regression/install/install_preserves_codec_settings_contract.rs @@ -19,7 +19,9 @@ const CLIENT_CAMERA: &str = include_str!(concat!( fn server_install_defaults_to_hevc_ingress_and_mjpeg_uvc_output() { for marker in [ "PERSISTED_UVC_CODEC=$(persisted_uvc_value LESAVKA_UVC_CODEC || true)", - "INSTALL_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}", + "normalize_uvc_codec()", + "REQUESTED_UVC_CODEC=${LESAVKA_INSTALL_UVC_CODEC:-${PERSISTED_UVC_CODEC:-mjpeg}}", + "INSTALL_UVC_CODEC=$(normalize_uvc_codec \"$REQUESTED_UVC_CODEC\")", "INSTALL_CAM_CODEC=${LESAVKA_INSTALL_CAM_CODEC:-${LESAVKA_CAM_CODEC:-hevc}}", "printf 'LESAVKA_CAM_CODEC=%s\\n' \"${INSTALL_CAM_CODEC}\"", "printf 'LESAVKA_UVC_CODEC=%s\\n' \"${INSTALL_UVC_CODEC}\"", @@ -43,6 +45,11 @@ fn server_install_does_not_let_ambient_uvc_codec_override_persisted_default() { SERVER_INSTALL.contains("LESAVKA_INSTALL_UVC_CODEC"), "operator install override should remain available" ); + assert!( + SERVER_INSTALL + .contains("UVC gadget output codec '${REQUESTED_UVC_CODEC}' is not supported"), + "unsupported host-facing UVC codecs should be downgraded loudly instead of creating a stale YUYV descriptor" + ); } #[test]