launcher: polish relay controls
This commit is contained in:
parent
fb466abdfd
commit
da7a49bc8c
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_client"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1686,7 +1686,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_common"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@ -1698,7 +1698,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_server"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
||||
|
||||
[package]
|
||||
name = "lesavka_client"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -49,6 +49,11 @@ pub fn next_delay(current: Duration) -> Duration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the camera codec string used by launcher and server negotiation.
|
||||
///
|
||||
/// Inputs: operator/env codec text. Output: the supported transport codec when
|
||||
/// recognized. Why: the client must not silently fall back to a differently
|
||||
/// calibrated upstream path when the UI asks for HEVC or MJPEG.
|
||||
fn parse_camera_codec(raw: &str) -> Option<CameraCodec> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"mjpeg" | "mjpg" | "jpeg" => Some(CameraCodec::Mjpeg),
|
||||
|
||||
@ -27,8 +27,9 @@ impl InputRouting {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum WebcamTransport {
|
||||
#[default]
|
||||
Hevc,
|
||||
Mjpeg,
|
||||
}
|
||||
@ -81,12 +82,6 @@ impl WebcamTransport {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WebcamTransport {
|
||||
fn default() -> Self {
|
||||
Self::Hevc
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ViewMode {
|
||||
Unified,
|
||||
|
||||
@ -235,6 +235,51 @@ fn populated_launcher_runtime_widgets_stay_compact() {
|
||||
);
|
||||
}
|
||||
|
||||
#[gtk::test]
|
||||
#[serial]
|
||||
fn relay_connect_button_aligns_with_recovery_video_column() {
|
||||
if gtk::gdk::Display::default().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let app = gtk::Application::builder()
|
||||
.application_id("dev.lesavka.test-relay-row-alignment")
|
||||
.build();
|
||||
let _ = app.register(None::<>k::gio::Cancellable>);
|
||||
|
||||
let view = build_launcher_view(
|
||||
&app,
|
||||
"https://38.28.125.112:50051",
|
||||
&realistic_device_catalog(),
|
||||
&LauncherState::new(),
|
||||
);
|
||||
present_and_settle(&view.window);
|
||||
|
||||
let connect_bounds = view
|
||||
.widgets
|
||||
.start_button
|
||||
.compute_bounds(&view.window)
|
||||
.expect("connect button bounds");
|
||||
let video_bounds = view
|
||||
.widgets
|
||||
.uvc_recover_button
|
||||
.compute_bounds(&view.window)
|
||||
.expect("video recovery button bounds");
|
||||
|
||||
assert!(
|
||||
(connect_bounds.x() - video_bounds.x()).abs() <= 8.0,
|
||||
"Connect should visually align with the Video recovery column: connect_x={} video_x={}",
|
||||
connect_bounds.x(),
|
||||
video_bounds.x()
|
||||
);
|
||||
assert!(
|
||||
connect_bounds.width() >= video_bounds.width() - 2.0,
|
||||
"Connect should be at least as stable as the Video recovery button: connect_w={} video_w={}",
|
||||
connect_bounds.width(),
|
||||
video_bounds.width()
|
||||
);
|
||||
}
|
||||
|
||||
#[gtk::test]
|
||||
#[serial]
|
||||
fn launcher_shell_installs_native_window_chrome() {
|
||||
|
||||
@ -458,45 +458,7 @@
|
||||
next_diagnostics_probe.set(now + Duration::from_secs(2));
|
||||
}
|
||||
|
||||
if now >= next_diagnostics_sample.get() {
|
||||
let network = diagnostics_network.borrow_mut().snapshot();
|
||||
let uplink =
|
||||
crate::uplink_telemetry::load_uplink_telemetry(&uplink_telemetry_path);
|
||||
let client_process_cpu_pct = diagnostics_process
|
||||
.borrow_mut()
|
||||
.sample_percent()
|
||||
.unwrap_or(0.0);
|
||||
record_diagnostics_sample(
|
||||
&widgets,
|
||||
&state.borrow(),
|
||||
preview.as_ref().map(|preview| preview.as_ref()),
|
||||
uplink.as_ref(),
|
||||
network,
|
||||
client_process_cpu_pct,
|
||||
);
|
||||
next_diagnostics_sample.set(now + Duration::from_secs(1));
|
||||
}
|
||||
|
||||
let (camera_probe_active, camera_label, mic_probe_active) = {
|
||||
let state = state.borrow();
|
||||
(
|
||||
state.channels.camera && state.devices.camera.is_some(),
|
||||
state.devices.camera.clone(),
|
||||
state.channels.microphone && state.devices.microphone.is_some(),
|
||||
)
|
||||
};
|
||||
if let Err(err) = tests.borrow_mut().sync_relay_uplink_probe(
|
||||
child_running,
|
||||
camera_probe_active,
|
||||
camera_label.as_deref(),
|
||||
&camera_preview_path,
|
||||
mic_probe_active,
|
||||
&mic_level_path,
|
||||
) {
|
||||
widgets
|
||||
.status_label
|
||||
.set_text(&format!("Local uplink monitor could not start: {err}"));
|
||||
}
|
||||
include!("runtime_poll/runtime_monitor_tick.rs");
|
||||
|
||||
refresh_launcher_ui(&widgets, &state.borrow(), child_running);
|
||||
if child_running
|
||||
|
||||
40
client/src/launcher/ui/runtime_poll/runtime_monitor_tick.rs
Normal file
40
client/src/launcher/ui/runtime_poll/runtime_monitor_tick.rs
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
if now >= next_diagnostics_sample.get() {
|
||||
let network = diagnostics_network.borrow_mut().snapshot();
|
||||
let uplink = crate::uplink_telemetry::load_uplink_telemetry(&uplink_telemetry_path);
|
||||
let client_process_cpu_pct = diagnostics_process
|
||||
.borrow_mut()
|
||||
.sample_percent()
|
||||
.unwrap_or(0.0);
|
||||
record_diagnostics_sample(
|
||||
&widgets,
|
||||
&state.borrow(),
|
||||
preview.as_ref().map(|preview| preview.as_ref()),
|
||||
uplink.as_ref(),
|
||||
network,
|
||||
client_process_cpu_pct,
|
||||
);
|
||||
next_diagnostics_sample.set(now + Duration::from_secs(1));
|
||||
}
|
||||
|
||||
let (camera_probe_active, camera_label, mic_probe_active) = {
|
||||
let state = state.borrow();
|
||||
(
|
||||
state.channels.camera && state.devices.camera.is_some(),
|
||||
state.devices.camera.clone(),
|
||||
state.channels.microphone && state.devices.microphone.is_some(),
|
||||
)
|
||||
};
|
||||
if let Err(err) = tests.borrow_mut().sync_relay_uplink_probe(
|
||||
child_running,
|
||||
camera_probe_active,
|
||||
camera_label.as_deref(),
|
||||
&camera_preview_path,
|
||||
mic_probe_active,
|
||||
&mic_level_path,
|
||||
) {
|
||||
widgets
|
||||
.status_label
|
||||
.set_text(&format!("Local uplink monitor could not start: {err}"));
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@
|
||||
));
|
||||
let relay_grid = gtk::Grid::new();
|
||||
relay_grid.set_column_homogeneous(false);
|
||||
relay_grid.set_column_spacing(6);
|
||||
relay_grid.set_column_spacing(8);
|
||||
relay_grid.set_hexpand(true);
|
||||
relay_grid.set_row_spacing(8);
|
||||
relay_grid.attach(&server_entry, 0, 0, 1, 1);
|
||||
@ -23,7 +23,6 @@
|
||||
let start_button = rail_button("Connect", "Start or stop relay.");
|
||||
start_button.add_css_class("suggested-action");
|
||||
start_button.set_hexpand(false);
|
||||
stabilize_button(&start_button, 76);
|
||||
relay_grid.attach(&start_button, 2, 0, 1, 1);
|
||||
|
||||
connection_body.append(&relay_grid);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lesavka_common"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
||||
| `LESAVKA_CAM_FPS` | client media capture/playback override |
|
||||
| `LESAVKA_CAM_H264_KBIT` | client media capture/playback override |
|
||||
| `LESAVKA_CAM_HEIGHT` | client media capture/playback override |
|
||||
| `LESAVKA_CAM_HEVC_KEYFRAME_INTERVAL` | client HEVC upstream keyframe cadence in frames; defaults to `1` so freshness-first drops freeze/stutter instead of producing predictive-frame corruption |
|
||||
| `LESAVKA_CAM_JPEG_QUALITY` | client media capture/playback override |
|
||||
| `LESAVKA_CAM_KEYFRAME_INTERVAL` | client media capture/playback override |
|
||||
| `LESAVKA_CAM_MJPG` | client media capture/playback override |
|
||||
@ -312,6 +313,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
||||
| `LESAVKA_UVC_FRAME_META_LOG_PATH` | UVC helper diagnostic override; when set with `LESAVKA_UVC_FRAME_META=1`, append every MJPEG spool timing record as JSONL for full-probe HEVC/RCT correlation; summarize with `scripts/manual/summarize_uvc_frame_meta_log.py` |
|
||||
| `LESAVKA_UVC_FRAME_META_PATH` | UVC helper diagnostic override; explicit path for the optional MJPEG spool metadata sidecar |
|
||||
| `LESAVKA_UVC_FRAME_MAX_AGE_MS` | UVC helper freshness override; stale spooled MJPEG frames older than this are not replayed, defaults to `1000`; `0` disables TTL |
|
||||
| `LESAVKA_UVC_FRAME_MAX_BYTES` | UVC helper MJPEG frame-size guard; explicit maximum accepted frame bytes, where `0` disables the guard and otherwise oversized frames are frozen out |
|
||||
| `LESAVKA_UVC_FRAME_SIZE` | server hardware/device override |
|
||||
| `LESAVKA_UVC_HEIGHT` | server hardware/device override |
|
||||
| `LESAVKA_UVC_HEVC_SPOOL_PULL_TIMEOUT_MS` | server HEVC decode-to-MJPEG freshness override; appsink pull wait for decoded MJPEG handoff before publishing newest frame to the UVC helper, defaults to `5` and is capped at `50` |
|
||||
@ -322,6 +324,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
||||
| `LESAVKA_UVC_MAXPACKET` | server hardware/device override |
|
||||
| `LESAVKA_UVC_MAXPAYLOAD_LIMIT` | server hardware/device override |
|
||||
| `LESAVKA_UVC_MJPEG` | server hardware/device override |
|
||||
| `LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC` | UVC helper MJPEG budget guard; derives a per-frame byte cap from target FPS when `LESAVKA_UVC_FRAME_MAX_BYTES` is unset |
|
||||
| `LESAVKA_UVC_SKIP_UDEV` | server hardware/device override |
|
||||
| `LESAVKA_UVC_STREAMING_INTERVAL` | server hardware/device override |
|
||||
| `LESAVKA_UVC_STREAM_INTF` | server hardware/device override |
|
||||
@ -399,6 +402,7 @@ These entries are intentionally concise because most are manual lab or CI harnes
|
||||
| `LESAVKA_HEVC_REENTRY_WAIT_INTERVAL_SECONDS` | manual HEVC re-entry helper retry interval while waiting for SSH after a lab host outage, defaults to `15` |
|
||||
| `LESAVKA_HEVC_REENTRY_WAIT_SECONDS` | manual HEVC re-entry helper reachability wait budget; when greater than `0`, polls SSH before status/build/deploy/reconfigure instead of failing immediately |
|
||||
| `LESAVKA_INSTALL_CAM_CODEC` | server installer camera ingress codec default; persists `LESAVKA_CAM_CODEC` for installed services, defaults to `hevc` |
|
||||
| `LESAVKA_INSTALL_SOURCE` | install script source selector; use `ref` to fetch the requested git ref instead of building the existing local checkout |
|
||||
| `LESAVKA_INSTALL_UVC_FRAME_META` | server installer diagnostic toggle; persists `LESAVKA_UVC_FRAME_META`, defaults to `0` so spool metadata is opt-in |
|
||||
| `LESAVKA_INSTALL_UVC_FRAME_META_LOG_PATH` | server installer diagnostic path; persists `LESAVKA_UVC_FRAME_META_LOG_PATH`, defaults to `/tmp/lesavka-uvc-frame-meta.jsonl` for optional client-to-RCT spool-boundary fetches |
|
||||
| `LESAVKA_INSTALL_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US` | installer default override; seeds server calibration env files with known lab-measured output-path offsets |
|
||||
@ -418,6 +422,13 @@ These entries are intentionally concise because most are manual lab or CI harnes
|
||||
| `LESAVKA_MIC_PULSE_LATENCY_TIME_US` | client microphone capture override; tunes Pulse/PipeWire packet sizing, buffering, or selected source behavior |
|
||||
| `LESAVKA_MODE` | manual probe/server connection override used to resolve the target Lesavka server and mode under test |
|
||||
| `LESAVKA_OPEN_MANUAL_REVIEW_DOLPHIN` | manual browser-stimulus probe override; controls local review browser behavior for mirrored upstream A/V tests |
|
||||
| `LESAVKA_GOOGLE_MEET_URL` | manual Google Meet observer probe URL; recorded in artifacts and optionally opened locally without storing credentials |
|
||||
| `LESAVKA_MEET_ANALYSIS_REQUIRED` | manual Google Meet observer probe guard; when true, fail if no observer capture is supplied for analysis |
|
||||
| `LESAVKA_MEET_LOCAL_REVIEW` | manual Google Meet observer probe toggle for local post-run review helpers |
|
||||
| `LESAVKA_MEET_OBSERVER_CAPTURE` | manual Google Meet observer recording path copied and analyzed after the synthetic upstream stimulus run |
|
||||
| `LESAVKA_MEET_OPEN_URL` | manual Google Meet observer probe toggle; when true, opens `LESAVKA_GOOGLE_MEET_URL` in the local browser |
|
||||
| `LESAVKA_MEET_SKIP_PROMPTS` | manual Google Meet observer probe toggle for non-interactive lab retries after setup is already complete |
|
||||
| `LESAVKA_MEET_START_DELAY_SECONDS` | manual Google Meet observer probe delay before synthetic media starts, giving the operator/browser time to settle |
|
||||
| `LESAVKA_OUTPUT_DELAY_CONFIRMING` | manual direct UVC/UAC output-delay probe override; controls server-generated output calibration, confirmation, freshness, or reporting |
|
||||
| `LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US` | manual direct UVC/UAC output-delay probe override; controls server-generated output calibration, confirmation, freshness, or reporting |
|
||||
| `LESAVKA_OUTPUT_DELAY_PROBE_VIDEO_DELAY_US` | manual direct UVC/UAC output-delay probe override; controls server-generated output calibration, confirmation, freshness, or reporting |
|
||||
@ -557,7 +568,12 @@ These entries are intentionally concise because most are manual lab or CI harnes
|
||||
| `LESAVKA_UPSTREAM_BLIND_HEAL_MAX_STEP_US` | server upstream media blind-healer tuning knob; adjusts cautious runtime offset correction when telemetry indicates persistent skew |
|
||||
| `LESAVKA_UPSTREAM_BLIND_HEAL_MIN_SAMPLES` | server upstream media blind-healer tuning knob; adjusts cautious runtime offset correction when telemetry indicates persistent skew |
|
||||
| `LESAVKA_UPSTREAM_BLIND_HEAL_TARGET` | server upstream media blind-healer tuning knob; adjusts cautious runtime offset correction when telemetry indicates persistent skew |
|
||||
| `LESAVKA_UPSTREAM_AUTO_HEAL` | client live bundled-upstream startup heal toggle; defaults on to retire a stale first UAC epoch after connect |
|
||||
| `LESAVKA_UPSTREAM_AUTO_HEAL_AFTER_MS` | client live bundled-upstream startup heal delay; defaults to `3000`ms before issuing the safe audio-epoch recovery |
|
||||
| `LESAVKA_UPSTREAM_SOURCE_LEAD_CAP_MS` | server upstream media timing override; bounds live source lead or playout behavior while tuning client-to-server transport |
|
||||
| `LESAVKA_UVC_CONFIGFS_BASE` | server UVC gadget mode/configfs override used by runtime reconfiguration and hardware-in-the-loop probes |
|
||||
| `LESAVKA_UVC_HEVC_JPEG_QUALITY` | server HEVC-to-MJPEG UVC bridge JPEG quality; defaults to `90` to keep RCT output compatible while limiting encode cost |
|
||||
| `LESAVKA_UVC_HEVC_FREEZE_ON_SIZE_DROP` | server HEVC-to-MJPEG corruption guard toggle; defaults on so suspicious decoded frame collapses freeze the last good MJPEG frame |
|
||||
| `LESAVKA_UVC_HEVC_JPEG_QUALITY` | server HEVC-to-MJPEG UVC bridge JPEG quality; defaults to `72` to lower UVC payload pressure while keeping RCT output compatible |
|
||||
| `LESAVKA_UVC_HEVC_MIN_REFERENCE_BYTES` | server HEVC-to-MJPEG corruption guard baseline; decoded MJPEG frames smaller than this do not become freeze references |
|
||||
| `LESAVKA_UVC_HEVC_SIZE_DROP_PCT` | server HEVC-to-MJPEG corruption guard threshold; frames below this percentage of the last good reference are frozen out |
|
||||
| `LESAVKA_UVC_MODE` | server UVC gadget mode/configfs override used by runtime reconfiguration and hardware-in-the-loop probes |
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
"client/src/app/uplink_media.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 17
|
||||
"loc": 19
|
||||
},
|
||||
"client/src/app/uplink_media/bundled_media_queue.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -55,6 +55,11 @@
|
||||
"doc_debt": 0,
|
||||
"loc": 212
|
||||
},
|
||||
"client/src/app/uplink_media/video_keyframes.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 103
|
||||
},
|
||||
"client/src/app/uplink_media/voice_loop.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
@ -63,17 +68,17 @@
|
||||
"client/src/app/uplink_media/webcam_media_loop.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 295
|
||||
"loc": 421
|
||||
},
|
||||
"client/src/app_support.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 3,
|
||||
"loc": 138
|
||||
"loc": 174
|
||||
},
|
||||
"client/src/bin/lesavka-relayctl.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 423
|
||||
"loc": 425
|
||||
},
|
||||
"client/src/bin/lesavka-sync-analyze.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -113,7 +118,7 @@
|
||||
"client/src/input/camera.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 206
|
||||
"loc": 232
|
||||
},
|
||||
"client/src/input/camera/bus_and_encoder.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -123,7 +128,7 @@
|
||||
"client/src/input/camera/capture_pipeline.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 404
|
||||
"loc": 420
|
||||
},
|
||||
"client/src/input/camera/device_selection.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -268,7 +273,7 @@
|
||||
"client/src/launcher/diagnostics/diagnostics_models.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 1,
|
||||
"loc": 199
|
||||
"loc": 200
|
||||
},
|
||||
"client/src/launcher/diagnostics/recommendations.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -278,17 +283,17 @@
|
||||
"client/src/launcher/diagnostics/snapshot_report.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 2,
|
||||
"loc": 303
|
||||
"loc": 304
|
||||
},
|
||||
"client/src/launcher/diagnostics/snapshot_report_text.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 2,
|
||||
"loc": 329
|
||||
"loc": 330
|
||||
},
|
||||
"client/src/launcher/mod.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 5,
|
||||
"loc": 240
|
||||
"loc": 244
|
||||
},
|
||||
"client/src/launcher/power.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -333,12 +338,12 @@
|
||||
"client/src/launcher/state/launcher_state_impl.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 17,
|
||||
"loc": 459
|
||||
"loc": 463
|
||||
},
|
||||
"client/src/launcher/state/launcher_status_line.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 1,
|
||||
"loc": 48
|
||||
"loc": 49
|
||||
},
|
||||
"client/src/launcher/state/profile_helpers.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -348,27 +353,27 @@
|
||||
"client/src/launcher/state/selection_models.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 325
|
||||
"loc": 380
|
||||
},
|
||||
"client/src/launcher/state/selection_models/sync_and_state_status.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 329
|
||||
"loc": 331
|
||||
},
|
||||
"client/src/launcher/ui.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 1,
|
||||
"loc": 201
|
||||
"loc": 202
|
||||
},
|
||||
"client/src/launcher/ui/activation_context.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 45
|
||||
"loc": 46
|
||||
},
|
||||
"client/src/launcher/ui/activation_setup.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 187
|
||||
"loc": 189
|
||||
},
|
||||
"client/src/launcher/ui/control_requests.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -408,7 +413,7 @@
|
||||
"client/src/launcher/ui/message_and_network_state.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 3,
|
||||
"loc": 141
|
||||
"loc": 157
|
||||
},
|
||||
"client/src/launcher/ui/power_display_key_bindings.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -423,12 +428,17 @@
|
||||
"client/src/launcher/ui/relay_input_bindings.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 190
|
||||
"loc": 201
|
||||
},
|
||||
"client/src/launcher/ui/runtime_poll.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 497
|
||||
"loc": 476
|
||||
},
|
||||
"client/src/launcher/ui/runtime_poll/runtime_monitor_tick.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 40
|
||||
},
|
||||
"client/src/launcher/ui/session_preview_coverage.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -438,7 +448,7 @@
|
||||
"client/src/launcher/ui/stage_device_bindings.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 190
|
||||
"loc": 219
|
||||
},
|
||||
"client/src/launcher/ui/startup_window_guard.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -548,12 +558,12 @@
|
||||
"client/src/launcher/ui_runtime/status_details.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 12,
|
||||
"loc": 427
|
||||
"loc": 423
|
||||
},
|
||||
"client/src/launcher/ui_runtime/status_refresh.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 3,
|
||||
"loc": 350
|
||||
"loc": 366
|
||||
},
|
||||
"client/src/layout.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -893,12 +903,12 @@
|
||||
"server/src/bin/lesavka_uvc/coverage_model.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 137
|
||||
"loc": 141
|
||||
},
|
||||
"server/src/bin/lesavka_uvc/coverage_startup.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 5,
|
||||
"loc": 203
|
||||
"loc": 229
|
||||
},
|
||||
"server/src/bin/lesavka_uvc/payload_limits.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -998,7 +1008,7 @@
|
||||
"server/src/main.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 1,
|
||||
"loc": 109
|
||||
"loc": 112
|
||||
},
|
||||
"server/src/main/entrypoint.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1023,7 +1033,7 @@
|
||||
"server/src/main/relay_service.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 414
|
||||
"loc": 494
|
||||
},
|
||||
"server/src/main/relay_service/camera_stream_rpc.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1053,7 +1063,7 @@
|
||||
"server/src/main/relay_service/upstream_media_rpc.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 251
|
||||
"loc": 285
|
||||
},
|
||||
"server/src/main/relay_service_coverage.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1073,7 +1083,7 @@
|
||||
"server/src/main/relay_service_tests.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 220
|
||||
"loc": 308
|
||||
},
|
||||
"server/src/main/relay_stream_lifecycle.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1198,7 +1208,7 @@
|
||||
"server/src/video_sinks.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 4
|
||||
"loc": 7
|
||||
},
|
||||
"server/src/video_sinks/camera_relay.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1210,6 +1220,11 @@
|
||||
"doc_debt": 7,
|
||||
"loc": 466
|
||||
},
|
||||
"server/src/video_sinks/hevc_mjpeg_guard.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 2,
|
||||
"loc": 120
|
||||
},
|
||||
"server/src/video_sinks/mjpeg_spool.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
@ -1218,12 +1233,12 @@
|
||||
"server/src/video_sinks/webcam_sink.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 6,
|
||||
"loc": 479
|
||||
"loc": 494
|
||||
},
|
||||
"server/src/video_support.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 1,
|
||||
"loc": 301
|
||||
"loc": 340
|
||||
},
|
||||
"testing/src/lib.rs": {
|
||||
"clippy_warnings": 0,
|
||||
|
||||
@ -10,7 +10,7 @@ bench = false
|
||||
|
||||
[package]
|
||||
name = "lesavka_server"
|
||||
version = "0.21.16"
|
||||
version = "0.21.17"
|
||||
edition = "2024"
|
||||
autobins = false
|
||||
|
||||
|
||||
@ -160,6 +160,11 @@ fn media_v2_handoff_schedule(
|
||||
}
|
||||
|
||||
#[cfg(not(coverage))]
|
||||
/// Convert a negotiated video FPS into a microsecond frame step.
|
||||
///
|
||||
/// Inputs: frames per second from the camera config. Output: at least one
|
||||
/// microsecond per frame. Why: recovery keyframe cadence and audio/video
|
||||
/// bundle planning both need a bounded step even when a bad mode reports zero.
|
||||
fn media_v2_frame_step_us(fps: u32) -> u64 {
|
||||
if fps == 0 {
|
||||
return 1;
|
||||
|
||||
@ -8,8 +8,8 @@ mod tests {
|
||||
prepare_media_v2_video, retain_freshest_audio_packet, retain_freshest_video_packet,
|
||||
summarize_media_v2_bundle,
|
||||
};
|
||||
use crate::camera::CameraCodec;
|
||||
use lesavka_common::lesavka::{AudioPacket, UpstreamMediaBundle, VideoPacket};
|
||||
use lesavka_server::camera::CameraCodec;
|
||||
use lesavka_server::upstream_media_runtime::{
|
||||
UpstreamClientTiming, UpstreamMediaKind, UpstreamMediaRuntime,
|
||||
};
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
// Camera sink pipelines for UVC webcam output and HDMI capture adapters.
|
||||
mod hevc_mjpeg_guard;
|
||||
mod hevc_mjpeg_guard {
|
||||
include!("video_sinks/hevc_mjpeg_guard.rs");
|
||||
}
|
||||
include!("video_sinks/webcam_sink.rs");
|
||||
include!("video_sinks/hdmi_sink.rs");
|
||||
include!("video_sinks/camera_relay.rs");
|
||||
|
||||
@ -51,6 +51,8 @@ mod app_support {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CameraCodec {
|
||||
H264,
|
||||
Hevc,
|
||||
Mjpeg,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@ -96,7 +98,7 @@ mod relay_transport {
|
||||
|
||||
mod input {
|
||||
pub mod camera {
|
||||
pub use crate::app_support::CameraConfig;
|
||||
pub use crate::app_support::{CameraCodec, CameraConfig};
|
||||
use lesavka_common::lesavka::VideoPacket;
|
||||
|
||||
pub struct CameraCapture;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user