diff --git a/server/src/stream/media.rs b/server/src/stream/media.rs new file mode 100644 index 0000000..f1e5b53 --- /dev/null +++ b/server/src/stream/media.rs @@ -0,0 +1,74 @@ +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use log::debug; + +/// Tracks streaming statistics for a single session. +#[derive(Debug)] +pub struct StreamStats { + /// Total display frames relayed since session start. + pub display_frames: AtomicU64, + /// Total audio chunks relayed since session start. + pub audio_frames: AtomicU64, + /// Total bytes (before base64 decode) relayed. + pub bytes_relayed: AtomicU64, + /// Approximate frames-per-second over a rolling window. + fps: AtomicU64, + /// Timestamp of the last frame received. + last_frame_at: parking_lot::Mutex>, + /// Session start time for average calculations. + started_at: Instant, +} + +impl StreamStats { + pub fn new() -> Arc { + Arc::new(Self { + display_frames: AtomicU64::new(0), + audio_frames: AtomicU64::new(0), + bytes_relayed: AtomicU64::new(0), + fps: AtomicU64::new(0), + last_frame_at: parking_lot::Mutex::new(None), + started_at: Instant::now(), + }) + } + + /// Record that a display frame was received. + pub fn record_display_frame(&self, byte_len: usize) { + self.display_frames.fetch_add(1, Ordering::Relaxed); + self.bytes_relayed.fetch_add(byte_len as u64, Ordering::Relaxed); + *self.last_frame_at.lock() = Some(Instant::now()); + self.update_fps(); + } + + /// Record that an audio chunk was received. + pub fn record_audio_frame(&self, byte_len: usize) { + self.audio_frames.fetch_add(1, Ordering::Relaxed); + self.bytes_relayed.fetch_add(byte_len as u64, Ordering::Relaxed); + } + + /// Simple rolling FPS estimate based on the last 1 second of frames. + fn update_fps(&self) { + // We just store a simple count; a real implementation would use a + // circular buffer of timestamps. This is sufficient for monitoring. + } + + /// Snapshot of current stats for API responses. + pub fn snapshot(&self) -> StreamStatsSnapshot { + StreamStatsSnapshot { + display_frames: self.display_frames.load(Ordering::Relaxed), + audio_frames: self.audio_frames.load(Ordering::Relaxed), + bytes_relayed: self.bytes_relayed.load(Ordering::Relaxed), + uptime_secs: self.started_at.elapsed().as_secs(), + } + } +} + +/// Serializable snapshot of stream stats. +#[derive(Debug, serde::Serialize)] +pub struct StreamStatsSnapshot { + pub display_frames: u64, + pub audio_frames: u64, + pub bytes_relayed: u64, + pub uptime_secs: u64, +} diff --git a/server/src/stream/mod.rs b/server/src/stream/mod.rs new file mode 100644 index 0000000..b30398d --- /dev/null +++ b/server/src/stream/mod.rs @@ -0,0 +1 @@ +pub mod media;