1use std::time::{Duration, Instant};
4
5#[derive(Debug, Clone, Default)]
7pub struct SessionStats {
8 pub bytes_received: u64,
10 pub bytes_sent: u64,
12 pub duration: Duration,
14 pub video_frames: u64,
16 pub audio_frames: u64,
18 pub keyframes: u64,
20 pub dropped_frames: u64,
22 pub bitrate: u64,
24}
25
26impl SessionStats {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn calculate_bitrate(&mut self) {
34 let secs = self.duration.as_secs();
35 if secs > 0 {
36 self.bitrate = (self.bytes_received * 8) / secs;
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct StreamStats {
44 pub stream_key: String,
46 pub started_at: Instant,
48 pub bytes_received: u64,
50 pub video_frames: u64,
52 pub audio_frames: u64,
54 pub keyframes: u64,
56 pub last_video_ts: u32,
58 pub last_audio_ts: u32,
60 pub video_codec: Option<String>,
62 pub audio_codec: Option<String>,
64 pub width: Option<u32>,
66 pub height: Option<u32>,
68 pub framerate: Option<f64>,
70 pub audio_sample_rate: Option<u32>,
72 pub audio_channels: Option<u8>,
74}
75
76impl StreamStats {
77 pub fn new(stream_key: String) -> Self {
78 Self {
79 stream_key,
80 started_at: Instant::now(),
81 bytes_received: 0,
82 video_frames: 0,
83 audio_frames: 0,
84 keyframes: 0,
85 last_video_ts: 0,
86 last_audio_ts: 0,
87 video_codec: None,
88 audio_codec: None,
89 width: None,
90 height: None,
91 framerate: None,
92 audio_sample_rate: None,
93 audio_channels: None,
94 }
95 }
96
97 pub fn duration(&self) -> Duration {
99 self.started_at.elapsed()
100 }
101
102 pub fn bitrate(&self) -> u64 {
104 let secs = self.duration().as_secs();
105 if secs > 0 {
106 (self.bytes_received * 8) / secs
107 } else {
108 0
109 }
110 }
111
112 pub fn calculated_framerate(&self) -> f64 {
114 let secs = self.duration().as_secs_f64();
115 if secs > 0.0 {
116 self.video_frames as f64 / secs
117 } else {
118 0.0
119 }
120 }
121}
122
123#[derive(Debug, Clone, Default)]
125pub struct ServerStats {
126 pub total_connections: u64,
128 pub active_connections: u64,
130 pub total_bytes_received: u64,
132 pub total_bytes_sent: u64,
134 pub active_streams: u64,
136 pub uptime: Duration,
138}
139
140impl ServerStats {
141 pub fn new() -> Self {
142 Self::default()
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use std::time::Duration;
150
151 #[test]
152 fn test_session_stats_new() {
153 let stats = SessionStats::new();
154 assert_eq!(stats.bytes_received, 0);
155 assert_eq!(stats.bytes_sent, 0);
156 assert_eq!(stats.video_frames, 0);
157 assert_eq!(stats.audio_frames, 0);
158 assert_eq!(stats.keyframes, 0);
159 assert_eq!(stats.dropped_frames, 0);
160 assert_eq!(stats.bitrate, 0);
161 }
162
163 #[test]
164 fn test_session_stats_calculate_bitrate() {
165 let mut stats = SessionStats::new();
166 stats.bytes_received = 1_000_000; stats.duration = Duration::from_secs(10);
168
169 stats.calculate_bitrate();
170
171 assert_eq!(stats.bitrate, 800_000);
173 }
174
175 #[test]
176 fn test_session_stats_calculate_bitrate_zero_duration() {
177 let mut stats = SessionStats::new();
178 stats.bytes_received = 1_000_000;
179 stats.duration = Duration::from_secs(0);
180
181 stats.calculate_bitrate();
182
183 assert_eq!(stats.bitrate, 0);
185 }
186
187 #[test]
188 fn test_stream_stats_new() {
189 let stats = StreamStats::new("test_stream".to_string());
190 assert_eq!(stats.stream_key, "test_stream");
191 assert_eq!(stats.bytes_received, 0);
192 assert_eq!(stats.video_frames, 0);
193 assert_eq!(stats.audio_frames, 0);
194 assert_eq!(stats.keyframes, 0);
195 assert_eq!(stats.last_video_ts, 0);
196 assert_eq!(stats.last_audio_ts, 0);
197 assert!(stats.video_codec.is_none());
198 assert!(stats.audio_codec.is_none());
199 }
200
201 #[test]
202 fn test_stream_stats_duration() {
203 let stats = StreamStats::new("test".to_string());
204
205 let duration = stats.duration();
207 assert!(duration.as_nanos() > 0 || duration == Duration::ZERO);
208 }
209
210 #[test]
211 fn test_stream_stats_bitrate_zero_duration() {
212 let stats = StreamStats::new("test".to_string());
213
214 let bitrate = stats.bitrate();
216 assert!(bitrate == 0 || bitrate > 0); }
219
220 #[test]
221 fn test_stream_stats_calculated_framerate() {
222 let stats = StreamStats::new("test".to_string());
223
224 let framerate = stats.calculated_framerate();
226 assert!(framerate >= 0.0);
227 }
228
229 #[test]
230 fn test_server_stats_new() {
231 let stats = ServerStats::new();
232 assert_eq!(stats.total_connections, 0);
233 assert_eq!(stats.active_connections, 0);
234 assert_eq!(stats.total_bytes_received, 0);
235 assert_eq!(stats.total_bytes_sent, 0);
236 assert_eq!(stats.active_streams, 0);
237 }
238
239 #[test]
240 fn test_stream_stats_with_data() {
241 let mut stats = StreamStats::new("live_stream".to_string());
242
243 stats.bytes_received = 5_000_000; stats.video_frames = 300;
246 stats.audio_frames = 500;
247 stats.keyframes = 10;
248 stats.last_video_ts = 10000;
249 stats.last_audio_ts = 10050;
250 stats.video_codec = Some("H.264".to_string());
251 stats.audio_codec = Some("AAC".to_string());
252 stats.width = Some(1920);
253 stats.height = Some(1080);
254 stats.framerate = Some(30.0);
255 stats.audio_sample_rate = Some(44100);
256 stats.audio_channels = Some(2);
257
258 assert_eq!(stats.video_codec, Some("H.264".to_string()));
259 assert_eq!(stats.width, Some(1920));
260 assert_eq!(stats.height, Some(1080));
261 assert_eq!(stats.audio_channels, Some(2));
262 }
263}