fix(sync): gate overlap startup on camera warmup
This commit is contained in:
parent
f8c48cd89c
commit
7224ae399d
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1676,7 +1676,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@ -1688,7 +1688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
||||
|
||||
[package]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ bench = false
|
||||
|
||||
[package]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
edition = "2024"
|
||||
autobins = false
|
||||
|
||||
|
||||
@ -16,6 +16,14 @@ use config::{
|
||||
};
|
||||
use state::UpstreamClockState;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/// Logical upstream media kinds that share one live-call session timeline.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum UpstreamMediaKind {
|
||||
@ -143,6 +151,7 @@ impl UpstreamMediaRuntime {
|
||||
state.session_id = self.next_session_id.fetch_add(1, Ordering::SeqCst) + 1;
|
||||
state.first_camera_remote_pts_us = None;
|
||||
state.first_microphone_remote_pts_us = None;
|
||||
state.camera_startup_ready = false;
|
||||
state.session_base_remote_pts_us = None;
|
||||
state.last_video_local_pts_us = None;
|
||||
state.last_audio_local_pts_us = None;
|
||||
@ -216,6 +225,7 @@ impl UpstreamMediaRuntime {
|
||||
{
|
||||
state.first_camera_remote_pts_us = None;
|
||||
state.first_microphone_remote_pts_us = None;
|
||||
state.camera_startup_ready = false;
|
||||
state.session_base_remote_pts_us = None;
|
||||
state.last_video_local_pts_us = None;
|
||||
state.last_audio_local_pts_us = None;
|
||||
@ -317,11 +327,27 @@ impl UpstreamMediaRuntime {
|
||||
state.microphone_packet_count
|
||||
}
|
||||
};
|
||||
let first_slot = match kind {
|
||||
UpstreamMediaKind::Camera => &mut state.first_camera_remote_pts_us,
|
||||
UpstreamMediaKind::Microphone => &mut state.first_microphone_remote_pts_us,
|
||||
let mut first_remote_for_kind = match kind {
|
||||
UpstreamMediaKind::Camera => {
|
||||
let first_slot = &mut state.first_camera_remote_pts_us;
|
||||
*first_slot.get_or_insert(remote_pts_us)
|
||||
}
|
||||
UpstreamMediaKind::Microphone => {
|
||||
let first_slot = &mut state.first_microphone_remote_pts_us;
|
||||
*first_slot.get_or_insert(remote_pts_us)
|
||||
}
|
||||
};
|
||||
let first_remote_for_kind = *first_slot.get_or_insert(remote_pts_us);
|
||||
if kind == UpstreamMediaKind::Camera {
|
||||
let startup_grace_us = upstream_camera_startup_grace_us();
|
||||
if !state.camera_startup_ready
|
||||
&& (startup_grace_us == 0
|
||||
|| remote_pts_us.saturating_sub(first_remote_for_kind) >= startup_grace_us)
|
||||
{
|
||||
state.camera_startup_ready = true;
|
||||
state.first_camera_remote_pts_us = Some(remote_pts_us);
|
||||
first_remote_for_kind = remote_pts_us;
|
||||
}
|
||||
}
|
||||
let now = Instant::now();
|
||||
let pairing_deadline = *state
|
||||
.pairing_anchor_deadline
|
||||
@ -331,6 +357,7 @@ impl UpstreamMediaRuntime {
|
||||
if state.session_base_remote_pts_us.is_none() {
|
||||
if state.first_camera_remote_pts_us.is_some()
|
||||
&& state.first_microphone_remote_pts_us.is_some()
|
||||
&& state.camera_startup_ready
|
||||
{
|
||||
let first_camera_remote_pts_us =
|
||||
state.first_camera_remote_pts_us.unwrap_or_default();
|
||||
|
||||
@ -7,6 +7,7 @@ pub(super) struct UpstreamClockState {
|
||||
pub active_microphone_generation: Option<u64>,
|
||||
pub first_camera_remote_pts_us: Option<u64>,
|
||||
pub first_microphone_remote_pts_us: Option<u64>,
|
||||
pub camera_startup_ready: bool,
|
||||
pub session_base_remote_pts_us: Option<u64>,
|
||||
pub last_video_local_pts_us: Option<u64>,
|
||||
pub last_audio_local_pts_us: Option<u64>,
|
||||
|
||||
@ -63,6 +63,34 @@ fn first_packets_wait_for_the_counterpart_before_pairing() {
|
||||
assert_eq!(audio_first.due_at, video_first.due_at);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlap_waits_for_camera_startup_grace_before_establishing_the_shared_base() {
|
||||
temp_env::with_var("LESAVKA_UPSTREAM_CAMERA_STARTUP_GRACE_MS", Some("250"), || {
|
||||
let runtime = UpstreamMediaRuntime::new();
|
||||
let _camera = runtime.activate_camera();
|
||||
let _microphone = runtime.activate_microphone();
|
||||
|
||||
assert!(matches!(
|
||||
runtime.plan_video_pts(1_000_000, 16_666),
|
||||
super::UpstreamPlanDecision::AwaitingPair
|
||||
));
|
||||
assert!(matches!(
|
||||
runtime.plan_audio_pts(1_000_000),
|
||||
super::UpstreamPlanDecision::AwaitingPair
|
||||
));
|
||||
assert!(matches!(
|
||||
runtime.plan_video_pts(1_200_000, 16_666),
|
||||
super::UpstreamPlanDecision::AwaitingPair
|
||||
));
|
||||
|
||||
let video_ready = play(runtime.plan_video_pts(1_250_000, 16_666));
|
||||
let audio_ready = play(runtime.plan_audio_pts(1_260_000));
|
||||
|
||||
assert_eq!(video_ready.local_pts_us, 0);
|
||||
assert_eq!(audio_ready.local_pts_us, 10_000);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlap_pairing_drops_leading_packets_before_the_shared_base() {
|
||||
let runtime = UpstreamMediaRuntime::new();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user