107 lines
3.7 KiB
Rust
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();
|
|
}
|