use crate::rtpfb::acknowledgement::PacketReport;
use std::time::Instant;
pub trait BandwidthEstimator: Send + Sync {
fn on_reports(&mut self, now: Instant, reports: &[PacketReport]);
fn target_bitrate(&self) -> f64;
fn handle_timeout(&mut self, _now: Instant) {}
fn poll_timeout(&self) -> Option<Instant> {
None
}
fn stats(&self) -> EstimatorStats {
EstimatorStats::default()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[non_exhaustive]
pub struct EstimatorStats {
pub delay_based_bitrate: Option<f64>,
pub loss_based_bitrate: Option<f64>,
pub packet_loss: Option<f64>,
pub round_trip_time: Option<std::time::Duration>,
}
#[derive(Debug, Clone, Copy)]
pub struct ConstantBitrate {
bits_per_second: f64,
}
impl ConstantBitrate {
pub fn new(bits_per_second: f64) -> Self {
Self { bits_per_second }
}
}
impl BandwidthEstimator for ConstantBitrate {
fn on_reports(&mut self, _now: Instant, _reports: &[PacketReport]) {}
fn target_bitrate(&self) -> f64 {
self.bits_per_second
}
}
#[cfg(test)]
mod tests {
use super::*;
use rtcp::transport_feedbacks::cc_feedback_report::Ecn;
use std::time::Duration;
fn report(id: u64, arrived: bool, departure: Instant) -> PacketReport {
PacketReport {
ssrc: 1,
id,
rtp_sequence_number: id as u16,
is_twcc: true,
twcc_sequence_number: id as u16,
size: 1200,
arrived,
departure,
arrival: arrived.then(|| Duration::from_millis(10)),
ecn: Ecn::default(),
}
}
#[test]
fn an_estimator_needs_only_two_methods() {
struct Minimal(f64);
impl BandwidthEstimator for Minimal {
fn on_reports(&mut self, _now: Instant, reports: &[PacketReport]) {
let arrived = reports.iter().filter(|report| report.arrived).count();
self.0 = 100_000.0 * arrived as f64;
}
fn target_bitrate(&self) -> f64 {
self.0
}
}
let epoch = Instant::now();
let mut estimator = Minimal(0.0);
estimator.on_reports(epoch, &[report(1, true, epoch), report(2, false, epoch)]);
assert_eq!(100_000.0, estimator.target_bitrate());
assert_eq!(None, estimator.poll_timeout(), "the default is idle");
assert_eq!(EstimatorStats::default(), estimator.stats());
estimator.handle_timeout(epoch + Duration::from_secs(1));
}
#[test]
fn a_constant_estimator_ignores_what_it_is_told() {
let epoch = Instant::now();
let mut estimator = ConstantBitrate::new(750_000.0);
assert_eq!(750_000.0, estimator.target_bitrate());
estimator.on_reports(epoch, &[report(1, false, epoch)]);
estimator.handle_timeout(epoch + Duration::from_secs(10));
assert_eq!(
750_000.0,
estimator.target_bitrate(),
"loss and time must not move a rate the application fixed"
);
}
#[test]
fn an_estimator_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ConstantBitrate>();
assert_send_sync::<Box<dyn BandwidthEstimator>>();
}
}