use crate::MediaFrame;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct SegmentDecision {
pub skip: bool,
pub cut_previous: Option<f64>,
pub open_new: bool,
}
#[derive(Debug, Clone)]
pub struct SegmentClock {
target_ms: i64,
started: bool,
seg_start_pts: i64,
last_pts: i64,
}
impl SegmentClock {
pub fn new(target_secs: u64) -> Self {
Self {
target_ms: (target_secs.max(1) * 1000) as i64,
started: false,
seg_start_pts: 0,
last_pts: 0,
}
}
pub fn is_open(&self) -> bool {
self.started
}
pub fn observe(&mut self, frame: &MediaFrame) -> SegmentDecision {
if !self.started {
if !frame.is_keyframe() {
return SegmentDecision {
skip: true,
..SegmentDecision::default()
};
}
self.started = true;
self.seg_start_pts = frame.pts;
self.last_pts = frame.pts;
return SegmentDecision {
skip: false,
cut_previous: None,
open_new: true,
};
}
let mut decision = SegmentDecision::default();
if frame.is_keyframe() {
let elapsed_ms = (frame.pts - self.seg_start_pts).max(0);
if elapsed_ms >= self.target_ms {
decision.cut_previous = Some(elapsed_ms as f64 / 1000.0);
decision.open_new = true;
self.seg_start_pts = frame.pts;
}
}
self.last_pts = frame.pts;
decision
}
pub fn flush(&mut self) -> Option<f64> {
if !self.started {
return None;
}
self.started = false;
Some((self.last_pts - self.seg_start_pts).max(0) as f64 / 1000.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CodecId;
use bytes::Bytes;
fn frame(pts: i64, key: bool) -> MediaFrame {
MediaFrame::new_video(pts, pts, Bytes::from_static(b"x"), CodecId::H264, key)
}
#[test]
fn skips_until_first_keyframe() {
let mut c = SegmentClock::new(2);
assert!(c.observe(&frame(0, false)).skip);
let d = c.observe(&frame(10, true));
assert!(!d.skip && d.open_new && d.cut_previous.is_none());
assert!(c.is_open());
}
#[test]
fn cuts_on_keyframe_after_target_and_flushes_tail() {
let mut c = SegmentClock::new(2);
c.observe(&frame(0, true)); assert_eq!(c.observe(&frame(1000, true)).cut_previous, None); let d = c.observe(&frame(2000, true)); assert_eq!(d.cut_previous, Some(2.0));
assert!(d.open_new);
c.observe(&frame(2500, false));
assert_eq!(c.flush(), Some(0.5)); assert_eq!(c.flush(), None); }
}