fix: preserve UVC format compatibility
This commit is contained in:
parent
4dea0407b8
commit
ffd6a08749
13
AGENTS.md
13
AGENTS.md
@ -674,3 +674,16 @@ downstream appsrc dropping.
|
||||
- [x] Stop default downstream appsrc leaking on the UAC speech path; shredded chunks are worse than modest added latency for calls.
|
||||
- [ ] Reinstall/restart Theia services so `/etc/lesavka/uvc.env` is refreshed from `640x480 @ 20fps` to `1280x720 @ 30fps`.
|
||||
- [ ] Re-run manual Google Meet before trusting mirrored probe calibration; verify speech is intelligible and video cadence is stable by eye.
|
||||
|
||||
## 0.17.37 UVC Format Compatibility Checklist
|
||||
|
||||
Context: after 0.17.36, Google Meet showed `Video Format Not Supported`. The client correctly
|
||||
captured the UI-selected `720p@30` profile, but it emitted those frames into a server UVC gadget still
|
||||
advertising `640x480 @ 20fps`. USB camera consumers require advertised caps and frame payloads to
|
||||
match; otherwise the feed is rejected before we can evaluate smoothness or sync.
|
||||
|
||||
- [x] Preserve UI-selected capture quality as the source capture profile.
|
||||
- [x] Restore safe default UVC emission to the negotiated server gadget profile so browsers see frames matching the camera format they negotiated.
|
||||
- [x] Keep `LESAVKA_CAM_EMIT_UI_PROFILE=1` as an explicit lab-only opt-in until the server can reconfigure the UVC gadget from the UI/session profile.
|
||||
- [x] Keep `LESAVKA_CAM_LOCK_TO_SERVER_PROFILE=1` as a safety override that wins over experimental UI-profile emission.
|
||||
- [ ] Add a real server-side UVC profile reconfigure path before making UI-selected quality drive the gadget-advertised output format.
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_client"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1686,7 +1686,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_common"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@ -1698,7 +1698,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_server"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
||||
|
||||
[package]
|
||||
name = "lesavka_client"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -237,7 +237,7 @@ impl LesavkaClientApp {
|
||||
width = cfg.width,
|
||||
height = cfg.height,
|
||||
fps = cfg.fps,
|
||||
"📸 using server camera caps as codec/fallback; launcher camera quality remains authoritative"
|
||||
"📸 using negotiated server UVC caps for emitted format; launcher quality still controls local capture"
|
||||
);
|
||||
}
|
||||
let ep = vid_ep.clone();
|
||||
|
||||
@ -83,6 +83,7 @@ mod tests {
|
||||
("LESAVKA_CAM_HEIGHT", Some("720")),
|
||||
("LESAVKA_CAM_FPS", Some("30")),
|
||||
("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
|
||||
("LESAVKA_CAM_EMIT_UI_PROFILE", None),
|
||||
],
|
||||
|| assert_eq!(resolved_capture_profile(Some(cfg)), (1280, 720, 30)),
|
||||
);
|
||||
@ -90,8 +91,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
/// UI-selected launcher quality is the source of truth for the camera uplink.
|
||||
fn negotiated_output_profile_follows_launcher_quality_by_default() {
|
||||
/// UVC output must match the gadget profile that browsers negotiate.
|
||||
fn negotiated_output_profile_matches_server_uvc_contract_by_default() {
|
||||
let cfg = CameraConfig {
|
||||
codec: CameraCodec::Mjpeg,
|
||||
width: 640,
|
||||
@ -104,6 +105,36 @@ mod tests {
|
||||
("LESAVKA_CAM_HEIGHT", Some("720")),
|
||||
("LESAVKA_CAM_FPS", Some("30")),
|
||||
("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
|
||||
("LESAVKA_CAM_EMIT_UI_PROFILE", None),
|
||||
],
|
||||
|| {
|
||||
let capture_profile = resolved_capture_profile(Some(cfg));
|
||||
assert_eq!(capture_profile, (1280, 720, 30));
|
||||
assert_eq!(
|
||||
resolved_output_profile(Some(cfg), capture_profile),
|
||||
(640, 480, 20)
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
/// Keeps UI-profile emission explicit until the server can reconfigure UVC.
|
||||
fn explicit_ui_profile_emission_keeps_lab_mode_available() {
|
||||
let cfg = CameraConfig {
|
||||
codec: CameraCodec::Mjpeg,
|
||||
width: 640,
|
||||
height: 480,
|
||||
fps: 20,
|
||||
};
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("LESAVKA_CAM_WIDTH", Some("1280")),
|
||||
("LESAVKA_CAM_HEIGHT", Some("720")),
|
||||
("LESAVKA_CAM_FPS", Some("30")),
|
||||
("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", None),
|
||||
("LESAVKA_CAM_EMIT_UI_PROFILE", Some("1")),
|
||||
],
|
||||
|| {
|
||||
let capture_profile = resolved_capture_profile(Some(cfg));
|
||||
@ -118,8 +149,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
/// Keeps the explicit lab lock available for controlled gadget debugging.
|
||||
fn explicit_server_profile_lock_keeps_lab_mode_available() {
|
||||
/// The safety lock wins if both experimental flags are set.
|
||||
fn explicit_server_profile_lock_wins_over_ui_emission() {
|
||||
let cfg = CameraConfig {
|
||||
codec: CameraCodec::Mjpeg,
|
||||
width: 640,
|
||||
@ -132,6 +163,7 @@ mod tests {
|
||||
("LESAVKA_CAM_HEIGHT", Some("720")),
|
||||
("LESAVKA_CAM_FPS", Some("30")),
|
||||
("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE", Some("1")),
|
||||
("LESAVKA_CAM_EMIT_UI_PROFILE", Some("1")),
|
||||
],
|
||||
|| {
|
||||
let capture_profile = resolved_capture_profile(Some(cfg));
|
||||
|
||||
@ -291,7 +291,10 @@ fn resolved_output_profile(
|
||||
capture_profile: (u32, u32, u32),
|
||||
) -> (u32, u32, u32) {
|
||||
match cfg {
|
||||
Some(cfg) if env_flag_enabled("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE") => {
|
||||
Some(cfg)
|
||||
if env_flag_enabled("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE")
|
||||
|| !env_flag_enabled("LESAVKA_CAM_EMIT_UI_PROFILE") =>
|
||||
{
|
||||
(cfg.width, cfg.height, cfg.fps.max(1))
|
||||
}
|
||||
_ => capture_profile,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lesavka_common"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ bench = false
|
||||
|
||||
[package]
|
||||
name = "lesavka_server"
|
||||
version = "0.17.36"
|
||||
version = "0.17.37"
|
||||
edition = "2024"
|
||||
autobins = false
|
||||
|
||||
|
||||
@ -171,6 +171,7 @@ fn launcher_webcam_quality_selection_reaches_preview_and_relay_env() {
|
||||
assert!(DEVICE_TEST_SRC.contains("capsfilter caps=\\\"video/x-raw"));
|
||||
assert!(CAMERA_SRC.contains("fn resolved_capture_profile"));
|
||||
assert!(CAMERA_SRC.contains("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE"));
|
||||
assert!(CAMERA_SRC.contains("LESAVKA_CAM_EMIT_UI_PROFILE"));
|
||||
assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_CAM_WIDTH\""));
|
||||
assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_CAM_H264_KBIT\""));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user