#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
mod fixtures;
use ff_stream::{DashOutput, StreamError};
use fixtures::{DirGuard, create_test_video, tmp_dir};
use std::path::PathBuf;
use std::time::Duration;
fn run_dash_write(test_name: &str, segment_secs: u64) -> Option<(PathBuf, DirGuard)> {
let out_dir = tmp_dir(test_name);
let guard = DirGuard(out_dir.clone());
let input_path = out_dir.join("input.mp4");
if !create_test_video(&input_path) {
return None;
}
let result = DashOutput::new(out_dir.to_str().unwrap())
.input(input_path.to_str().unwrap())
.segment_duration(Duration::from_secs(segment_secs))
.build()
.expect("build should succeed")
.write();
match result {
Err(StreamError::Ffmpeg { code, message }) => {
println!("Skipping: DASH write failed: {message} (code={code})");
None
}
Err(e) => panic!("Unexpected error: {e}"),
Ok(()) => Some((out_dir, guard)),
}
}
#[test]
fn write_should_produce_manifest_and_segments() {
let Some((out_dir, _guard)) = run_dash_write("dash_write_test", 1) else {
return;
};
let manifest = out_dir.join("manifest.mpd");
assert!(manifest.exists(), "manifest.mpd should exist");
assert!(
std::fs::metadata(&manifest).unwrap().len() > 0,
"manifest.mpd should be non-empty"
);
let segments: Vec<_> = std::fs::read_dir(&out_dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| {
let name = e.file_name().to_string_lossy().to_string();
name.ends_with(".m4s") || name.ends_with(".mp4")
})
.filter(|e| e.file_name().to_string_lossy() != "manifest.mpd")
.collect();
assert!(
!segments.is_empty(),
"at least one segment file (.m4s or .mp4) should be present"
);
println!(
"DASH output: {} segments, manifest {} bytes",
segments.len(),
std::fs::metadata(&manifest).unwrap().len(),
);
}
#[test]
fn manifest_should_contain_required_dash_tags() {
let Some((out_dir, _guard)) = run_dash_write("dash_tags_test", 1) else {
return;
};
let content = std::fs::read_to_string(out_dir.join("manifest.mpd")).unwrap();
assert!(
content.contains("<?xml"),
"missing <?xml declaration in manifest"
);
assert!(content.contains("MPD"), "missing MPD element in manifest");
assert!(
content.contains("AdaptationSet"),
"missing AdaptationSet in manifest"
);
}
#[test]
fn representation_should_match_encoder_dimensions() {
let Some((out_dir, _guard)) = run_dash_write("dash_repr_test", 1) else {
return;
};
let content = std::fs::read_to_string(out_dir.join("manifest.mpd")).unwrap();
assert!(
content.contains("Representation"),
"missing Representation element"
);
assert!(
content.contains("width=\"320\""),
"Representation missing width=\"320\""
);
assert!(
content.contains("height=\"240\""),
"Representation missing height=\"240\""
);
}
#[test]
fn manifest_should_contain_single_adaptation_set() {
let Some((out_dir, _guard)) = run_dash_write("dash_single_as_test", 1) else {
return;
};
let content = std::fs::read_to_string(out_dir.join("manifest.mpd")).unwrap();
let count = content.matches("<AdaptationSet").count();
assert_eq!(count, 1, "expected exactly 1 AdaptationSet, got {count}");
}
#[test]
fn init_segment_should_be_present() {
let Some((out_dir, _guard)) = run_dash_write("dash_init_test", 1) else {
return;
};
let init = out_dir.join("init-stream0.m4s");
assert!(init.exists(), "init-stream0.m4s should exist");
assert!(
std::fs::metadata(&init).unwrap().len() > 0,
"init-stream0.m4s should be non-empty"
);
}