1use std::collections::VecDeque;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ResolutionBitrate {
11 pub width: u32,
13 pub height: u32,
15 pub bitrate_kbps: u32,
17 pub codec: String,
19}
20
21impl ResolutionBitrate {
22 #[must_use]
24 pub fn new(width: u32, height: u32, bitrate_kbps: u32, codec: impl Into<String>) -> Self {
25 Self {
26 width,
27 height,
28 bitrate_kbps,
29 codec: codec.into(),
30 }
31 }
32
33 #[must_use]
35 pub fn pixel_count(&self) -> u64 {
36 u64::from(self.width) * u64::from(self.height)
37 }
38
39 #[must_use]
41 pub fn aspect_ratio(&self) -> f32 {
42 self.width as f32 / self.height as f32
43 }
44}
45
46#[derive(Debug, Clone)]
48pub struct AbrLadder {
49 pub renditions: Vec<ResolutionBitrate>,
51}
52
53impl AbrLadder {
54 #[must_use]
56 pub fn new(mut renditions: Vec<ResolutionBitrate>) -> Self {
57 renditions.sort_by(|a, b| b.bitrate_kbps.cmp(&a.bitrate_kbps));
59 Self { renditions }
60 }
61
62 #[must_use]
66 pub fn optimal_bitrate_kbps(&self, bandwidth_kbps: u32) -> Option<&ResolutionBitrate> {
67 let threshold = (f64::from(bandwidth_kbps) * 0.8) as u32;
68 self.renditions.iter().find(|r| r.bitrate_kbps <= threshold)
69 }
70
71 #[must_use]
73 pub fn len(&self) -> usize {
74 self.renditions.len()
75 }
76
77 #[must_use]
79 pub fn is_empty(&self) -> bool {
80 self.renditions.is_empty()
81 }
82
83 #[must_use]
85 pub fn highest_quality(&self) -> Option<&ResolutionBitrate> {
86 self.renditions.first()
87 }
88
89 #[must_use]
91 pub fn lowest_quality(&self) -> Option<&ResolutionBitrate> {
92 self.renditions.last()
93 }
94}
95
96#[derive(Debug, Clone, Default)]
98pub struct AbrLadderGenerator;
99
100impl AbrLadderGenerator {
101 #[must_use]
103 pub fn new() -> Self {
104 Self
105 }
106
107 #[must_use]
112 pub fn generate(&self, target_resolution: (u32, u32), codec: &str) -> AbrLadder {
113 let (_target_w, target_h) = target_resolution;
114
115 let standard_rungs: &[(u32, u32, u32)] = &[
117 (1920, 1080, 8000),
118 (1280, 720, 4500),
119 (960, 540, 2000),
120 (640, 360, 800),
121 (426, 240, 300),
122 ];
123
124 let renditions = standard_rungs
125 .iter()
126 .filter(|(_, h, _)| *h <= target_h)
127 .map(|(w, h, kbps)| ResolutionBitrate::new(*w, *h, *kbps, codec))
128 .collect();
129
130 AbrLadder::new(renditions)
131 }
132}
133
134#[derive(Debug, Clone, Default)]
136pub struct PerTitleEncoding;
137
138impl PerTitleEncoding {
139 #[must_use]
141 pub fn new() -> Self {
142 Self
143 }
144
145 #[must_use]
149 pub fn analyze_complexity(frame_variance: &[f32]) -> f32 {
150 if frame_variance.is_empty() {
151 return 0.0;
152 }
153 let sum: f32 = frame_variance.iter().sum();
154 sum / frame_variance.len() as f32
155 }
156
157 #[must_use]
161 pub fn optimize_ladder(base_ladder: &AbrLadder, complexity: f32) -> AbrLadder {
162 let factor = complexity.clamp(0.5, 2.0);
164
165 let adjusted = base_ladder
166 .renditions
167 .iter()
168 .map(|r| {
169 let new_bitrate = ((r.bitrate_kbps as f32) * factor).round() as u32;
170 ResolutionBitrate::new(r.width, r.height, new_bitrate, r.codec.clone())
171 })
172 .collect();
173
174 AbrLadder::new(adjusted)
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub struct BandwidthPoint {
181 pub timestamp_ms: u64,
183 pub kbps: u32,
185}
186
187#[derive(Debug, Clone)]
189pub struct BandwidthEstimator {
190 samples: VecDeque<BandwidthPoint>,
191 capacity: usize,
192}
193
194impl BandwidthEstimator {
195 #[must_use]
197 pub fn new() -> Self {
198 Self {
199 samples: VecDeque::with_capacity(30),
200 capacity: 30,
201 }
202 }
203
204 pub fn add_sample(&mut self, point: BandwidthPoint) {
206 if self.samples.len() >= self.capacity {
207 self.samples.pop_front();
208 }
209 self.samples.push_back(point);
210 }
211
212 #[must_use]
214 pub fn sample_count(&self) -> usize {
215 self.samples.len()
216 }
217
218 #[must_use]
220 pub fn estimated_kbps(&self) -> Option<u32> {
221 if self.samples.is_empty() {
222 return None;
223 }
224 let sum: u64 = self.samples.iter().map(|s| u64::from(s.kbps)).sum();
225 Some((sum / self.samples.len() as u64) as u32)
226 }
227
228 #[must_use]
230 pub fn samples(&self) -> &VecDeque<BandwidthPoint> {
231 &self.samples
232 }
233}
234
235impl Default for BandwidthEstimator {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_resolution_bitrate_new() {
247 let r = ResolutionBitrate::new(1920, 1080, 8000, "h264");
248 assert_eq!(r.width, 1920);
249 assert_eq!(r.height, 1080);
250 assert_eq!(r.bitrate_kbps, 8000);
251 assert_eq!(r.codec, "h264");
252 }
253
254 #[test]
255 fn test_resolution_bitrate_pixel_count() {
256 let r = ResolutionBitrate::new(1920, 1080, 8000, "h264");
257 assert_eq!(r.pixel_count(), 1920 * 1080);
258 }
259
260 #[test]
261 fn test_resolution_bitrate_aspect_ratio() {
262 let r = ResolutionBitrate::new(1920, 1080, 8000, "h264");
263 let ar = r.aspect_ratio();
264 assert!((ar - 16.0 / 9.0).abs() < 0.01);
265 }
266
267 #[test]
268 fn test_abr_ladder_sorted_descending() {
269 let renditions = vec![
270 ResolutionBitrate::new(640, 360, 800, "h264"),
271 ResolutionBitrate::new(1920, 1080, 8000, "h264"),
272 ResolutionBitrate::new(1280, 720, 4500, "h264"),
273 ];
274 let ladder = AbrLadder::new(renditions);
275 assert_eq!(ladder.renditions[0].bitrate_kbps, 8000);
276 assert_eq!(ladder.renditions[1].bitrate_kbps, 4500);
277 assert_eq!(ladder.renditions[2].bitrate_kbps, 800);
278 }
279
280 #[test]
281 fn test_abr_ladder_optimal_bitrate() {
282 let gen = AbrLadderGenerator::new();
283 let ladder = gen.generate((1920, 1080), "h264");
284
285 let opt = ladder.optimal_bitrate_kbps(10000);
287 assert!(opt.is_some());
288 assert_eq!(opt.expect("should succeed in test").bitrate_kbps, 8000);
289
290 let opt2 = ladder.optimal_bitrate_kbps(5000);
292 assert!(opt2.is_some());
293 assert_eq!(opt2.expect("should succeed in test").bitrate_kbps, 2000);
294 }
295
296 #[test]
297 fn test_abr_ladder_optimal_bitrate_too_low() {
298 let gen = AbrLadderGenerator::new();
299 let ladder = gen.generate((1920, 1080), "h264");
300
301 let opt = ladder.optimal_bitrate_kbps(100);
303 assert!(opt.is_none());
304 }
305
306 #[test]
307 fn test_abr_ladder_empty_check() {
308 let ladder = AbrLadder::new(vec![]);
309 assert!(ladder.is_empty());
310 assert_eq!(ladder.len(), 0);
311 assert!(ladder.highest_quality().is_none());
312 assert!(ladder.lowest_quality().is_none());
313 }
314
315 #[test]
316 fn test_abr_ladder_generator_full_1080p() {
317 let gen = AbrLadderGenerator::new();
318 let ladder = gen.generate((1920, 1080), "h264");
319 assert_eq!(ladder.len(), 5);
320 assert_eq!(
321 ladder
322 .highest_quality()
323 .expect("should succeed in test")
324 .bitrate_kbps,
325 8000
326 );
327 assert_eq!(
328 ladder
329 .lowest_quality()
330 .expect("should succeed in test")
331 .bitrate_kbps,
332 300
333 );
334 }
335
336 #[test]
337 fn test_abr_ladder_generator_720p_limit() {
338 let gen = AbrLadderGenerator::new();
339 let ladder = gen.generate((1280, 720), "vp9");
340 for r in &ladder.renditions {
342 assert!(r.height <= 720);
343 }
344 }
345
346 #[test]
347 fn test_per_title_analyze_complexity_empty() {
348 let c = PerTitleEncoding::analyze_complexity(&[]);
349 assert_eq!(c, 0.0);
350 }
351
352 #[test]
353 fn test_per_title_analyze_complexity() {
354 let variances = vec![1.0, 2.0, 3.0, 4.0];
355 let c = PerTitleEncoding::analyze_complexity(&variances);
356 assert!((c - 2.5).abs() < 1e-5);
357 }
358
359 #[test]
360 fn test_per_title_optimize_ladder_clamping() {
361 let gen = AbrLadderGenerator::new();
362 let base = gen.generate((1920, 1080), "h264");
363
364 let optimized = PerTitleEncoding::optimize_ladder(&base, 3.0);
366 let base_highest = base
367 .highest_quality()
368 .expect("should succeed in test")
369 .bitrate_kbps;
370 let opt_highest = optimized
371 .highest_quality()
372 .expect("should succeed in test")
373 .bitrate_kbps;
374 assert_eq!(opt_highest, (base_highest as f32 * 2.0).round() as u32);
375 }
376
377 #[test]
378 fn test_per_title_optimize_ladder_lower() {
379 let gen = AbrLadderGenerator::new();
380 let base = gen.generate((1920, 1080), "h264");
381
382 let optimized = PerTitleEncoding::optimize_ladder(&base, 0.5);
384 let base_highest = base
385 .highest_quality()
386 .expect("should succeed in test")
387 .bitrate_kbps;
388 let opt_highest = optimized
389 .highest_quality()
390 .expect("should succeed in test")
391 .bitrate_kbps;
392 assert_eq!(opt_highest, (base_highest as f32 * 0.5).round() as u32);
393 }
394
395 #[test]
396 fn test_bandwidth_estimator_add_sample() {
397 let mut est = BandwidthEstimator::new();
398 assert_eq!(est.sample_count(), 0);
399 assert!(est.estimated_kbps().is_none());
400
401 est.add_sample(BandwidthPoint {
402 timestamp_ms: 0,
403 kbps: 5000,
404 });
405 assert_eq!(est.sample_count(), 1);
406 assert_eq!(est.estimated_kbps(), Some(5000));
407 }
408
409 #[test]
410 fn test_bandwidth_estimator_ring_buffer() {
411 let mut est = BandwidthEstimator::new();
412 for i in 0..35u64 {
413 est.add_sample(BandwidthPoint {
414 timestamp_ms: i * 1000,
415 kbps: 1000,
416 });
417 }
418 assert_eq!(est.sample_count(), 30);
420 }
421
422 #[test]
423 fn test_bandwidth_estimator_average() {
424 let mut est = BandwidthEstimator::new();
425 est.add_sample(BandwidthPoint {
426 timestamp_ms: 0,
427 kbps: 4000,
428 });
429 est.add_sample(BandwidthPoint {
430 timestamp_ms: 1000,
431 kbps: 6000,
432 });
433 assert_eq!(est.estimated_kbps(), Some(5000));
434 }
435}