media: avoid bulk clamp on non-bulk uvc kernels

This commit is contained in:
Brad Stein 2026-05-14 17:57:49 -03:00
parent eb3b029071
commit 3318900d96
11 changed files with 67 additions and 20 deletions

6
Cargo.lock generated
View File

@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "lesavka_client" name = "lesavka_client"
version = "0.22.31" version = "0.22.32"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1686,7 +1686,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_common" name = "lesavka_common"
version = "0.22.31" version = "0.22.32"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -1698,7 +1698,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_server" name = "lesavka_server"
version = "0.22.31" version = "0.22.32"
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.31" version = "0.22.32"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

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

View File

@ -313,7 +313,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
| `LESAVKA_UVC_APP_MAX_BYTES` | server UVC appsrc memory guard; defaults to `4194304` queued bytes | | `LESAVKA_UVC_APP_MAX_BYTES` | server UVC appsrc memory guard; defaults to `4194304` queued bytes |
| `LESAVKA_UVC_APP_MAX_TIME_NS` | server UVC appsrc memory guard; defaults to `200000000` ns of queued media | | `LESAVKA_UVC_APP_MAX_TIME_NS` | server UVC appsrc memory guard; defaults to `200000000` ns of queued media |
| `LESAVKA_UVC_BLOCKING` | server hardware/device override | | `LESAVKA_UVC_BLOCKING` | server hardware/device override |
| `LESAVKA_UVC_BULK` | UVC transfer-mode override; defaults to `1` so patched kernels prefer reliable bulk transfer over lossy isochronous MJPEG. Set `0` to force classic isochronous descriptors | | `LESAVKA_UVC_BULK` | UVC transfer-mode override; defaults to `1` so patched kernels prefer reliable bulk transfer over lossy isochronous MJPEG. If the live kernel does not expose `streaming_bulk`, Lesavka falls back to isochronous descriptors without the 512-byte bulk clamp. Set `0` to force classic isochronous descriptors |
| `LESAVKA_UVC_BUFFER_COUNT` | UVC helper freshness override; number of queued gadget output buffers, defaults to `2` for live-call freshness | | `LESAVKA_UVC_BUFFER_COUNT` | UVC helper freshness override; number of queued gadget output buffers, defaults to `2` for live-call freshness |
| `LESAVKA_UVC_BY_PATH_ROOT` | server hardware/device override | | `LESAVKA_UVC_BY_PATH_ROOT` | server hardware/device override |
| `LESAVKA_UVC_CODEC` | server hardware/device override | | `LESAVKA_UVC_CODEC` | server hardware/device override |

View File

@ -181,8 +181,10 @@ flag_enabled() {
esac esac
} }
if flag_enabled "${LESAVKA_UVC_BULK:-1}"; then if flag_enabled "${LESAVKA_UVC_BULK:-1}"; then
UVC_BULK_REQUESTED=1
UVC_BULK=1 UVC_BULK=1
else else
UVC_BULK_REQUESTED=
UVC_BULK= UVC_BULK=
fi fi
UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg} UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg}
@ -294,18 +296,20 @@ compute_uvc_payload_cap() {
UVC_PAYLOAD_CAP=$((bytes * pct / 100)) UVC_PAYLOAD_CAP=$((bytes * pct / 100))
} }
compute_uvc_payload_cap apply_uvc_payload_limits() {
if [[ -n $UVC_PAYLOAD_CAP && $UVC_PAYLOAD_CAP -gt 0 ]]; then compute_uvc_payload_cap
log "UVC fifo periodic=${UVC_FIFO_PERIODIC:-?} np=${UVC_FIFO_NP:-?} cap=${UVC_PAYLOAD_CAP}B pct=${UVC_PAYLOAD_PCT:-?} src=${UVC_PAYLOAD_SRC:-?} udc=${UVC_UDC:-?}" if [[ -n $UVC_PAYLOAD_CAP && $UVC_PAYLOAD_CAP -gt 0 ]]; then
if ((UVC_MAXPACKET > UVC_PAYLOAD_CAP)); then log "UVC fifo periodic=${UVC_FIFO_PERIODIC:-?} np=${UVC_FIFO_NP:-?} cap=${UVC_PAYLOAD_CAP}B pct=${UVC_PAYLOAD_PCT:-?} src=${UVC_PAYLOAD_SRC:-?} udc=${UVC_UDC:-?}"
log "clamping UVC maxpacket $UVC_MAXPACKET -> $UVC_PAYLOAD_CAP" if ((UVC_MAXPACKET > UVC_PAYLOAD_CAP)); then
UVC_MAXPACKET=$UVC_PAYLOAD_CAP log "clamping UVC maxpacket $UVC_MAXPACKET -> $UVC_PAYLOAD_CAP"
UVC_MAXPACKET=$UVC_PAYLOAD_CAP
fi
fi fi
fi if [[ -n $UVC_BULK && $UVC_MAXPACKET -gt 512 ]]; then
if [[ -n $UVC_BULK && $UVC_MAXPACKET -gt 512 ]]; then log "clamping UVC maxpacket $UVC_MAXPACKET -> 512 (bulk)"
log "clamping UVC maxpacket $UVC_MAXPACKET -> 512 (bulk)" UVC_MAXPACKET=512
UVC_MAXPACKET=512 fi
fi }
if [[ -n ${LESAVKA_UVC_MJPEG:-} ]]; then if [[ -n ${LESAVKA_UVC_MJPEG:-} ]]; then
UVC_CODEC=mjpeg UVC_CODEC=mjpeg
fi fi
@ -588,6 +592,19 @@ if [[ -z $DISABLE_UVC ]]; then
# ----------------------- UVC function (usbvideo) ------------------ # ----------------------- UVC function (usbvideo) ------------------
mkdir -p "$G/functions/uvc.usb0" mkdir -p "$G/functions/uvc.usb0"
F="$G/functions/uvc.usb0" F="$G/functions/uvc.usb0"
if [[ -n $UVC_BULK_REQUESTED ]]; then
if [[ -e "$F/streaming_bulk" ]]; then
UVC_BULK=1
else
# Some kernels do not expose the patched bulk-transfer knob. Falling
# back to isochronous must also avoid the 512-byte bulk packet clamp;
# otherwise MJPEG frames are much more likely to arrive truncated.
log "UVC bulk requested but this kernel lacks streaming_bulk; using isochronous descriptors"
UVC_BULK=
UVC_MAXPACKET=${LESAVKA_UVC_MAXPACKET:-1024}
fi
fi
apply_uvc_payload_limits
echo "$UVC_STREAMING_INTERVAL" >"$F/streaming_interval" echo "$UVC_STREAMING_INTERVAL" >"$F/streaming_interval"
echo "$UVC_MAXPACKET" >"$F/streaming_maxpacket" echo "$UVC_MAXPACKET" >"$F/streaming_maxpacket"
echo "$UVC_MAXBURST" >"$F/streaming_maxburst" echo "$UVC_MAXBURST" >"$F/streaming_maxburst"

View File

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

View File

@ -669,7 +669,17 @@ fn uvc_frame_size_guard_enabled() -> bool {
} }
fn uvc_bulk_transfer_enabled() -> bool { fn uvc_bulk_transfer_enabled() -> bool {
env_flag_enabled("LESAVKA_UVC_BULK", true) if !env_flag_enabled("LESAVKA_UVC_BULK", true) {
return false;
}
let base = std::path::Path::new(CONFIGFS_UVC_BASE);
if base.exists() && !base.join("streaming_bulk").exists() {
eprintln!(
"[lesavka-uvc] UVC bulk requested but live configfs has no streaming_bulk; using isochronous payload sizing"
);
return false;
}
true
} }
fn uvc_frame_size_for_active_mode(width: u32, height: u32, fps: u32) -> u32 { fn uvc_frame_size_for_active_mode(width: u32, height: u32, fps: u32) -> u32 {

View File

@ -19,6 +19,8 @@ const UVC_DATA_SIZE: usize = 60;
const UVC_STRING_CONTROL_IDX: u8 = 0; const UVC_STRING_CONTROL_IDX: u8 = 0;
#[cfg(coverage)] #[cfg(coverage)]
const UVC_STRING_STREAMING_IDX: u8 = 1; const UVC_STRING_STREAMING_IDX: u8 = 1;
#[cfg(coverage)]
const CONFIGFS_UVC_BASE: &str = "/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0";
#[cfg(coverage)] #[cfg(coverage)]
const USB_DIR_IN: u8 = 0x80; const USB_DIR_IN: u8 = 0x80;

View File

@ -222,7 +222,14 @@ fn uvc_frame_size_guard_enabled() -> bool {
#[cfg(coverage)] #[cfg(coverage)]
fn uvc_bulk_transfer_enabled() -> bool { fn uvc_bulk_transfer_enabled() -> bool {
env_flag_enabled("LESAVKA_UVC_BULK", true) if !env_flag_enabled("LESAVKA_UVC_BULK", true) {
return false;
}
let base = std::path::Path::new(CONFIGFS_UVC_BASE);
if base.exists() && !base.join("streaming_bulk").exists() {
return false;
}
true
} }
#[cfg(coverage)] #[cfg(coverage)]

View File

@ -97,6 +97,9 @@ fn core_script_keeps_uvc_output_on_supported_mjpeg_descriptor() {
"UVC codec '$UVC_CODEC' is not supported by the MJPEG UVC helper; using mjpeg", "UVC codec '$UVC_CODEC' is not supported by the MJPEG UVC helper; using mjpeg",
"UVC_CODEC=mjpeg", "UVC_CODEC=mjpeg",
"flag_enabled \"${LESAVKA_UVC_BULK:-1}\"", "flag_enabled \"${LESAVKA_UVC_BULK:-1}\"",
"UVC bulk requested but this kernel lacks streaming_bulk; using isochronous descriptors",
"apply_uvc_payload_limits",
"UVC_MAXPACKET=${LESAVKA_UVC_MAXPACKET:-1024}",
"uvc_mjpeg_frame_size_for_fps()", "uvc_mjpeg_frame_size_for_fps()",
"UVC_MJPEG_BUDGET_BYTES_PER_SEC=${LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC:-10000000}", "UVC_MJPEG_BUDGET_BYTES_PER_SEC=${LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC:-10000000}",
"UVC_FRAME_SIZE=\"$(uvc_mjpeg_frame_size_for_fps \"$UVC_FPS\")\"", "UVC_FRAME_SIZE=\"$(uvc_mjpeg_frame_size_for_fps \"$UVC_FPS\")\"",

View File

@ -341,6 +341,14 @@ mod uvc_binary {
}); });
} }
#[test]
fn uvc_bulk_mode_is_tied_to_live_configfs_support() {
let source = include_str!("../../../../server/src/bin/lesavka-uvc.real.inc");
assert!(source.contains("base.exists() && !base.join(\"streaming_bulk\").exists()"));
assert!(source.contains("using isochronous payload sizing"));
}
#[test] #[test]
#[serial] #[serial]
fn uvc_stats_snapshot_can_be_disabled_or_written() { fn uvc_stats_snapshot_can_be_disabled_or_written() {