102 lines
3.6 KiB
Rust

use std::time::Duration;
use tokio::time::Instant;
use super::UpstreamMediaKind;
use crate::calibration::{FACTORY_MJPEG_AUDIO_OFFSET_US, FACTORY_MJPEG_VIDEO_OFFSET_US};
pub(super) fn upstream_timing_trace_enabled() -> bool {
std::env::var("LESAVKA_UPSTREAM_TIMING_TRACE")
.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(false)
}
pub(super) fn upstream_playout_delay() -> Duration {
let delay_ms = std::env::var("LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS")
.ok()
.and_then(|value| value.trim().parse::<u64>().ok())
.unwrap_or(1_000);
Duration::from_millis(delay_ms)
}
pub(super) fn upstream_require_paired_startup() -> bool {
std::env::var("LESAVKA_UPSTREAM_REQUIRE_PAIRED_STARTUP")
.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(true)
}
pub(super) fn upstream_playout_offset_us(kind: UpstreamMediaKind) -> i64 {
let name = match kind {
UpstreamMediaKind::Camera => "LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US",
UpstreamMediaKind::Microphone => "LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US",
};
let default_offset_us = match kind {
UpstreamMediaKind::Camera => FACTORY_MJPEG_VIDEO_OFFSET_US,
// Hardware sync probes on the MJPEG UVC path show the UAC leg arriving
// about 80ms after video when using the older +35ms default. Bias the
// server playout earlier so the shipped default lands in the preferred
// lip-sync band instead of hovering at the guardrail.
UpstreamMediaKind::Microphone => FACTORY_MJPEG_AUDIO_OFFSET_US,
};
std::env::var(name)
.ok()
.and_then(|value| value.trim().parse::<i64>().ok())
.unwrap_or(default_offset_us)
}
pub(super) fn upstream_pairing_master_slack() -> Duration {
let slack_us = std::env::var("LESAVKA_UPSTREAM_PAIR_SLACK_US")
.ok()
.and_then(|value| value.trim().parse::<u64>().ok())
.unwrap_or(20_000);
Duration::from_micros(slack_us)
}
pub(super) fn upstream_reanchor_late_threshold(playout_delay: Duration) -> Duration {
if let Some(override_ms) = std::env::var("LESAVKA_UPSTREAM_REANCHOR_LATE_MS")
.ok()
.and_then(|value| value.trim().parse::<u64>().ok())
{
return Duration::from_millis(override_ms);
}
let default_ms = (playout_delay.as_millis().min(u64::MAX as u128) as u64)
.saturating_div(2)
.max(250);
Duration::from_millis(default_ms)
}
pub(super) fn upstream_camera_startup_grace_us() -> u64 {
std::env::var("LESAVKA_UPSTREAM_CAMERA_STARTUP_GRACE_MS")
.ok()
.and_then(|value| value.trim().parse::<u64>().ok())
.unwrap_or(if cfg!(test) { 0 } else { 250 })
.saturating_mul(1_000)
}
pub(super) fn upstream_reanchor_window_us(playout_delay: Duration) -> u64 {
playout_delay.as_micros().min(u64::MAX as u128) as u64
}
pub(super) fn apply_playout_offset(base: Instant, offset_us: i64) -> Instant {
if offset_us >= 0 {
base + Duration::from_micros(offset_us as u64)
} else {
base.checked_sub(Duration::from_micros(offset_us.unsigned_abs()))
.unwrap_or(base)
}
}