131 lines
3.5 KiB
Rust
131 lines
3.5 KiB
Rust
|
|
#[cfg(not(coverage))]
|
||
|
|
fn upstream_stale_drop_budget() -> Duration {
|
||
|
|
let drop_ms = std::env::var("LESAVKA_UPSTREAM_STALE_DROP_MS")
|
||
|
|
.ok()
|
||
|
|
.and_then(|value| value.trim().parse::<u64>().ok())
|
||
|
|
.unwrap_or(80);
|
||
|
|
Duration::from_millis(drop_ms)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(not(coverage))]
|
||
|
|
/// Keeps only the newest webcam packet when the host path is already behind.
|
||
|
|
fn retain_freshest_video_packet(
|
||
|
|
pending: &mut std::collections::VecDeque<VideoPacket>,
|
||
|
|
) -> usize {
|
||
|
|
if pending.len() <= 1 {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
let newest = pending.pop_back().expect("non-empty pending video queue");
|
||
|
|
let dropped = pending.len();
|
||
|
|
pending.clear();
|
||
|
|
pending.push_back(newest);
|
||
|
|
dropped
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(not(coverage))]
|
||
|
|
#[derive(Clone, Copy, Debug)]
|
||
|
|
enum UpstreamStreamCleanupKind {
|
||
|
|
Microphone,
|
||
|
|
Camera,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(not(coverage))]
|
||
|
|
struct UpstreamStreamCleanup {
|
||
|
|
runtime: Arc<UpstreamMediaRuntime>,
|
||
|
|
kind: UpstreamStreamCleanupKind,
|
||
|
|
generation: u64,
|
||
|
|
rpc_id: u64,
|
||
|
|
session_id: u64,
|
||
|
|
camera_session_id: Option<u64>,
|
||
|
|
outcome: &'static str,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(not(coverage))]
|
||
|
|
impl UpstreamStreamCleanup {
|
||
|
|
fn microphone(
|
||
|
|
runtime: Arc<UpstreamMediaRuntime>,
|
||
|
|
generation: u64,
|
||
|
|
rpc_id: u64,
|
||
|
|
session_id: u64,
|
||
|
|
) -> Self {
|
||
|
|
Self {
|
||
|
|
runtime,
|
||
|
|
kind: UpstreamStreamCleanupKind::Microphone,
|
||
|
|
generation,
|
||
|
|
rpc_id,
|
||
|
|
session_id,
|
||
|
|
camera_session_id: None,
|
||
|
|
outcome: "aborted",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn camera(
|
||
|
|
runtime: Arc<UpstreamMediaRuntime>,
|
||
|
|
generation: u64,
|
||
|
|
rpc_id: u64,
|
||
|
|
session_id: u64,
|
||
|
|
camera_session_id: u64,
|
||
|
|
) -> Self {
|
||
|
|
Self {
|
||
|
|
runtime,
|
||
|
|
kind: UpstreamStreamCleanupKind::Camera,
|
||
|
|
generation,
|
||
|
|
rpc_id,
|
||
|
|
session_id,
|
||
|
|
camera_session_id: Some(camera_session_id),
|
||
|
|
outcome: "aborted",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn mark_closed(&mut self) {
|
||
|
|
self.outcome = "closed";
|
||
|
|
}
|
||
|
|
|
||
|
|
fn mark_superseded(&mut self) {
|
||
|
|
self.outcome = "superseded";
|
||
|
|
}
|
||
|
|
|
||
|
|
fn mark_aborted(&mut self) {
|
||
|
|
self.outcome = "aborted";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(not(coverage))]
|
||
|
|
impl Drop for UpstreamStreamCleanup {
|
||
|
|
/// Closes only the stream generation owned by this RPC lifecycle guard.
|
||
|
|
fn drop(&mut self) {
|
||
|
|
match self.kind {
|
||
|
|
UpstreamStreamCleanupKind::Microphone => {
|
||
|
|
self.runtime.close_microphone(self.generation);
|
||
|
|
info!(
|
||
|
|
rpc_id = self.rpc_id,
|
||
|
|
session_id = self.session_id,
|
||
|
|
generation = self.generation,
|
||
|
|
outcome = self.outcome,
|
||
|
|
"🎤 stream_microphone lifecycle ended"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
UpstreamStreamCleanupKind::Camera => {
|
||
|
|
self.runtime.close_camera(self.generation);
|
||
|
|
info!(
|
||
|
|
rpc_id = self.rpc_id,
|
||
|
|
session_id = self.session_id,
|
||
|
|
camera_session_id = self.camera_session_id.unwrap_or_default(),
|
||
|
|
generation = self.generation,
|
||
|
|
outcome = self.outcome,
|
||
|
|
"🎥 stream_camera lifecycle ended"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Maps expected remote-audio availability failures onto retryable gRPC status codes.
|
||
|
|
fn remote_audio_status(message: String) -> Status {
|
||
|
|
if message.contains("remote USB gadget is not attached") {
|
||
|
|
Status::unavailable(message)
|
||
|
|
} else {
|
||
|
|
Status::internal(message)
|
||
|
|
}
|
||
|
|
}
|