use crate::seek::SeekIndexEntry;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PreRollAction {
Decode,
Present,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PreRollSample {
pub entry: SeekIndexEntry,
pub action: PreRollAction,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PreRollSeekPlan {
pub keyframe: SeekIndexEntry,
pub target_pts: i64,
pub samples: Vec<PreRollSample>,
pub discard_count: u32,
pub present_count: u32,
pub file_offset: u64,
}
impl PreRollSeekPlan {
#[must_use]
pub fn is_immediate(&self) -> bool {
self.discard_count == 0
}
#[must_use]
pub fn total_samples(&self) -> usize {
self.samples.len()
}
pub fn discard_samples(&self) -> impl Iterator<Item = &PreRollSample> {
self.samples
.iter()
.filter(|s| matches!(s.action, PreRollAction::Decode))
}
pub fn present_samples(&self) -> impl Iterator<Item = &PreRollSample> {
self.samples
.iter()
.filter(|s| matches!(s.action, PreRollAction::Present))
}
#[must_use]
pub fn first_present_pts(&self) -> Option<i64> {
self.present_samples().next().map(|s| s.entry.pts)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::seek::{SampleAccurateSeeker, SampleIndex, SeekIndexEntry};
fn build_sample_index() -> SampleIndex {
let mut idx = SampleIndex::new(90000);
for i in 0u32..10 {
let pts = i64::from(i) * 3000;
let entry = if i % 5 == 0 {
SeekIndexEntry::keyframe(pts, pts, u64::from(i) * 500, 200, 3000, i)
} else {
SeekIndexEntry::non_keyframe(pts, pts, u64::from(i) * 500, 200, 3000, i)
};
idx.add_entry(entry);
}
idx.finalize();
idx
}
#[test]
fn test_preroll_action_variants() {
assert_ne!(PreRollAction::Decode, PreRollAction::Present);
}
#[test]
fn test_preroll_seek_plan_is_immediate() {
let kf = SeekIndexEntry::keyframe(0, 0, 0, 200, 3000, 0);
let plan = PreRollSeekPlan {
keyframe: kf,
target_pts: 0,
samples: vec![PreRollSample {
entry: kf,
action: PreRollAction::Present,
}],
discard_count: 0,
present_count: 1,
file_offset: 0,
};
assert!(plan.is_immediate());
assert_eq!(plan.total_samples(), 1);
}
#[test]
fn test_preroll_plan_with_discards() {
let kf = SeekIndexEntry::keyframe(0, 0, 0, 200, 3000, 0);
let inter = SeekIndexEntry::non_keyframe(3000, 3000, 200, 200, 3000, 1);
let target = SeekIndexEntry::non_keyframe(6000, 6000, 400, 200, 3000, 2);
let plan = PreRollSeekPlan {
keyframe: kf,
target_pts: 6000,
samples: vec![
PreRollSample {
entry: kf,
action: PreRollAction::Decode,
},
PreRollSample {
entry: inter,
action: PreRollAction::Decode,
},
PreRollSample {
entry: target,
action: PreRollAction::Present,
},
],
discard_count: 2,
present_count: 1,
file_offset: 0,
};
assert!(!plan.is_immediate());
assert_eq!(plan.total_samples(), 3);
assert_eq!(plan.discard_samples().count(), 2);
assert_eq!(plan.present_samples().count(), 1);
assert_eq!(plan.first_present_pts(), Some(6000));
}
#[test]
fn test_preroll_seek_on_keyframe() {
let mut seeker = SampleAccurateSeeker::new();
seeker.add_stream(0, build_sample_index());
let plan = seeker.plan_preroll_seek(0, 0, None).expect("should plan");
assert!(plan.is_immediate());
assert_eq!(plan.keyframe.pts, 0);
assert!(plan.present_count >= 1);
}
#[test]
fn test_preroll_seek_between_keyframes() {
let mut seeker = SampleAccurateSeeker::new();
seeker.add_stream(0, build_sample_index());
let plan = seeker
.plan_preroll_seek(0, 9000, None)
.expect("should plan");
assert_eq!(plan.keyframe.pts, 0);
assert!(!plan.is_immediate());
assert!(plan.discard_count > 0);
assert!(plan.present_count >= 1);
let first_present = plan.first_present_pts().expect("should have present");
assert!(first_present <= 9000);
}
#[test]
fn test_preroll_seek_with_max_limit() {
let mut seeker = SampleAccurateSeeker::new();
seeker.add_stream(0, build_sample_index());
let plan = seeker
.plan_preroll_seek(0, 12000, Some(2))
.expect("should plan");
assert!(plan.total_samples() <= 3);
}
#[test]
fn test_preroll_seek_unknown_stream() {
let seeker = SampleAccurateSeeker::new();
let plan = seeker.plan_preroll_seek(99, 0, None);
assert!(plan.is_none());
}
#[test]
fn test_preroll_count() {
let mut seeker = SampleAccurateSeeker::new();
seeker.add_stream(0, build_sample_index());
let count = seeker.preroll_count(0, 9000);
assert!(count.is_some());
assert!(count.expect("should have count") > 0);
}
#[test]
fn test_preroll_plan_iterators() {
let mut seeker = SampleAccurateSeeker::new();
seeker.add_stream(0, build_sample_index());
let plan = seeker
.plan_preroll_seek(0, 9000, None)
.expect("should plan");
let discard_count = plan.discard_samples().count();
let present_count = plan.present_samples().count();
assert_eq!(discard_count, plan.discard_count as usize);
assert_eq!(present_count, plan.present_count as usize);
assert_eq!(discard_count + present_count, plan.total_samples());
}
}