Files

107 lines
3.7 KiB
Rust

//! Run through scripts/validate/playback-fixture.sh for real FFmpeg seek coverage.
use clipbin_core::store::Store;
use clipbin_media::{
decode::{Decoder, scale},
player::{Picture, Player},
};
use std::{
path::PathBuf,
thread,
time::{Duration, Instant},
};
fn fixture() -> Option<PathBuf> {
std::env::var_os("CLIPBIN_TEST_MEDIA_DIR").map(PathBuf::from)
}
#[test]
fn seeking_matches_sequential_frames_in_each_independent_segment() {
let Some(directory) = fixture() else {
return;
};
for segment in 0..3 {
let path = directory.join(format!("1/segment-{segment:03}.mkv"));
let mut sequential = Decoder::open(&path, 60).unwrap();
let mut expected = Vec::new();
while let Some((index, frame)) = sequential.next_frame().unwrap() {
expected.push((index, scale(&frame, (160, 90)).unwrap().bgra));
}
assert_eq!(expected.len(), 120);
for target in [0, 1, 59, 60, 61, 119] {
let mut decoder = Decoder::open(&path, 60).unwrap();
decoder
.seek(Duration::from_secs_f64(target as f64 / 60.))
.unwrap();
loop {
let (index, frame) = decoder.next_frame().unwrap().unwrap();
if index >= target {
assert_eq!(index, target);
assert_eq!(
scale(&frame, (160, 90)).unwrap().bgra,
expected[target as usize].1
);
break;
}
}
}
}
}
fn wait(player: &Player, at: Duration) -> clipbin_media::player::Snapshot {
let until = Instant::now() + Duration::from_secs(5);
loop {
let snapshot = player.snapshot();
if snapshot.time == at && !matches!(snapshot.picture, Picture::Loading) {
return snapshot;
}
assert!(Instant::now() < until, "seek timed out: {snapshot:?}");
thread::sleep(Duration::from_millis(10));
}
}
#[test]
fn player_crosses_boundaries_and_gaps_and_discards_superseded_seeks() {
let Some(directory) = fixture() else {
return;
};
let player = Player::new();
player.audio(true, 1.);
player.open(Store { root: directory }, 1, Duration::ZERO);
assert!(matches!(
wait(&player, Duration::ZERO).picture,
Picture::Frame(_)
));
for millis in [1999, 2000, 2500, 4000, 5999, 6000, 7000] {
let at = Duration::from_millis(millis);
player.seek(at);
let snapshot = wait(&player, at);
assert_eq!(
matches!(snapshot.picture, Picture::Gap),
(4000..6000).contains(&millis),
"at {millis}"
);
}
for i in 0..50 {
player.seek(Duration::from_millis(i * 40));
}
player.seek(Duration::from_millis(6500));
assert!(matches!(
wait(&player, Duration::from_millis(6500)).picture,
Picture::Frame(_)
));
player.seek(Duration::from_millis(1900));
wait(&player, Duration::from_millis(1900));
player.play(true);
thread::sleep(Duration::from_millis(400));
player.play(false);
let snapshot = player.snapshot();
assert!((Duration::from_millis(2250)..Duration::from_millis(2500)).contains(&snapshot.time));
assert!(matches!(snapshot.picture, Picture::Frame(_)));
assert!(snapshot.video_time.unwrap().abs_diff(snapshot.time) < Duration::from_millis(20));
player.seek(Duration::from_millis(3900));
wait(&player, Duration::from_millis(3900));
player.play(true);
thread::sleep(Duration::from_millis(350));
let snapshot = player.snapshot();
assert!(snapshot.time >= Duration::from_millis(4200));
assert!(matches!(snapshot.picture, Picture::Gap));
player.close();
}