Skip to content
File

Blob: firmware/vendor/str0m/src/bwe/smoother.rs

rust97 lines
1use std::collections::VecDeque;
2use std::time::{Duration, Instant};
3 
4use crate::rtp_::Bitrate;
5 
6/// Time window for averaging estimate changes.
7const ESTIMATE_WINDOW: Duration = Duration::from_secs(3);
8 
9type TimeBitrate = (Instant, Bitrate);
10 
11const TOLERANCE: f64 = 0.05;
12 
13/// Smooths BWE estimates by averaging over a time window.
14pub struct EstimateSmoother {
15 estimates: VecDeque<TimeBitrate>,
16 maybe_emit: bool,
17 emitted: Option<Bitrate>,
18}
19 
20impl EstimateSmoother {
21 pub fn new() -> Self {
22 Self {
23 estimates: VecDeque::new(),
24 maybe_emit: false,
25 emitted: None,
26 }
27 }
28 
29 /// Record a new estimate and update the smoothed average.
30 pub fn record(&mut self, now: Instant, estimate: Bitrate) {
31 // Did value change from previous?
32 let do_update = self.estimates.back().map(|b| b.1) != Some(estimate);
33 
34 if do_update {
35 self.maybe_emit = true;
36 self.estimates.push_back((now, estimate));
37 }
38 
39 // Remove entries older than the window.
40 while let Some((time, _)) = self.estimates.front() {
41 if now.duration_since(*time) > ESTIMATE_WINDOW {
42 // Keep last entry
43 if self.estimates.len() == 1 {
44 break;
45 }
46 
47 self.maybe_emit = true;
48 self.estimates.pop_front();
49 } else {
50 break;
51 }
52 }
53 }
54 
55 /// Poll for an estimate to emit. Returns Some only when there's a new value to emit.
56 pub fn poll(&mut self) -> Option<Bitrate> {
57 if !self.maybe_emit {
58 return None;
59 }
60 
61 if self.estimates.is_empty() {
62 return None;
63 }
64 
65 let total: f64 = self.estimates.iter().map(|b| b.1.as_f64()).sum();
66 let avg = total / self.estimates.len() as f64;
67 let rate: Bitrate = avg.into();
68 
69 // This forces emitting if we have the first ever value, or a last
70 // where the rest of the window is gone (estimates stop coming).
71 let force = self.estimates.len() == 1 && self.emitted != Some(rate);
72 
73 // Emit if we deviate enough from previously emitted.
74 let deviate = if let Some(emitted) = self.emitted {
75 !in_tolerance(emitted, rate)
76 } else {
77 true
78 };
79 
80 self.maybe_emit = false;
81 
82 // Are we not to emit?
83 if !force && !deviate {
84 return None;
85 }
86 
87 self.emitted = Some(rate);
88 Some(rate)
89 }
90}
91 
92fn in_tolerance(b1: Bitrate, b2: Bitrate) -> bool {
93 let min = b1 * (1.0 - TOLERANCE);
94 let max = b1 * (1.0 + TOLERANCE);
95 b2 > min && b2 < max
96}