use std::collections::VecDeque;
use std::time::{Duration, Instant};
use crate::rtp_::Bitrate;
const ESTIMATE_WINDOW: Duration = Duration::from_secs(3);
type TimeBitrate = (Instant, Bitrate);
const TOLERANCE: f64 = 0.05;
pub struct EstimateSmoother {
esimates: VecDeque<TimeBitrate>,
maybe_emit: bool,
emitted: Option<Bitrate>,
}
impl EstimateSmoother {
pub fn new() -> Self {
Self {
esimates: VecDeque::new(),
maybe_emit: false,
emitted: None,
}
}
pub fn record(&mut self, now: Instant, estimate: Bitrate) {
let do_update = self.esimates.back().map(|b| b.1) != Some(estimate);
if do_update {
self.maybe_emit = true;
self.esimates.push_back((now, estimate));
}
while let Some((time, _)) = self.esimates.front() {
if now.duration_since(*time) > ESTIMATE_WINDOW {
if self.esimates.len() == 1 {
break;
}
self.maybe_emit = true;
self.esimates.pop_front();
} else {
break;
}
}
}
pub fn poll(&mut self) -> Option<Bitrate> {
if !self.maybe_emit {
return None;
}
if self.esimates.is_empty() {
return None;
}
let total: f64 = self.esimates.iter().map(|b| b.1.as_f64()).sum();
let avg = total / self.esimates.len() as f64;
let rate: Bitrate = avg.into();
let force = self.esimates.len() == 1 && self.emitted != Some(rate);
let deviate = if let Some(emitted) = self.emitted {
!in_tolerance(emitted, rate)
} else {
true
};
self.maybe_emit = false;
if !force && !deviate {
return None;
}
self.emitted = Some(rate);
Some(rate)
}
}
fn in_tolerance(b1: Bitrate, b2: Bitrate) -> bool {
let min = b1 * (1.0 - TOLERANCE);
let max = b1 * (1.0 + TOLERANCE);
b2 > min && b2 < max
}