#[allow(dead_code)] const DEFAULT_DISCONTINUITY_THRESHOLD_NS: u64 = 5_000_000_000;
#[allow(dead_code)] pub(crate) struct PtsTracker {
last_pts_ns: Option<u64>,
frame_count: u64,
discontinuity_threshold_ns: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)] pub(crate) enum PtsResult {
First,
Normal {
delta_ns: u64,
},
Discontinuity {
gap_ns: u64,
prev_ns: u64,
current_ns: u64,
},
}
impl PtsTracker {
#[allow(dead_code)] pub fn new() -> Self {
Self {
last_pts_ns: None,
frame_count: 0,
discontinuity_threshold_ns: DEFAULT_DISCONTINUITY_THRESHOLD_NS,
}
}
#[allow(dead_code)] pub fn observe(&mut self, pts_ns: u64) -> PtsResult {
self.frame_count += 1;
let result = match self.last_pts_ns {
None => PtsResult::First,
Some(prev) => {
let gap = pts_ns.abs_diff(prev);
if gap > self.discontinuity_threshold_ns {
PtsResult::Discontinuity {
gap_ns: gap,
prev_ns: prev,
current_ns: pts_ns,
}
} else {
PtsResult::Normal { delta_ns: gap }
}
}
};
self.last_pts_ns = Some(pts_ns);
result
}
}
#[cfg(test)]
impl PtsTracker {
pub fn with_threshold_ns(threshold_ns: u64) -> Self {
Self {
last_pts_ns: None,
frame_count: 0,
discontinuity_threshold_ns: threshold_ns,
}
}
pub fn frame_count(&self) -> u64 {
self.frame_count
}
pub fn reset(&mut self) {
self.last_pts_ns = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_frame_is_first() {
let mut t = PtsTracker::new();
assert_eq!(t.observe(0), PtsResult::First);
assert_eq!(t.frame_count(), 1);
}
#[test]
fn normal_progression() {
let mut t = PtsTracker::new();
t.observe(0);
let r = t.observe(33_333_333); assert_eq!(
r,
PtsResult::Normal {
delta_ns: 33_333_333
}
);
}
#[test]
fn forward_discontinuity() {
let mut t = PtsTracker::with_threshold_ns(1_000_000_000); t.observe(0);
let r = t.observe(2_000_000_000); assert!(matches!(
r,
PtsResult::Discontinuity {
gap_ns: 2_000_000_000,
..
}
));
}
#[test]
fn backward_discontinuity() {
let mut t = PtsTracker::with_threshold_ns(1_000_000_000);
t.observe(5_000_000_000);
let r = t.observe(1_000_000_000); assert!(matches!(
r,
PtsResult::Discontinuity {
gap_ns: 4_000_000_000,
..
}
));
}
#[test]
fn reset_makes_next_frame_first() {
let mut t = PtsTracker::new();
t.observe(100);
t.reset();
assert_eq!(t.observe(200), PtsResult::First);
assert_eq!(t.frame_count(), 2);
}
#[test]
fn small_jitter_is_normal() {
let mut t = PtsTracker::with_threshold_ns(1_000_000_000);
t.observe(1_000_000_000);
let r = t.observe(1_500_000_000);
assert!(matches!(r, PtsResult::Normal { .. }));
}
#[test]
fn threshold_boundary_is_normal() {
let mut t = PtsTracker::with_threshold_ns(1_000_000_000);
t.observe(0);
let r = t.observe(1_000_000_000);
assert!(matches!(r, PtsResult::Normal { .. }));
}
}