mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 11:13:55 +00:00
FilmCraft's MPEG audio Layer I/II decoder as `craft_audio::mpa` behind `mpa-decode`, kept over symphonia's: the same quality where both decode, and it also decodes the MP2 files ffmpeg writes by default (mono at 384 kbit/s), which symphonia rejects. `TrackDecoder` and the native read path take MP1/MP2 in MP4, Matroska and raw streams; symphonia keeps MP3 only, so its `mp1` and `mp2` features are dropped. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
76 lines
2.6 KiB
Rust
76 lines
2.6 KiB
Rust
//! The parts of FilmCraft's `filmcraft-testkit` that the ported codec tests use, on craft-media's
|
|
//! oracle rules (`tests/common`): `CRAFT_MEDIA_FFMPEG`, then `PATH`; a missing tool skips unless
|
|
//! `CRAFT_MEDIA_REQUIRE_ORACLES=1`; fixtures go to `$CRAFT_MEDIA_FIXTURES_DIR` or
|
|
//! `target/fixtures`, never committed.
|
|
|
|
use crate::common;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
/// `let ff = require_ffmpeg!();`: the ffmpeg path, or return from the test after printing
|
|
/// `SKIPPED` (a failure with `CRAFT_MEDIA_REQUIRE_ORACLES=1`).
|
|
macro_rules! require_ffmpeg {
|
|
() => {
|
|
match $crate::testkit::ffmpeg_or_skip(concat!(module_path!(), ":", line!())) {
|
|
Some(p) => p,
|
|
None => return,
|
|
}
|
|
};
|
|
($ret:expr) => {
|
|
match $crate::testkit::ffmpeg_or_skip(concat!(module_path!(), ":", line!())) {
|
|
Some(p) => p,
|
|
None => return $ret,
|
|
}
|
|
};
|
|
}
|
|
|
|
/// ffmpeg, or `None` after printing `SKIPPED` for `context`.
|
|
pub fn ffmpeg_or_skip(context: &str) -> Option<PathBuf> {
|
|
common::tool("ffmpeg").or_else(|| common::skip(&format!("{context}: ffmpeg not found (set CRAFT_MEDIA_FFMPEG or put ffmpeg on PATH)")))
|
|
}
|
|
|
|
/// `<fixtures>/<sub>` (`sub` may hold `/`), created if missing.
|
|
pub fn fixtures_dir(sub: &str) -> PathBuf {
|
|
let mut d = common::fixture_subdir("codecs");
|
|
for part in sub.split('/').filter(|p| !p.is_empty()) {
|
|
d.push(part);
|
|
}
|
|
std::fs::create_dir_all(&d).unwrap_or_else(|e| panic!("create {}: {e}", d.display()));
|
|
d
|
|
}
|
|
|
|
pub mod oracle {
|
|
use std::path::PathBuf;
|
|
|
|
/// A tool by name, silently.
|
|
pub fn find_tool(name: &str) -> Option<PathBuf> {
|
|
crate::common::tool(name)
|
|
}
|
|
}
|
|
|
|
pub mod fixtures {
|
|
use super::*;
|
|
|
|
/// `out` if it already exists (non-empty); otherwise `make(tmp)` on a temporary sibling, renamed
|
|
/// into place. `None` when `make` fails.
|
|
pub fn generate(out: &Path, make: impl FnOnce(&Path) -> bool) -> Option<PathBuf> {
|
|
if std::fs::metadata(out).is_ok_and(|m| m.len() > 0) {
|
|
return Some(out.to_path_buf());
|
|
}
|
|
if let Some(dir) = out.parent() {
|
|
let _ = std::fs::create_dir_all(dir);
|
|
}
|
|
let tmp = common::temp_path(out);
|
|
if !make(&tmp) || !tmp.exists() {
|
|
let _ = std::fs::remove_file(&tmp);
|
|
return None;
|
|
}
|
|
std::fs::rename(&tmp, out).ok()?;
|
|
Some(out.to_path_buf())
|
|
}
|
|
|
|
/// Run a fixture generator; FilmCraft's version also reports it to `cargo xtask fixtures`.
|
|
pub fn generate_and_report<T>(_name: &str, _outputs: &[PathBuf], generate: impl FnOnce() -> Option<T>) -> Option<T> {
|
|
generate()
|
|
}
|
|
}
|