const PCR_HZ: u64 = 27_000_000;
const PACKET_BITS: u64 = 188 * 8;
pub(super) const PCR_WRAP: u64 = (1 << 33) * 300;
const WINDOW: u64 = 2 * PCR_HZ;
const SAMPLE: u64 = WINDOW / 4;
pub(super) const MAX_INTERVAL: u64 = PCR_HZ;
const AGREEMENT: u64 = 20;
const DRIFT: u64 = 10;
#[derive(Clone, Copy, Default)]
struct Interval {
packets: u64,
ticks: u64,
}
impl Interval {
fn add(&mut self, other: Interval) {
self.packets += other.packets;
self.ticks += other.ticks;
}
fn rate(&self) -> u64 {
rate(self.packets, self.ticks)
}
fn agrees(&self, rate: u64) -> bool {
self.rate().abs_diff(rate) * 1000 <= rate * AGREEMENT
}
}
fn rate(packets: u64, ticks: u64) -> u64 {
(u128::from(packets) * u128::from(PACKET_BITS) * u128::from(PCR_HZ) / u128::from(ticks)) as u64
}
#[derive(Default)]
pub(super) struct Meter {
packets: u64,
last: Option<u64>,
sample: Interval,
window: Vec<Interval>,
published: Option<u64>,
}
impl Meter {
pub fn published(&self) -> Option<u64> {
self.published
}
pub fn packet(&mut self) {
self.packets += 1;
}
pub fn pcr(&mut self, pcr: u64) -> bool {
let packets = std::mem::take(&mut self.packets);
let Some(last) = self.last.replace(pcr) else {
return false;
};
let ticks = (pcr + PCR_WRAP - last) % PCR_WRAP;
if ticks == 0 || ticks > MAX_INTERVAL {
return self.discontinuity();
}
self.sample.add(Interval { packets, ticks });
if self.sample.ticks < SAMPLE {
return false;
}
self.window.push(std::mem::take(&mut self.sample));
if self.window.iter().map(|s| s.ticks).sum::<u64>() < WINDOW {
return false;
}
self.judge()
}
pub fn discontinuity(&mut self) -> bool {
self.packets = 0;
self.last = None;
self.sample = Interval::default();
self.window.clear();
self.published.take().is_some()
}
fn judge(&mut self) -> bool {
let window = std::mem::take(&mut self.window);
let mut whole = Interval::default();
window.iter().for_each(|s| whole.add(*s));
let rate = whole.rate();
let stable = window.iter().all(|s| s.agrees(rate));
match self.published {
None if stable => {
self.published = Some(rate);
true
}
None => false,
Some(published) if stable => {
let drifted = rate.abs_diff(published) * 1000 > published * DRIFT;
if drifted {
self.published = Some(rate);
}
drifted
}
Some(published) => {
let gone = !window.iter().any(|s| s.agrees(published));
if gone {
self.published = None;
}
gone
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn feed(meter: &mut Meter, intervals: impl IntoIterator<Item = (u64, u64)>) -> Vec<Option<u64>> {
let mut changes = Vec::new();
let mut pcr = meter.last.unwrap_or(0);
if meter.last.is_none() {
meter.packet();
assert!(!meter.pcr(pcr));
}
for (packets, ticks) in intervals {
for _ in 0..packets {
meter.packet();
}
pcr = (pcr + ticks) % PCR_WRAP;
if meter.pcr(pcr) {
changes.push(meter.published());
}
}
changes
}
const CBR: (u64, u64) = (44, 714_701);
const PER_WINDOW: usize = 4 * SAMPLE.div_ceil(CBR.1) as usize;
#[test]
fn cbr_publishes_once_per_window() {
let mut meter = Meter::default();
let changes = feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW * 3));
assert_eq!(changes, vec![Some(2_499_999)], "published once, then held");
}
#[test]
fn vbr_never_publishes() {
let mut meter = Meter::default();
let intervals = (0..PER_WINDOW as u64 * 3).map(|i| (if (i / 7) % 2 == 0 { 10 } else { 60 }, 714_701));
assert!(feed(&mut meter, intervals).is_empty());
assert_eq!(meter.published(), None);
}
#[test]
fn a_glitch_holds_the_rate_and_a_new_rate_replaces_it() {
let mut meter = Meter::default();
assert_eq!(
feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW)),
vec![Some(2_499_999)]
);
let glitch = std::iter::once((200, 714_701)).chain(std::iter::repeat_n(CBR, PER_WINDOW - 1));
assert!(feed(&mut meter, glitch).is_empty());
assert_eq!(meter.published(), Some(2_499_999));
let noise = std::iter::repeat_n((44, 711_000), PER_WINDOW);
assert!(feed(&mut meter, noise).is_empty());
let faster = std::iter::repeat_n((88, 714_701), PER_WINDOW);
assert_eq!(feed(&mut meter, faster), vec![Some(4_999_998)]);
}
#[test]
fn instability_clears_and_a_fresh_window_republishes() {
let mut meter = Meter::default();
assert_eq!(
feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW)),
vec![Some(2_499_999)]
);
let vbr = (0..PER_WINDOW as u64).map(|i| (if (i / 7) % 2 == 0 { 10 } else { 30 }, 714_701));
assert_eq!(feed(&mut meter, vbr), vec![None]);
let changes = feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW - 1));
assert!(changes.is_empty(), "no publish before the window fills");
assert_eq!(feed(&mut meter, [CBR]), vec![Some(2_499_999)]);
}
#[test]
fn discontinuity_clears_immediately() {
let mut meter = Meter::default();
assert_eq!(
feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW)),
vec![Some(2_499_999)]
);
assert!(meter.discontinuity(), "a published rate is cleared");
assert_eq!(meter.published(), None);
assert!(!meter.discontinuity(), "nothing left to clear");
assert_eq!(
feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW)),
vec![Some(2_499_999)]
);
meter.packet();
assert!(meter.pcr(1), "the clock jumped backwards");
assert_eq!(meter.published(), None);
}
#[test]
fn survives_the_pcr_wrap() {
let mut meter = Meter {
last: Some(PCR_WRAP - CBR.1 * 3),
..Default::default()
};
let changes = feed(&mut meter, std::iter::repeat_n(CBR, PER_WINDOW));
assert_eq!(changes, vec![Some(2_499_999)]);
}
}