fix(sync): keep microphone paired through camera warmup
This commit is contained in:
parent
7224ae399d
commit
56c475f742
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1676,7 +1676,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1688,7 +1688,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.14.10"
|
version = "0.14.11"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -8,15 +8,6 @@ fn upstream_stale_drop_budget() -> Duration {
|
|||||||
Duration::from_millis(drop_ms)
|
Duration::from_millis(drop_ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(coverage))]
|
|
||||||
fn upstream_camera_startup_grace() -> Duration {
|
|
||||||
let grace_ms = std::env::var("LESAVKA_UPSTREAM_CAMERA_STARTUP_GRACE_MS")
|
|
||||||
.ok()
|
|
||||||
.and_then(|value| value.trim().parse::<u64>().ok())
|
|
||||||
.unwrap_or(250);
|
|
||||||
Duration::from_millis(grace_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
fn retain_freshest_video_packet(
|
fn retain_freshest_video_packet(
|
||||||
pending: &mut std::collections::VecDeque<VideoPacket>,
|
pending: &mut std::collections::VecDeque<VideoPacket>,
|
||||||
@ -289,10 +280,6 @@ impl Relay for Handler {
|
|||||||
let mut inbound_closed = false;
|
let mut inbound_closed = false;
|
||||||
let stale_drop_budget = upstream_stale_drop_budget();
|
let stale_drop_budget = upstream_stale_drop_budget();
|
||||||
let mut startup_video_settled = false;
|
let mut startup_video_settled = false;
|
||||||
let startup_grace_us = upstream_camera_startup_grace()
|
|
||||||
.as_micros()
|
|
||||||
.min(u64::MAX as u128) as u64;
|
|
||||||
let mut cold_startup_grace_pending = startup_grace_us > 0;
|
|
||||||
loop {
|
loop {
|
||||||
if !camera_rt.is_active(session_id)
|
if !camera_rt.is_active(session_id)
|
||||||
|| !upstream_media_rt.is_camera_active(upstream_lease.generation)
|
|| !upstream_media_rt.is_camera_active(upstream_lease.generation)
|
||||||
@ -339,20 +326,6 @@ impl Relay for Handler {
|
|||||||
}
|
}
|
||||||
lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(plan) => plan,
|
lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(plan) => plan,
|
||||||
};
|
};
|
||||||
if cold_startup_grace_pending && plan.local_pts_us < startup_grace_us {
|
|
||||||
let coalesced = retain_freshest_video_packet(&mut pending);
|
|
||||||
tracing::debug!(
|
|
||||||
rpc_id,
|
|
||||||
session_id,
|
|
||||||
remote_pts_us = pkt.pts,
|
|
||||||
local_pts_us = plan.local_pts_us,
|
|
||||||
startup_grace_us,
|
|
||||||
dropped_pending = coalesced,
|
|
||||||
"🎥 dropping startup video until the shared-session warm-up grace is spent"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cold_startup_grace_pending = false;
|
|
||||||
if !upstream_media_rt
|
if !upstream_media_rt
|
||||||
.wait_for_audio_master(plan.local_pts_us, plan.due_at)
|
.wait_for_audio_master(plan.local_pts_us, plan.due_at)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -7,15 +7,6 @@ fn upstream_stale_drop_budget() -> Duration {
|
|||||||
Duration::from_millis(drop_ms)
|
Duration::from_millis(drop_ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(coverage)]
|
|
||||||
fn upstream_camera_startup_grace() -> Duration {
|
|
||||||
let grace_ms = std::env::var("LESAVKA_UPSTREAM_CAMERA_STARTUP_GRACE_MS")
|
|
||||||
.ok()
|
|
||||||
.and_then(|value| value.trim().parse::<u64>().ok())
|
|
||||||
.unwrap_or(250);
|
|
||||||
Duration::from_millis(grace_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(coverage)]
|
#[cfg(coverage)]
|
||||||
fn retain_freshest_video_packet(
|
fn retain_freshest_video_packet(
|
||||||
pending: &mut std::collections::VecDeque<VideoPacket>,
|
pending: &mut std::collections::VecDeque<VideoPacket>,
|
||||||
@ -184,10 +175,6 @@ impl Relay for Handler {
|
|||||||
let mut pending = std::collections::VecDeque::new();
|
let mut pending = std::collections::VecDeque::new();
|
||||||
let mut inbound_closed = false;
|
let mut inbound_closed = false;
|
||||||
let stale_drop_budget = upstream_stale_drop_budget();
|
let stale_drop_budget = upstream_stale_drop_budget();
|
||||||
let startup_grace_us = upstream_camera_startup_grace()
|
|
||||||
.as_micros()
|
|
||||||
.min(u64::MAX as u128) as u64;
|
|
||||||
let mut cold_startup_grace_pending = startup_grace_us > 0;
|
|
||||||
loop {
|
loop {
|
||||||
if !camera_rt.is_active(session_id)
|
if !camera_rt.is_active(session_id)
|
||||||
|| !upstream_media_rt.is_camera_active(upstream_lease.generation)
|
|| !upstream_media_rt.is_camera_active(upstream_lease.generation)
|
||||||
@ -225,11 +212,6 @@ impl Relay for Handler {
|
|||||||
}
|
}
|
||||||
lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(plan) => plan,
|
lesavka_server::upstream_media_runtime::UpstreamPlanDecision::Play(plan) => plan,
|
||||||
};
|
};
|
||||||
if cold_startup_grace_pending && plan.local_pts_us < startup_grace_us {
|
|
||||||
let _ = retain_freshest_video_packet(&mut pending);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cold_startup_grace_pending = false;
|
|
||||||
if !upstream_media_rt
|
if !upstream_media_rt
|
||||||
.wait_for_audio_master(plan.local_pts_us, plan.due_at)
|
.wait_for_audio_master(plan.local_pts_us, plan.due_at)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -397,6 +397,19 @@ impl UpstreamMediaRuntime {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return UpstreamPlanDecision::AwaitingPair;
|
return UpstreamPlanDecision::AwaitingPair;
|
||||||
|
} else if state.first_camera_remote_pts_us.is_some() && !state.camera_startup_ready {
|
||||||
|
if upstream_timing_trace_enabled()
|
||||||
|
&& (packet_count <= 10 || packet_count.is_multiple_of(300))
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
session_id,
|
||||||
|
?kind,
|
||||||
|
packet_count,
|
||||||
|
remote_pts_us,
|
||||||
|
"upstream media packet buffered while camera startup warm-up is still in progress"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return UpstreamPlanDecision::AwaitingPair;
|
||||||
} else {
|
} else {
|
||||||
let single_stream_base_remote_pts_us = match kind {
|
let single_stream_base_remote_pts_us = match kind {
|
||||||
UpstreamMediaKind::Camera => {
|
UpstreamMediaKind::Camera => {
|
||||||
|
|||||||
@ -91,6 +91,39 @@ fn overlap_waits_for_camera_startup_grace_before_establishing_the_shared_base()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pairing_window_does_not_expire_into_one_sided_playout_while_camera_warms_up() {
|
||||||
|
temp_env::with_var("LESAVKA_UPSTREAM_CAMERA_STARTUP_GRACE_MS", Some("250"), || {
|
||||||
|
temp_env::with_var("LESAVKA_UPSTREAM_PLAYOUT_DELAY_MS", Some("20"), || {
|
||||||
|
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
|
||||||
|
));
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(30));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
runtime.plan_audio_pts(1_010_000),
|
||||||
|
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]
|
#[test]
|
||||||
fn overlap_pairing_drops_leading_packets_before_the_shared_base() {
|
fn overlap_pairing_drops_leading_packets_before_the_shared_base() {
|
||||||
let runtime = UpstreamMediaRuntime::new();
|
let runtime = UpstreamMediaRuntime::new();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user