115 lines
5.2 KiB
Rust
115 lines
5.2 KiB
Rust
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
|
|
}
|
|
}
|