#![expect(clippy::unwrap_used)]
use std::path::PathBuf;
use std::time::Duration;
use re_chunk::{Chunk, EntityPath};
use re_mp4_reader::{
Mode, Mp4Config, Mp4TranscodeOptions, TimeWindow, VideoCodec, load_mp4, load_mp4_from_bytes,
};
use re_sdk_types::archetypes::VideoStream;
use re_sdk_types::components::IsKeyframe;
const STREAMABLE_FIXTURES: &[&str] = &[
"Big_Buck_Bunny_1080_1s_h264_nobframes.mp4",
"Big_Buck_Bunny_1080_1s_h265_nobframes.mp4",
"Big_Buck_Bunny_1080_1s_av1.mp4",
"Big_Buck_Bunny_1080_1s_vp8.mp4",
"Big_Buck_Bunny_1080_1s_vp9.mp4",
];
fn fixture_path(file_name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.parent())
.expect("path exists")
.join("tests/assets/video")
.join(file_name)
}
fn stream_config() -> Mp4Config {
Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default(),
time_window: None,
},
..Default::default()
}
}
fn collect_chunks(
iter: impl Iterator<Item = Result<Chunk, re_mp4_reader::Mp4Error>>,
label: &str,
) -> Vec<Chunk> {
iter.map(|c| c.unwrap_or_else(|err| panic!("chunk error in {label}: {err}")))
.collect()
}
fn is_sample_chunk(chunk: &Chunk) -> bool {
chunk
.components()
.contains_component(VideoStream::descriptor_sample().component)
}
fn sample_chunks(chunks: &[Chunk]) -> Vec<&Chunk> {
chunks
.iter()
.filter(|c| !c.is_static() && is_sample_chunk(c))
.collect()
}
fn sample_times(chunks: &[Chunk]) -> Vec<i64> {
let mut times = Vec::new();
for chunk in sample_chunks(chunks) {
assert_eq!(chunk.timelines().len(), 1);
times.extend_from_slice(chunk.timelines().values().next().unwrap().times_raw());
}
times
}
fn keyframe_marker_times(chunks: &[Chunk], label: &str) -> Vec<i64> {
let markers: Vec<&Chunk> = chunks
.iter()
.filter(|c| {
!is_sample_chunk(c)
&& c.components()
.contains_component(VideoStream::descriptor_is_keyframe().component)
})
.collect();
assert_eq!(
markers.len(),
1,
"{label}: expected exactly one dedicated IsKeyframe marker chunk"
);
let marker = markers[0];
assert!(
!marker.is_static(),
"{label}: the keyframe marker must be temporal"
);
let flags: Vec<bool> = marker
.iter_component::<IsKeyframe>(VideoStream::descriptor_is_keyframe().component)
.flat_map(|batch| batch.iter().map(|kf| bool::from(kf.0)).collect::<Vec<_>>())
.collect();
assert!(
flags.iter().all(|&kf| kf),
"{label}: the marker is sparse — only `true` may be logged, got {flags:?}"
);
assert_eq!(marker.timelines().len(), 1);
marker
.timelines()
.values()
.next()
.unwrap()
.times_raw()
.to_vec()
}
#[test]
fn streaming_from_path_matches_in_memory() {
let entity_path = EntityPath::from("video");
for file_name in STREAMABLE_FIXTURES {
let path = fixture_path(file_name);
let from_path = collect_chunks(
load_mp4(&path, &stream_config(), &entity_path).unwrap(),
file_name,
);
let bytes = std::fs::read(&path).unwrap();
let from_bytes = collect_chunks(
load_mp4_from_bytes(bytes, &stream_config(), &entity_path, file_name).unwrap(),
file_name,
);
assert!(
from_path.len() >= 3,
"{file_name}: expected at least a codec chunk, one GOP chunk and the keyframe marker, got {}",
from_path.len()
);
assert_eq!(
from_path.len(),
from_bytes.len(),
"{file_name}: chunk count differs between path and in-memory streaming"
);
for (i, (a, b)) in std::iter::zip(&from_path, &from_bytes).enumerate() {
assert_eq!(
a.timelines(),
b.timelines(),
"{file_name}: timeline mismatch in chunk {i}"
);
assert_eq!(
a.components().0,
b.components().0,
"{file_name}: component data mismatch in chunk {i}"
);
}
}
}
#[test]
fn is_keyframe_is_a_sparse_marker_in_its_own_chunk() {
let entity_path = EntityPath::from("video");
for file_name in STREAMABLE_FIXTURES {
let chunks = collect_chunks(
load_mp4(&fixture_path(file_name), &stream_config(), &entity_path).unwrap(),
file_name,
);
for chunk in sample_chunks(&chunks) {
assert!(
!chunk
.components()
.contains_component(VideoStream::descriptor_is_keyframe().component),
"{file_name}: `is_keyframe` must not be co-located with sample bytes"
);
}
let keyframes = keyframe_marker_times(&chunks, file_name);
let num_gops = sample_chunks(&chunks).len();
assert_eq!(
keyframes.len(),
num_gops,
"{file_name}: expected one keyframe row per GOP chunk"
);
let gop_starts: Vec<i64> = sample_chunks(&chunks)
.into_iter()
.map(|c| c.timelines().values().next().unwrap().times_raw()[0])
.collect();
assert_eq!(
keyframes, gop_starts,
"{file_name}: keyframe times must be the GOP start times"
);
}
}
#[test]
fn b_frames_are_transcoded_into_a_video_stream() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264.mp4");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default(),
time_window: None,
},
..Default::default()
};
let iter = match load_mp4(&path, &config, &entity_path) {
Ok(iter) => iter,
Err(err) if err.to_string().contains("FFmpeg") => {
eprintln!("skipping: ffmpeg not available ({err})");
return;
}
Err(err) => panic!("unexpected error transcoding B-frames: {err}"),
};
let chunks = collect_chunks(iter, "h264_bframes");
let static_chunks = chunks.iter().filter(|c| c.is_static()).count();
assert_eq!(static_chunks, 1, "expected exactly one static codec chunk");
let expected_samples = total_sample_rows(&collect_chunks(
load_mp4(
&fixture_path("Big_Buck_Bunny_1080_1s_h264_nobframes.mp4"),
&stream_config(),
&entity_path,
)
.unwrap(),
"h264_nobframes",
));
let times = sample_times(&chunks);
assert_eq!(
times.len(),
expected_samples,
"transcoded sample count must match the source frame count"
);
assert!(
times.array_windows().all(|[a, b]| a < b),
"transcoded PTS must be strictly increasing (B-frames stripped): {times:?}"
);
let keyframes = keyframe_marker_times(&chunks, "h264_bframes");
assert!(!keyframes.is_empty());
assert!(
keyframes.iter().all(|kf| times.contains(kf)),
"every keyframe time must be one of the emitted sample times"
);
}
fn total_sample_rows(chunks: &[Chunk]) -> usize {
sample_chunks(chunks).into_iter().map(Chunk::num_rows).sum()
}
#[test]
fn b_frames_without_ffmpeg_reports_missing_ffmpeg() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264.mp4");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default()
.with_ffmpeg_override(PathBuf::from("/definitely/not/a/real/ffmpeg")),
time_window: None,
},
..Default::default()
};
let msg = match load_mp4(&path, &config, &entity_path) {
Ok(_) => panic!("expected a transcode error when ffmpeg is missing"),
Err(err) => err.to_string(),
};
assert!(
msg.contains("Couldn't find an installation of the FFmpeg executable"),
"expected the FFmpeg-not-installed message, got: {msg}"
);
}
#[test]
fn av1_source_transcodes_to_h264_output() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_av1.mp4");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default().with_output_codec(VideoCodec::H264),
time_window: None,
},
..Default::default()
};
let iter = match load_mp4(&path, &config, &entity_path) {
Ok(iter) => iter,
Err(err) if err.to_string().contains("FFmpeg") || err.to_string().contains("encoder") => {
eprintln!("skipping: ffmpeg/encoder not available ({err})");
return;
}
Err(err) => panic!("unexpected error transcoding AV1 → H.264: {err}"),
};
let chunks = collect_chunks(iter, "av1_to_h264");
let static_chunks = chunks.iter().filter(|c| c.is_static()).count();
assert_eq!(static_chunks, 1, "expected one static codec chunk");
assert!(
chunks.iter().any(|c| !c.is_static()),
"expected at least one sample chunk"
);
}
#[test]
fn requesting_the_source_codec_stays_on_the_direct_path() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264_nobframes.mp4");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default()
.with_output_codec(VideoCodec::H264)
.with_ffmpeg_override(PathBuf::from("/definitely/not/a/real/ffmpeg")),
time_window: None,
},
..Default::default()
};
let iter =
load_mp4(&path, &config, &entity_path).expect("no-op output_codec must not invoke ffmpeg");
let chunks = collect_chunks(iter, "h264_noop");
assert_eq!(
chunks.iter().filter(|c| c.is_static()).count(),
1,
"expected one static codec chunk"
);
assert!(
chunks.iter().any(|c| !c.is_static()),
"expected at least one sample chunk"
);
}
fn transcode_or_skip(
path: &std::path::Path,
transcode: Mp4TranscodeOptions,
time_window: Option<TimeWindow>,
label: &str,
) -> Option<Vec<Chunk>> {
let entity_path = EntityPath::from("video");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode,
time_window,
},
..Default::default()
};
let is_env_error = |err: &re_mp4_reader::Mp4Error| {
let msg = err.to_string();
msg.contains("FFmpeg") || msg.contains("encoder") || msg.contains("transcode")
};
let iter = match load_mp4(path, &config, &entity_path) {
Ok(iter) => iter,
Err(err) if is_env_error(&err) => {
eprintln!("skipping {label}: ffmpeg/encoder not available ({err})");
return None;
}
Err(err) => panic!("unexpected error in {label}: {err}"),
};
let mut chunks = Vec::new();
for item in iter {
match item {
Ok(chunk) => chunks.push(chunk),
Err(err) if is_env_error(&err) => {
eprintln!("skipping {label}: transcode failed at runtime ({err})");
return None;
}
Err(err) => panic!("chunk error in {label}: {err}"),
}
}
Some(chunks)
}
#[test]
fn gop_size_forces_keyframe_spacing() {
const GOP: usize = 10;
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264_nobframes.mp4");
let Some(chunks) = transcode_or_skip(
&path,
Mp4TranscodeOptions::default().with_gop_size(GOP as u32),
None,
"gop_spacing",
) else {
return;
};
let gop_sizes: Vec<usize> = sample_chunks(&chunks)
.into_iter()
.map(Chunk::num_rows)
.collect();
assert!(
gop_sizes.len() >= 2,
"gop_size={GOP} should force multiple GOPs on a 1s clip, got {gop_sizes:?}"
);
let keyframes = keyframe_marker_times(&chunks, "gop_spacing");
assert_eq!(
keyframes.len(),
gop_sizes.len(),
"expected one keyframe marker row per GOP, got {keyframes:?} for GOPs {gop_sizes:?}"
);
for (i, &n) in gop_sizes.iter().enumerate() {
if i + 1 < gop_sizes.len() {
assert_eq!(
n, GOP,
"GOP {i} should hold exactly {GOP} samples, got {n} (all: {gop_sizes:?})"
);
} else {
assert!(
(1..=GOP).contains(&n),
"the last GOP should hold 1..={GOP} samples, got {n}"
);
}
}
}
#[test]
fn transcodes_across_codec_pairs() {
let pairs = [
(
"Big_Buck_Bunny_1080_1s_h264_nobframes.mp4",
VideoCodec::AV1,
"h264_to_av1",
),
(
"Big_Buck_Bunny_1080_1s_h264_nobframes.mp4",
VideoCodec::VP9,
"h264_to_vp9",
),
(
"Big_Buck_Bunny_1080_1s_h265_nobframes.mp4",
VideoCodec::H264,
"h265_to_h264",
),
];
let mut ran = 0;
for (fixture, target, label) in pairs {
let path = fixture_path(fixture);
let Some(chunks) = transcode_or_skip(
&path,
Mp4TranscodeOptions::default().with_output_codec(target),
None,
label,
) else {
continue;
};
assert_eq!(
chunks.iter().filter(|c| c.is_static()).count(),
1,
"{label}: expected one static codec chunk"
);
let times = sample_times(&chunks);
assert!(!times.is_empty(), "{label}: expected sample chunks");
assert!(
times.array_windows().all(|[a, b]| a < b),
"{label}: transcoded PTS must be strictly increasing: {times:?}"
);
assert!(
!keyframe_marker_times(&chunks, label).is_empty(),
"{label}: expected a sparse keyframe marker"
);
ran += 1;
}
if ran == 0 {
eprintln!("skipping transcodes_across_codec_pairs: no output encoders available");
}
}
#[test]
fn aligned_window_needs_no_ffmpeg() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_av1.mp4");
let window = TimeWindow::new(Duration::ZERO, Duration::from_millis(500)).expect("valid window");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default()
.with_ffmpeg_override(PathBuf::from("/definitely/not/a/real/ffmpeg")),
time_window: Some(window),
},
..Default::default()
};
let chunks = collect_chunks(
load_mp4(&path, &config, &entity_path).expect("an aligned windowed read needs no ffmpeg"),
"windowed_direct",
);
assert!(
!sample_times(&chunks).is_empty(),
"the windowed direct read must emit samples"
);
}
#[test]
fn aligned_window_slices_the_direct_read_exactly() {
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264_nobframes.mp4");
let entity_path = EntityPath::from("video");
let full = collect_chunks(
load_mp4(&path, &stream_config(), &entity_path).expect("direct read"),
"windowed_slice_full",
);
let full_times = sample_times(&full);
let end_ns = 900_000_000_i64;
let window = TimeWindow::new(Duration::ZERO, Duration::from_millis(900)).expect("valid window");
let expected: Vec<i64> = full_times.iter().copied().filter(|&t| t < end_ns).collect();
assert!(
expected.len() < full_times.len(),
"the window must trim the tail for the law to be meaningful"
);
for chunk_by_gop in [true, false] {
let label = format!("windowed_slice(chunk_by_gop: {chunk_by_gop})");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop,
transcode: Mp4TranscodeOptions::default(),
time_window: Some(window),
},
..Default::default()
};
let windowed = collect_chunks(
load_mp4(&path, &config, &entity_path).expect("windowed direct read"),
&label,
);
assert_eq!(sample_times(&windowed), expected, "{label}");
assert_eq!(
keyframe_marker_times(&windowed, &label).first(),
Some(&0),
"{label}: the stream starts on the keyframe at the window start"
);
}
}
#[test]
fn mid_gop_window_requires_ffmpeg() {
let entity_path = EntityPath::from("video");
let path = fixture_path("Big_Buck_Bunny_1080_1s_av1.mp4");
let window = TimeWindow::new(Duration::from_millis(400), Duration::from_millis(900))
.expect("valid window");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default()
.with_ffmpeg_override(PathBuf::from("/definitely/not/a/real/ffmpeg")),
time_window: Some(window),
},
..Default::default()
};
let msg = match load_mp4(&path, &config, &entity_path) {
Ok(_) => {
panic!(
"a mid-GOP windowed read must re-encode its head, so it must fail without ffmpeg"
)
}
Err(err) => err.to_string(),
};
assert!(
msg.contains("Couldn't find an installation of the FFmpeg executable"),
"expected the FFmpeg-not-installed message, got: {msg}"
);
}
#[test]
fn mid_gop_window_is_frame_exact() {
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264_nobframes.mp4");
let entity_path = EntityPath::from("video");
let (start_ms, end_ms) = (400, 900);
let window = TimeWindow::new(
Duration::from_millis(start_ms),
Duration::from_millis(end_ms),
)
.expect("valid window");
let full_times = sample_times(&collect_chunks(
load_mp4(&path, &stream_config(), &entity_path).expect("direct read"),
"frame_exact_full",
));
let Some(exact) = transcode_or_skip(
&path,
Mp4TranscodeOptions::default(),
Some(window),
"exact_window",
) else {
return;
};
let times = sample_times(&exact);
let window_len_ns = i64::try_from(end_ms - start_ms).expect("small") * 1_000_000;
assert_eq!(
times.iter().filter(|&&t| t == 0).count(),
1,
"frame-exact: exactly one sample at t=0, got {times:?}"
);
assert!(
times.iter().all(|&t| (0..=window_len_ns).contains(&t)),
"every emitted time must lie within the window's length, got {times:?}"
);
assert!(
times.len() < full_times.len(),
"the windowed read must emit fewer samples than the full file"
);
}
#[test]
fn windowed_read_at_the_end_of_a_100_frame_gop() {
let dir = std::path::Path::new(env!("CARGO_TARGET_TMPDIR")).join("worst_case_gop");
std::fs::create_dir_all(&dir).expect("temp dir");
let path = dir.join("gop100.mp4");
let generated = std::process::Command::new("ffmpeg")
.args(["-y", "-f", "lavfi", "-i", "testsrc2=duration=4:rate=30"])
.args(["-c:v", "libx264", "-bf", "0", "-g", "100"])
.args(["-force_key_frames", "expr:eq(mod(n,100),0)"])
.args(["-x264-params", "scenecut=0"])
.args(["-pix_fmt", "yuv420p"])
.arg(&path)
.output();
match generated {
Ok(out) if out.status.success() => {}
_ => {
eprintln!("skipping windowed_read_at_the_end_of_a_100_frame_gop: ffmpeg not available");
return;
}
}
let entity_path = EntityPath::from("video");
let full = collect_chunks(
load_mp4(&path, &stream_config(), &entity_path).expect("direct read"),
"maximal_preroll_full",
);
let full_times = sample_times(&full);
let keyframes = keyframe_marker_times(&full, "maximal_preroll_full");
assert_eq!(full_times.len(), 120, "the fixture must have 120 frames");
assert_eq!(
keyframes.len(),
2,
"the fixture must have keyframes only at frames 0 and 100"
);
let start_ns = 3_300_000_000_i64; let window =
TimeWindow::new(Duration::from_millis(3300), Duration::from_secs(4)).expect("valid window");
let covering_keyframe = keyframes.iter().copied().filter(|&t| t <= start_ns).max();
assert_eq!(covering_keyframe, Some(0), "frame 0 covers frame 99");
let config = Mp4Config {
mode: Mode::Stream {
chunk_by_gop: true,
transcode: Mp4TranscodeOptions::default(),
time_window: Some(window),
},
..Default::default()
};
let windowed = collect_chunks(
load_mp4(&path, &config, &entity_path).expect("smart-cut read"),
"worst_case_gop",
);
let times = sample_times(&windowed);
assert_eq!(
times.iter().filter(|&&t| t == 0).count(),
1,
"frame-exact: exactly one sample at t=0 (frame 99, re-encoded as a keyframe)"
);
let expected_tail: Vec<i64> = full_times
.iter()
.copied()
.filter(|&t| t > start_ns)
.map(|t| t - start_ns)
.collect();
assert_eq!(
times[1..],
expected_tail,
"the smart cut's direct tail must equal the source's samples exactly"
);
assert_eq!(
keyframe_marker_times(&windowed, "worst_case_gop").first(),
Some(&0),
"the re-encoded head starts on a keyframe at t=0"
);
}
#[test]
fn windowed_transcode_emits_only_the_window() {
let path = fixture_path("Big_Buck_Bunny_1080_1s_h264.mp4");
let Some(full) = transcode_or_skip(
&path,
Mp4TranscodeOptions::default(),
None,
"windowed_transcode_full",
) else {
return;
};
let window = TimeWindow::new(Duration::from_millis(200), Duration::from_millis(600))
.expect("valid window");
let Some(windowed) = transcode_or_skip(
&path,
Mp4TranscodeOptions::default(),
Some(window),
"windowed_transcode",
) else {
return;
};
let window_len_ns = 400_000_000;
let times = sample_times(&windowed);
assert!(!times.is_empty(), "the window must not be empty");
assert!(
times.iter().all(|&t| (0..=window_len_ns).contains(&t)),
"every emitted time must be rebased to zero and lie within the window's length, got {times:?}"
);
assert!(
times.len() < sample_times(&full).len(),
"a windowed transcode must emit fewer samples than the full file"
);
}