fix: normalize UVC MJPEG before gadget handoff
This commit is contained in:
parent
3c7ccfdbcc
commit
29a565f9e2
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1658,7 +1658,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1692,7 +1692,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1704,7 +1704,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -321,7 +321,7 @@ fi
|
|||||||
UVC_FRAME_SIZE=${LESAVKA_UVC_FRAME_SIZE:-$((UVC_WIDTH * UVC_HEIGHT * 2))}
|
UVC_FRAME_SIZE=${LESAVKA_UVC_FRAME_SIZE:-$((UVC_WIDTH * UVC_HEIGHT * 2))}
|
||||||
UVC_INTERVAL_30=${LESAVKA_UVC_INTERVAL_30:-333333}
|
UVC_INTERVAL_30=${LESAVKA_UVC_INTERVAL_30:-333333}
|
||||||
UVC_INTERVAL_20=${LESAVKA_UVC_INTERVAL_20:-500000}
|
UVC_INTERVAL_20=${LESAVKA_UVC_INTERVAL_20:-500000}
|
||||||
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:-4500000}
|
||||||
UVC_ISOCHRONOUS_LIMIT_PCT=${LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT:-85}
|
UVC_ISOCHRONOUS_LIMIT_PCT=${LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT:-85}
|
||||||
|
|
||||||
uvc_isochronous_budget_bytes_per_sec() {
|
uvc_isochronous_budget_bytes_per_sec() {
|
||||||
|
|||||||
@ -388,7 +388,7 @@ LESAVKA_UVC_MAXBURST=$(uvc_env_value LESAVKA_UVC_MAXBURST 0)
|
|||||||
LESAVKA_UVC_BULK=$(uvc_env_value LESAVKA_UVC_BULK 1)
|
LESAVKA_UVC_BULK=$(uvc_env_value LESAVKA_UVC_BULK 1)
|
||||||
LESAVKA_UVC_FRAME_SIZE_GUARD=$(uvc_env_value LESAVKA_UVC_FRAME_SIZE_GUARD 1)
|
LESAVKA_UVC_FRAME_SIZE_GUARD=$(uvc_env_value LESAVKA_UVC_FRAME_SIZE_GUARD 1)
|
||||||
LESAVKA_UVC_FRAME_MAX_BYTES=$(uvc_env_value LESAVKA_UVC_FRAME_MAX_BYTES 0)
|
LESAVKA_UVC_FRAME_MAX_BYTES=$(uvc_env_value LESAVKA_UVC_FRAME_MAX_BYTES 0)
|
||||||
LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC=$(uvc_env_value LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC 10000000)
|
LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC=$(uvc_env_value LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC 4500000)
|
||||||
LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT=$(uvc_env_value LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT 85)
|
LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT=$(uvc_env_value LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT 85)
|
||||||
LESAVKA_UVC_STATS_PATH=$(uvc_env_value LESAVKA_UVC_STATS_PATH /run/lesavka-uvc-video-stats.json)
|
LESAVKA_UVC_STATS_PATH=$(uvc_env_value LESAVKA_UVC_STATS_PATH /run/lesavka-uvc-video-stats.json)
|
||||||
EOF
|
EOF
|
||||||
@ -1645,11 +1645,11 @@ SERVER_ENV_TMP=$(mktemp)
|
|||||||
printf 'LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS=%s\n' "${LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS:-20}"
|
printf 'LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS=%s\n' "${LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS:-20}"
|
||||||
printf 'LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS=%s\n' "${LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS:-2}"
|
printf 'LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS=%s\n' "${LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS:-2}"
|
||||||
printf 'LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT=%s\n' "${LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT:-15}"
|
printf 'LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT=%s\n' "${LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT:-15}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-0}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-1}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY:-72}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY:-60}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS:-25}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS:-25}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT:-30}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT:-30}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-768}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-384}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_VISUAL_GUARD=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_VISUAL_GUARD:-1}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_VISUAL_GUARD=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_VISUAL_GUARD:-1}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_SIZE_DROP_PCT=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_SIZE_DROP_PCT:-18}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_SIZE_DROP_PCT=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_SIZE_DROP_PCT:-18}"
|
||||||
printf 'LESAVKA_UVC_DIRECT_MJPEG_MIN_REFERENCE_BYTES=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_MIN_REFERENCE_BYTES:-49152}"
|
printf 'LESAVKA_UVC_DIRECT_MJPEG_MIN_REFERENCE_BYTES=%s\n' "${LESAVKA_UVC_DIRECT_MJPEG_MIN_REFERENCE_BYTES:-49152}"
|
||||||
|
|||||||
@ -16,7 +16,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.25.3"
|
version = "0.25.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const DEFAULT_UVC_BUFFER_COUNT: u32 = 1;
|
|||||||
const DEFAULT_UVC_IDLE_PUMP_MS: u64 = 2;
|
const DEFAULT_UVC_IDLE_PUMP_MS: u64 = 2;
|
||||||
const DEFAULT_UVC_FRAME_MAX_AGE_MS: u64 = 1_000;
|
const DEFAULT_UVC_FRAME_MAX_AGE_MS: u64 = 1_000;
|
||||||
const DEFAULT_UVC_QUEUE_PACING: bool = true;
|
const DEFAULT_UVC_QUEUE_PACING: bool = true;
|
||||||
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 10_000_000;
|
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 4_500_000;
|
||||||
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
||||||
const HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC: u32 = 8_000;
|
const HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC: u32 = 8_000;
|
||||||
const DEFAULT_UVC_STATS_INTERVAL_MS: u64 = 5_000;
|
const DEFAULT_UVC_STATS_INTERVAL_MS: u64 = 5_000;
|
||||||
|
|||||||
@ -59,7 +59,7 @@ const DEFAULT_UVC_FRAME_MAX_AGE_MS: u64 = 1_000;
|
|||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
const DEFAULT_UVC_QUEUE_PACING: bool = true;
|
const DEFAULT_UVC_QUEUE_PACING: bool = true;
|
||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 10_000_000;
|
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 4_500_000;
|
||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
|
|||||||
@ -80,7 +80,7 @@ fn uvc_frame_max_bytes_defaults_to_freshness_budget_and_allows_override() {
|
|||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
let cfg = sample_cfg();
|
let cfg = sample_cfg();
|
||||||
assert_eq!(uvc_frame_max_bytes(cfg), 400_000);
|
assert_eq!(uvc_frame_max_bytes(cfg), 180_000);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ fn uvc_frame_max_bytes_defaults_to_freshness_budget_and_allows_override() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
with_var("LESAVKA_UVC_FRAME_MAX_BYTES", Some("0"), || {
|
with_var("LESAVKA_UVC_FRAME_MAX_BYTES", Some("0"), || {
|
||||||
assert_eq!(uvc_frame_max_bytes(sample_cfg()), 400_000);
|
assert_eq!(uvc_frame_max_bytes(sample_cfg()), 180_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
with_vars(
|
with_vars(
|
||||||
|
|||||||
@ -16,11 +16,11 @@ const DEFAULT_HEVC_DOMINANT_BYTE_PCT: u32 = 92;
|
|||||||
const DEFAULT_DIRECT_MJPEG_SIZE_DROP_PCT: u32 = 18;
|
const DEFAULT_DIRECT_MJPEG_SIZE_DROP_PCT: u32 = 18;
|
||||||
const DEFAULT_DIRECT_MJPEG_MIN_REFERENCE_BYTES: u32 = 48 * 1024;
|
const DEFAULT_DIRECT_MJPEG_MIN_REFERENCE_BYTES: u32 = 48 * 1024;
|
||||||
const DEFAULT_DIRECT_MJPEG_PROFILE_MISMATCH_REJECT: bool = false;
|
const DEFAULT_DIRECT_MJPEG_PROFILE_MISMATCH_REJECT: bool = false;
|
||||||
const DEFAULT_DIRECT_MJPEG_NORMALIZE: bool = false;
|
const DEFAULT_DIRECT_MJPEG_NORMALIZE: bool = true;
|
||||||
const DEFAULT_DIRECT_MJPEG_JPEG_QUALITY: u32 = 72;
|
const DEFAULT_DIRECT_MJPEG_JPEG_QUALITY: u32 = 60;
|
||||||
const DEFAULT_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS: u32 = 25;
|
const DEFAULT_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS: u32 = 25;
|
||||||
const DEFAULT_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT: u32 = 30;
|
const DEFAULT_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT: u32 = 30;
|
||||||
const DEFAULT_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB: u32 = 768;
|
const DEFAULT_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB: u32 = 384;
|
||||||
|
|
||||||
/// Explains why a direct MJPEG frame was frozen before UVC handoff.
|
/// Explains why a direct MJPEG frame was frozen before UVC handoff.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@ -191,10 +191,9 @@ pub(super) fn direct_mjpeg_reject_profile_mismatch_enabled() -> bool {
|
|||||||
/// Decide whether direct MJPEG should be normalized before UVC spool.
|
/// Decide whether direct MJPEG should be normalized before UVC spool.
|
||||||
///
|
///
|
||||||
/// Inputs: optional `LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE`. Output: true unless
|
/// Inputs: optional `LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE`. Output: true unless
|
||||||
/// explicitly enabled. Why: this path runs native GStreamer JPEG decode/encode
|
/// explicitly disabled. Why: direct MJPEG camera frames can still stress the
|
||||||
/// inside `lesavka-server`; it is useful for lab comparisons, but field
|
/// high-speed isochronous UVC link; normalizing them to a simpler, smaller
|
||||||
/// evidence showed long-running RSS growth from the native allocator/buffer
|
/// JPEG bitstream before the gadget is safer than trusting passthrough bytes.
|
||||||
/// pools, so guarded passthrough is the production-safe default.
|
|
||||||
pub(super) fn direct_mjpeg_normalize_enabled() -> bool {
|
pub(super) fn direct_mjpeg_normalize_enabled() -> bool {
|
||||||
std::env::var("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE")
|
std::env::var("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE")
|
||||||
.ok()
|
.ok()
|
||||||
|
|||||||
@ -14,7 +14,7 @@ fn hevc_jpeg_quality_defaults_to_moderate_transport_pressure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn direct_mjpeg_normalization_defaults_off_and_clamps_tuning() {
|
fn direct_mjpeg_normalization_defaults_on_and_clamps_tuning() {
|
||||||
temp_env::with_vars(
|
temp_env::with_vars(
|
||||||
[
|
[
|
||||||
("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE", None::<&str>),
|
("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE", None::<&str>),
|
||||||
@ -33,13 +33,13 @@ fn direct_mjpeg_normalization_defaults_off_and_clamps_tuning() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
assert!(!super::direct_mjpeg_normalize_enabled());
|
assert!(super::direct_mjpeg_normalize_enabled());
|
||||||
assert_eq!(super::direct_mjpeg_jpeg_quality(), 72);
|
assert_eq!(super::direct_mjpeg_jpeg_quality(), 60);
|
||||||
assert_eq!(super::direct_mjpeg_normalize_pull_timeout_ms(), 25);
|
assert_eq!(super::direct_mjpeg_normalize_pull_timeout_ms(), 25);
|
||||||
assert_eq!(super::direct_mjpeg_normalize_miss_limit(), 30);
|
assert_eq!(super::direct_mjpeg_normalize_miss_limit(), 30);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
super::direct_mjpeg_normalize_rss_limit_kb(),
|
super::direct_mjpeg_normalize_rss_limit_kb(),
|
||||||
Some(768 * 1024)
|
Some(384 * 1024)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,7 +13,7 @@ mod audit;
|
|||||||
static SPOOL_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
static SPOOL_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
||||||
static SPOOL_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
static SPOOL_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
||||||
const MAX_MJPEG_FRAME_BYTES: usize = 8 * 1024 * 1024;
|
const MAX_MJPEG_FRAME_BYTES: usize = 8 * 1024 * 1024;
|
||||||
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 10_000_000;
|
const DEFAULT_UVC_MJPEG_BUDGET_BYTES_PER_SEC: u32 = 4_500_000;
|
||||||
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
const DEFAULT_UVC_ISOCHRONOUS_LIMIT_PCT: u32 = 85;
|
||||||
const HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC: u32 = 8_000;
|
const HIGH_SPEED_ISOCHRONOUS_MICROFRAMES_PER_SEC: u32 = 8_000;
|
||||||
const CONFIGFS_UVC_BASE: &str = "/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0";
|
const CONFIGFS_UVC_BASE: &str = "/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0";
|
||||||
@ -377,8 +377,8 @@ pub(super) fn spool_mjpeg_frame_with_timing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(dir), Some(sequence)) = (audit_dir, sequence)
|
if let (Some(dir), Some(sequence)) = (audit_dir, sequence)
|
||||||
&& audit::claim_spool_audit_frame(sequence)
|
&& let Some(slot) = audit::claim_spool_audit_frame(sequence)
|
||||||
&& let Err(err) = audit::write_mjpeg_spool_audit_frame(&dir, sequence, data, timing)
|
&& let Err(err) = audit::write_mjpeg_spool_audit_frame(&dir, sequence, slot, data, timing)
|
||||||
{
|
{
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "lesavka_server::video",
|
target: "lesavka_server::video",
|
||||||
|
|||||||
@ -10,9 +10,15 @@ static AUDIT_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
|||||||
static AUDIT_SAVED_FRAMES: AtomicU64 = AtomicU64::new(0);
|
static AUDIT_SAVED_FRAMES: AtomicU64 = AtomicU64::new(0);
|
||||||
const DEFAULT_UVC_FRAME_AUDIT_EVERY: u32 = 1;
|
const DEFAULT_UVC_FRAME_AUDIT_EVERY: u32 = 1;
|
||||||
const DEFAULT_UVC_FRAME_AUDIT_MAX_FRAMES: u32 = 1800;
|
const DEFAULT_UVC_FRAME_AUDIT_MAX_FRAMES: u32 = 1800;
|
||||||
|
const DEFAULT_UVC_FRAME_AUDIT_ROLLING: bool = true;
|
||||||
const FNV1A64_OFFSET: u64 = 0xcbf29ce484222325;
|
const FNV1A64_OFFSET: u64 = 0xcbf29ce484222325;
|
||||||
const FNV1A64_PRIME: u64 = 0x100000001b3;
|
const FNV1A64_PRIME: u64 = 0x100000001b3;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(super) fn reset_spool_audit_counter_for_test() {
|
||||||
|
AUDIT_SAVED_FRAMES.store(0, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an optional unsigned tuning value.
|
/// Parse an optional unsigned tuning value.
|
||||||
///
|
///
|
||||||
/// Inputs: environment variable name. Output: parsed `u32` when present.
|
/// Inputs: environment variable name. Output: parsed `u32` when present.
|
||||||
@ -90,6 +96,24 @@ pub(super) fn mjpeg_spool_audit_max_frames() -> u64 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve whether bounded audits keep the latest frames instead of stopping.
|
||||||
|
///
|
||||||
|
/// Inputs: `LESAVKA_UVC_FRAME_AUDIT_ROLLING`. Output: true unless explicitly
|
||||||
|
/// disabled. Why: long live-debug sessions need the frames around the latest
|
||||||
|
/// recording, not stale startup evidence from hours earlier.
|
||||||
|
pub(super) fn mjpeg_spool_audit_rolling_enabled() -> bool {
|
||||||
|
std::env::var("LESAVKA_UVC_FRAME_AUDIT_ROLLING")
|
||||||
|
.ok()
|
||||||
|
.map(|value| {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
!(trimmed.eq_ignore_ascii_case("0")
|
||||||
|
|| trimmed.eq_ignore_ascii_case("false")
|
||||||
|
|| trimmed.eq_ignore_ascii_case("no")
|
||||||
|
|| trimmed.eq_ignore_ascii_case("off"))
|
||||||
|
})
|
||||||
|
.unwrap_or(DEFAULT_UVC_FRAME_AUDIT_ROLLING)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve the audit JSONL path.
|
/// Resolve the audit JSONL path.
|
||||||
///
|
///
|
||||||
/// Inputs: audit directory plus `LESAVKA_UVC_FRAME_AUDIT_LOG_PATH`. Output:
|
/// Inputs: audit directory plus `LESAVKA_UVC_FRAME_AUDIT_LOG_PATH`. Output:
|
||||||
@ -126,17 +150,24 @@ pub(super) fn should_sample_spool_audit_frame(sequence: u64, every: u64) -> bool
|
|||||||
offset.is_multiple_of(every.max(1))
|
offset.is_multiple_of(every.max(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decide whether the current spooled frame should be preserved.
|
/// Decide where the current spooled frame should be preserved.
|
||||||
///
|
///
|
||||||
/// Inputs: global spool sequence. Output: true if sampling and max-frame budget
|
/// Inputs: global spool sequence. Output: the bounded audit file slot, if
|
||||||
/// allow a save. Why: the max cap must count saved audit frames, not every
|
/// sampling and retention allow a save. Why: rolling audits must stay disk
|
||||||
/// frame the long-running server emitted before the audit started.
|
/// bounded while still keeping the frames nearest the user's recording.
|
||||||
pub(super) fn claim_spool_audit_frame(sequence: u64) -> bool {
|
pub(super) fn claim_spool_audit_frame(sequence: u64) -> Option<u64> {
|
||||||
if !should_sample_spool_audit_frame(sequence, mjpeg_spool_audit_every()) {
|
if !should_sample_spool_audit_frame(sequence, mjpeg_spool_audit_every()) {
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
let max_frames = mjpeg_spool_audit_max_frames();
|
let max_frames = mjpeg_spool_audit_max_frames();
|
||||||
max_frames == 0 || AUDIT_SAVED_FRAMES.fetch_add(1, Ordering::Relaxed) < max_frames
|
let saved = AUDIT_SAVED_FRAMES.fetch_add(1, Ordering::Relaxed);
|
||||||
|
if max_frames == 0 {
|
||||||
|
return Some(sequence);
|
||||||
|
}
|
||||||
|
if mjpeg_spool_audit_rolling_enabled() {
|
||||||
|
return Some(saved % max_frames + 1);
|
||||||
|
}
|
||||||
|
(saved < max_frames).then_some(sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format one exact-frame audit index record.
|
/// Format one exact-frame audit index record.
|
||||||
@ -146,6 +177,7 @@ pub(super) fn claim_spool_audit_frame(sequence: u64) -> bool {
|
|||||||
/// RCT frame with the server-boundary payload that preceded it.
|
/// RCT frame with the server-boundary payload that preceded it.
|
||||||
fn format_audit_record(
|
fn format_audit_record(
|
||||||
sequence: u64,
|
sequence: u64,
|
||||||
|
slot: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
timing: Option<MjpegSpoolTiming>,
|
timing: Option<MjpegSpoolTiming>,
|
||||||
frame_file: &str,
|
frame_file: &str,
|
||||||
@ -154,8 +186,9 @@ fn format_audit_record(
|
|||||||
let source_pts_us = timing.and_then(|value| value.source_pts_us);
|
let source_pts_us = timing.and_then(|value| value.source_pts_us);
|
||||||
let decoded_pts_us = timing.and_then(|value| value.decoded_pts_us);
|
let decoded_pts_us = timing.and_then(|value| value.decoded_pts_us);
|
||||||
format!(
|
format!(
|
||||||
"{{\"schema\":\"lesavka.uvc-mjpeg-spool-audit.v1\",\"sequence\":{},\"profile\":\"{}\",\"bytes\":{},\"source_pts_us\":{},\"decoded_pts_us\":{},\"spool_unix_ns\":{},\"fnv1a64\":\"{}\",\"file\":\"{}\"}}\n",
|
"{{\"schema\":\"lesavka.uvc-mjpeg-spool-audit.v1\",\"sequence\":{},\"slot\":{},\"profile\":\"{}\",\"bytes\":{},\"source_pts_us\":{},\"decoded_pts_us\":{},\"spool_unix_ns\":{},\"fnv1a64\":\"{}\",\"file\":\"{}\"}}\n",
|
||||||
sequence,
|
sequence,
|
||||||
|
slot,
|
||||||
json_escape(profile),
|
json_escape(profile),
|
||||||
data.len(),
|
data.len(),
|
||||||
json_number_or_null(source_pts_us),
|
json_number_or_null(source_pts_us),
|
||||||
@ -174,11 +207,12 @@ fn format_audit_record(
|
|||||||
pub(super) fn write_mjpeg_spool_audit_frame(
|
pub(super) fn write_mjpeg_spool_audit_frame(
|
||||||
dir: &Path,
|
dir: &Path,
|
||||||
sequence: u64,
|
sequence: u64,
|
||||||
|
slot: u64,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
timing: Option<MjpegSpoolTiming>,
|
timing: Option<MjpegSpoolTiming>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
fs::create_dir_all(dir)?;
|
fs::create_dir_all(dir)?;
|
||||||
let frame_file = format!("frame-{sequence:012}.mjpg");
|
let frame_file = format!("frame-{slot:012}.mjpg");
|
||||||
let tmp = dir.join(format!(
|
let tmp = dir.join(format!(
|
||||||
".{frame_file}.{}.{}.tmp",
|
".{frame_file}.{}.{}.tmp",
|
||||||
std::process::id(),
|
std::process::id(),
|
||||||
@ -187,6 +221,6 @@ pub(super) fn write_mjpeg_spool_audit_frame(
|
|||||||
fs::write(&tmp, data)?;
|
fs::write(&tmp, data)?;
|
||||||
fs::rename(&tmp, dir.join(&frame_file))?;
|
fs::rename(&tmp, dir.join(&frame_file))?;
|
||||||
|
|
||||||
let record = format_audit_record(sequence, data, timing, &frame_file);
|
let record = format_audit_record(sequence, slot, data, timing, &frame_file);
|
||||||
append_record(&mjpeg_spool_audit_log_path(dir), &record)
|
append_record(&mjpeg_spool_audit_log_path(dir), &record)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ fn mjpeg_spool_frame_budget_uses_live_budget_when_zero_or_unset() {
|
|||||||
("LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC", None::<&str>),
|
("LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC", None::<&str>),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
assert_eq!(super::mjpeg_spool_frame_max_bytes(30), 333_333);
|
assert_eq!(super::mjpeg_spool_frame_max_bytes(30), 150_000);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -151,11 +151,13 @@ fn mjpeg_spool_audit_knobs_are_opt_in_and_bounded() {
|
|||||||
("LESAVKA_UVC_FRAME_AUDIT_DIR", None::<&str>),
|
("LESAVKA_UVC_FRAME_AUDIT_DIR", None::<&str>),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_EVERY", None::<&str>),
|
("LESAVKA_UVC_FRAME_AUDIT_EVERY", None::<&str>),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", None::<&str>),
|
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", None::<&str>),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_ROLLING", None::<&str>),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
assert_eq!(super::audit::mjpeg_spool_audit_dir(), None);
|
assert_eq!(super::audit::mjpeg_spool_audit_dir(), None);
|
||||||
assert_eq!(super::audit::mjpeg_spool_audit_every(), 1);
|
assert_eq!(super::audit::mjpeg_spool_audit_every(), 1);
|
||||||
assert_eq!(super::audit::mjpeg_spool_audit_max_frames(), 1800);
|
assert_eq!(super::audit::mjpeg_spool_audit_max_frames(), 1800);
|
||||||
|
assert!(super::audit::mjpeg_spool_audit_rolling_enabled());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -164,6 +166,7 @@ fn mjpeg_spool_audit_knobs_are_opt_in_and_bounded() {
|
|||||||
("LESAVKA_UVC_FRAME_AUDIT_DIR", Some("/tmp/uvc-audit")),
|
("LESAVKA_UVC_FRAME_AUDIT_DIR", Some("/tmp/uvc-audit")),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_EVERY", Some("0")),
|
("LESAVKA_UVC_FRAME_AUDIT_EVERY", Some("0")),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", Some("0")),
|
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", Some("0")),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_ROLLING", Some("off")),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -172,6 +175,7 @@ fn mjpeg_spool_audit_knobs_are_opt_in_and_bounded() {
|
|||||||
);
|
);
|
||||||
assert_eq!(super::audit::mjpeg_spool_audit_every(), 1);
|
assert_eq!(super::audit::mjpeg_spool_audit_every(), 1);
|
||||||
assert_eq!(super::audit::mjpeg_spool_audit_max_frames(), 0);
|
assert_eq!(super::audit::mjpeg_spool_audit_max_frames(), 0);
|
||||||
|
assert!(!super::audit::mjpeg_spool_audit_rolling_enabled());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -324,7 +328,9 @@ fn spool_mjpeg_frame_writes_enabled_sidecar_with_timing() {
|
|||||||
/// JSONL timing/hash record. Why: this is the evidence needed to decide
|
/// JSONL timing/hash record. Why: this is the evidence needed to decide
|
||||||
/// whether corruption exists before or after the server-to-UVC boundary.
|
/// whether corruption exists before or after the server-to-UVC boundary.
|
||||||
#[test]
|
#[test]
|
||||||
|
#[serial_test::serial(audit)]
|
||||||
fn spool_mjpeg_frame_writes_enabled_boundary_audit() {
|
fn spool_mjpeg_frame_writes_enabled_boundary_audit() {
|
||||||
|
super::audit::reset_spool_audit_counter_for_test();
|
||||||
let dir = tempfile::tempdir().expect("tempdir");
|
let dir = tempfile::tempdir().expect("tempdir");
|
||||||
let frame = dir.path().join("frame.mjpg");
|
let frame = dir.path().join("frame.mjpg");
|
||||||
let audit = dir.path().join("audit");
|
let audit = dir.path().join("audit");
|
||||||
@ -338,6 +344,7 @@ fn spool_mjpeg_frame_writes_enabled_boundary_audit() {
|
|||||||
),
|
),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_EVERY", Some("1")),
|
("LESAVKA_UVC_FRAME_AUDIT_EVERY", Some("1")),
|
||||||
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", Some("1")),
|
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", Some("1")),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_ROLLING", Some("0")),
|
||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
super::spool_mjpeg_frame_with_timing(
|
super::spool_mjpeg_frame_with_timing(
|
||||||
@ -372,3 +379,61 @@ fn spool_mjpeg_frame_writes_enabled_boundary_audit() {
|
|||||||
assert!(index.contains("\"source_pts_us\":222"));
|
assert!(index.contains("\"source_pts_us\":222"));
|
||||||
assert!(index.contains("\"file\":\"frame-"));
|
assert!(index.contains("\"file\":\"frame-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verifies rolling audit keeps the latest bounded frame evidence.
|
||||||
|
///
|
||||||
|
/// Input: a one-frame rolling audit and two spooled MJPEG payloads. Output:
|
||||||
|
/// one overwritten frame file plus two index rows with distinct source
|
||||||
|
/// sequences. Why: long live repro sessions must not leave only stale startup
|
||||||
|
/// frames when the user records a later artifact.
|
||||||
|
#[test]
|
||||||
|
#[serial_test::serial(audit)]
|
||||||
|
fn spool_mjpeg_frame_rolls_enabled_boundary_audit() {
|
||||||
|
super::audit::reset_spool_audit_counter_for_test();
|
||||||
|
let dir = tempfile::tempdir().expect("tempdir");
|
||||||
|
let frame = dir.path().join("frame.mjpg");
|
||||||
|
let audit = dir.path().join("audit");
|
||||||
|
|
||||||
|
temp_env::with_vars(
|
||||||
|
[
|
||||||
|
("LESAVKA_UVC_FRAME_META", None::<&str>),
|
||||||
|
(
|
||||||
|
"LESAVKA_UVC_FRAME_AUDIT_DIR",
|
||||||
|
Some(audit.to_str().expect("utf8 path")),
|
||||||
|
),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_EVERY", Some("1")),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_MAX_FRAMES", Some("1")),
|
||||||
|
("LESAVKA_UVC_FRAME_AUDIT_ROLLING", Some("1")),
|
||||||
|
],
|
||||||
|
|| {
|
||||||
|
super::spool_mjpeg_frame_with_timing(
|
||||||
|
&frame,
|
||||||
|
b"first-jpeg",
|
||||||
|
Some(super::MjpegSpoolTiming::mjpeg_passthrough(111)),
|
||||||
|
)
|
||||||
|
.expect("spool first audited frame");
|
||||||
|
super::spool_mjpeg_frame_with_timing(
|
||||||
|
&frame,
|
||||||
|
b"latest-jpeg",
|
||||||
|
Some(super::MjpegSpoolTiming::mjpeg_passthrough(222)),
|
||||||
|
)
|
||||||
|
.expect("spool rolling audited frame");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let audited_frames = std::fs::read_dir(&audit)
|
||||||
|
.expect("audit dir")
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter(|entry| entry.file_name().to_string_lossy().ends_with(".mjpg"))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(audited_frames.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
std::fs::read(audited_frames[0].path()).expect("audit frame"),
|
||||||
|
b"latest-jpeg"
|
||||||
|
);
|
||||||
|
let index = std::fs::read_to_string(audit.join("spool-audit.jsonl")).expect("audit index");
|
||||||
|
assert_eq!(index.lines().count(), 2);
|
||||||
|
assert!(index.contains("\"slot\":1"));
|
||||||
|
assert!(index.contains("\"source_pts_us\":111"));
|
||||||
|
assert!(index.contains("\"source_pts_us\":222"));
|
||||||
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ fn core_script_keeps_uvc_output_on_supported_mjpeg_descriptor() {
|
|||||||
"apply_uvc_payload_limits",
|
"apply_uvc_payload_limits",
|
||||||
"UVC_MAXPACKET=${LESAVKA_UVC_MAXPACKET:-1024}",
|
"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:-4500000}",
|
||||||
"UVC_ISOCHRONOUS_LIMIT_PCT=${LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT:-85}",
|
"UVC_ISOCHRONOUS_LIMIT_PCT=${LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT:-85}",
|
||||||
"uvc_isochronous_budget_bytes_per_sec()",
|
"uvc_isochronous_budget_bytes_per_sec()",
|
||||||
"isoch_budget=\"$(uvc_isochronous_budget_bytes_per_sec)\"",
|
"isoch_budget=\"$(uvc_isochronous_budget_bytes_per_sec)\"",
|
||||||
|
|||||||
@ -192,11 +192,11 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS:-20}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS:-20}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS:-2}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_FRESHNESS_QUEUE_BUFFERS:-2}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT:-15}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEVC_DECODE_MISS_LIMIT:-15}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-0}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-1}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY:-72}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_JPEG_QUALITY:-60}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS:-25}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_PULL_TIMEOUT_MS:-25}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT:-30}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_MISS_LIMIT:-30}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-768}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-384}"));
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("lesavka_server::video=info"),
|
SERVER_INSTALL.contains("lesavka_server::video=info"),
|
||||||
"server installs should not leave the hot webcam frame path at debug logging by default"
|
"server installs should not leave the hot webcam frame path at debug logging by default"
|
||||||
|
|||||||
@ -76,7 +76,7 @@ fn assert_ordered(haystack: &str, earlier: &str, later: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn direct_mjpeg_normalizer_is_opt_in_after_native_rss_leak() {
|
fn direct_mjpeg_normalizer_defaults_on_with_rss_fuse_after_native_rss_leak() {
|
||||||
temp_env::with_vars(
|
temp_env::with_vars(
|
||||||
[
|
[
|
||||||
("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE", None::<&str>),
|
("LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE", None::<&str>),
|
||||||
@ -87,10 +87,10 @@ fn direct_mjpeg_normalizer_is_opt_in_after_native_rss_leak() {
|
|||||||
],
|
],
|
||||||
|| {
|
|| {
|
||||||
assert!(
|
assert!(
|
||||||
!guard::normalizer_enabled(),
|
guard::normalizer_enabled(),
|
||||||
"direct MJPEG normalization must stay opt-in after the native RSS leak"
|
"direct MJPEG normalization should be active but RSS-fused while chasing UVC artifacts"
|
||||||
);
|
);
|
||||||
assert_eq!(guard::normalizer_rss_limit_kb(), Some(768 * 1024));
|
assert_eq!(guard::normalizer_rss_limit_kb(), Some(384 * 1024));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -107,10 +107,10 @@ fn direct_mjpeg_normalizer_is_opt_in_after_native_rss_leak() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn installer_keeps_the_leaking_native_normalizer_disabled_by_default() {
|
fn installer_keeps_the_native_normalizer_memory_bounded_by_default() {
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-0}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-1}"));
|
||||||
assert!(!SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-1}"));
|
assert!(!SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE:-0}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-768}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_DIRECT_MJPEG_NORMALIZE_RSS_LIMIT_MB:-384}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user