lesavka/server/src/upstream_media_runtime/planner_snapshot_methods.rs

115 lines
5.2 KiB
Rust
Raw Normal View History

impl UpstreamMediaRuntime {
#[must_use]
/// Keeps `snapshot` explicit because it sits on server upstream media scheduling, where timing choices directly affect lip sync.
pub fn snapshot(&self) -> UpstreamPlannerSnapshot {
let state = self
.state
.lock()
.expect("upstream media state mutex poisoned");
let now = Instant::now();
UpstreamPlannerSnapshot {
session_id: state.session_id,
phase: state.phase.as_str(),
latest_camera_remote_pts_us: state.latest_camera_remote_pts_us,
latest_microphone_remote_pts_us: state.latest_microphone_remote_pts_us,
last_video_presented_pts_us: state.last_video_presented_pts_us,
last_audio_presented_pts_us: state.last_audio_presented_pts_us,
live_lag_ms: live_lag_ms(&state),
planner_skew_ms: planner_skew_ms(&state),
stale_audio_drops: state.stale_audio_drops,
stale_video_drops: state.stale_video_drops,
skew_video_drops: state.skew_video_drops,
freshness_reanchors: state.freshness_reanchors,
startup_timeouts: state.startup_timeouts,
video_freezes: state.video_freezes,
last_reason: state.last_reason.clone(),
client_capture_skew_ms: state.latest_paired_client_capture_skew_ms,
client_send_skew_ms: state.latest_paired_client_send_skew_ms,
server_receive_skew_ms: state.latest_paired_server_receive_skew_ms,
camera_client_queue_age_ms: state
.latest_camera_timing
.map(|sample| f64::from(sample.queue_age_ms)),
microphone_client_queue_age_ms: state
.latest_microphone_timing
.map(|sample| f64::from(sample.queue_age_ms)),
camera_server_receive_age_ms: state
.latest_camera_timing
.map(|sample| age_ms(now, sample.received_at)),
microphone_server_receive_age_ms: state
.latest_microphone_timing
.map(|sample| age_ms(now, sample.received_at)),
client_capture_abs_skew_p95_ms: state.client_capture_skew_window_ms.p95_abs(),
client_send_abs_skew_p95_ms: state.client_send_skew_window_ms.p95_abs(),
server_receive_abs_skew_p95_ms: state.server_receive_skew_window_ms.p95_abs(),
camera_client_queue_age_p95_ms: state.camera_client_queue_age_window_ms.p95(),
microphone_client_queue_age_p95_ms: state.microphone_client_queue_age_window_ms.p95(),
sink_handoff_skew_ms: latest_sink_handoff_skew_ms(&state),
sink_handoff_abs_skew_p95_ms: state.sink_handoff_skew_window_ms.p95_abs(),
camera_sink_late_ms: state.latest_camera_presentation.map(presentation_late_ms),
microphone_sink_late_ms: state
.latest_microphone_presentation
.map(presentation_late_ms),
camera_sink_late_p95_ms: state.camera_sink_late_window_ms.p95(),
microphone_sink_late_p95_ms: state.microphone_sink_late_window_ms.p95(),
client_timing_window_samples: state.client_capture_skew_window_ms.len() as u64,
sink_handoff_window_samples: state.sink_handoff_skew_window_ms.len() as u64,
}
}
#[must_use]
/// Keeps `map_video_pts` explicit because it sits on server upstream media scheduling, where timing choices directly affect lip sync.
/// Inputs are the typed parameters; output is the return value or side effect.
pub fn map_video_pts(&self, remote_pts_us: u64, frame_step_us: u64) -> Option<u64> {
match self.plan_video_pts(remote_pts_us, frame_step_us) {
UpstreamPlanDecision::Play(plan) => Some(plan.local_pts_us),
_ => None,
}
}
#[must_use]
/// Keeps `map_audio_pts` explicit because it sits on server upstream media scheduling, where timing choices directly affect lip sync.
/// Inputs are the typed parameters; output is the return value or side effect.
pub fn map_audio_pts(&self, remote_pts_us: u64) -> Option<u64> {
match self.plan_audio_pts(remote_pts_us) {
UpstreamPlanDecision::Play(plan) => Some(plan.local_pts_us),
_ => None,
}
}
#[must_use]
pub fn plan_video_pts(&self, remote_pts_us: u64, frame_step_us: u64) -> UpstreamPlanDecision {
self.plan_legacy_pts(
UpstreamMediaKind::Camera,
remote_pts_us,
frame_step_us.max(1),
)
}
#[must_use]
pub fn plan_audio_pts(&self, remote_pts_us: u64) -> UpstreamPlanDecision {
self.plan_legacy_pts(UpstreamMediaKind::Microphone, remote_pts_us, 1)
}
#[must_use]
pub fn plan_bundled_pts(
&self,
kind: UpstreamMediaKind,
remote_pts_us: u64,
min_step_us: u64,
bundle_base_remote_pts_us: u64,
bundle_epoch: Instant,
) -> UpstreamPlanDecision {
self.plan_rebased_pts(
kind,
remote_pts_us,
min_step_us.max(1),
Some(bundle_base_remote_pts_us),
Some(bundle_epoch),
)
}
pub async fn wait_for_audio_master(&self, _video_local_pts_us: u64, _due_at: Instant) -> bool {
true
}
}