refactor: drop internal bridge/ffi layers; exporter family lands

Single-lib cleanup: the per-crate src/bridge/ and src/ffi.rs layers are
gone (oakundo/oakcommon/oaknode/oaktimeline/oakcodec/oakaudio/
oakrender/oaktask/oakplugin/oakstorage); cross-crate calls are plain
Rust, CHandle marshalling shrinks to the oakengine boundary, and tests
call the Rust APIs directly (pure C-ABI wrapper tests removed where
the domain layer already covers the behavior).

exporter.h family implemented: oakengine_export_render (CLI contract),
oakengine_export_render_with_params (was a stub), last_error and
progress callback; synchronous path reuses task_create_export +
start_sync. Fixes on the way: oaktask video ticket self-deadlock,
audio params dropped on the export path, codec encoder AAC slicing and
H.264 time base. Real-mp4 tests cover both entry points, progress and
the illegal-argument matrix.

Also: oakstorage session maps null project handles to None (version-
info path), configstore test double literal 3.14 -> 3.15 (clippy PI
lint), oakaudio output callback scratch buffer + env-aware P1 test,
cli media round-trip test uses a generated 16-frame clip (no more
minute-long debug runs).
This commit is contained in:
2026-08-16 00:33:45 +08:00
parent 2248be8567
commit ab1a2e9c7b
293 changed files with 27826 additions and 90128 deletions
+26 -42
View File
@@ -22,7 +22,7 @@
//! `Unavailable`. Deterministic per-channel filenames derive from the
//! source + target audio params.
use std::ffi::CString;
use oakcommon::filefunctions::FileFunctions;
use std::path::Path;
/// Conform state of one audio stream.
@@ -184,18 +184,11 @@ fn conform_filenames(
out
}
/// `oakcommon_filefunctions_get_unique_file_identifier` wrapper (the bridge
/// returns a 64-bit id directly).
/// `oakcommon_filefunctions_get_unique_file_identifier` wrapper.
fn unique_file_identifier(filename: &str) -> String {
let c = match CString::new(filename) {
Ok(c) => c,
Err(_) => return String::new(),
};
// # Safety: `c` is a valid NUL-terminated C string alive for the call.
let id = unsafe {
crate::bridge::common::oakcommon_filefunctions_get_unique_file_identifier(c.as_ptr())
};
format!("{}", id)
FileFunctions::new()
.get_unique_file_identifier(filename)
.unwrap_or_default()
}
/// True when every conform filename already exists on disk.
@@ -234,34 +227,23 @@ mod tests {
dir.to_string_lossy().into_owned()
}
fn fnv1a64(bytes: &[u8]) -> u64 {
let mut h: u64 = 14695981039346656037;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(1099511628211);
}
h
}
#[test]
fn unique_identifier_matches_bridge_hash() {
// The frozen ABI returns the identifier as a string; the
// value-style adapter folds it into an FNV-1a-64. The test stub
// produces the decimal FNV of the path bytes, so the expected
// value is FNV of that decimal string.
let stub_id = fnv1a64(b"media.mp4") as i64;
let expected = format!("{}", fnv1a64(stub_id.to_string().as_bytes()) as i64);
assert_eq!(unique_file_identifier("media.mp4"), expected);
fn unique_identifier_uses_file_metadata() {
// The identifier is the 16-digit FNV-1a hex of (absolute path +
// mtime); a missing file has no identifier.
assert_eq!(unique_file_identifier("no-such-file.mp4"), "");
let dir = temp_subdir("id");
let file = std::path::Path::new(&dir).join("media.mp4");
std::fs::write(&file, b"x").unwrap();
let id = unique_file_identifier(&file.to_string_lossy());
assert_eq!(id.len(), 16);
assert!(id.chars().all(|c| c.is_ascii_hexdigit()));
// Deterministic: same input, same id.
assert_eq!(
unique_file_identifier("media.mp4"),
unique_file_identifier("media.mp4")
);
assert_eq!(id, unique_file_identifier(&file.to_string_lossy()));
let other = std::path::Path::new(&dir).join("other.mp4");
std::fs::write(&other, b"y").unwrap();
// Different input, different id.
assert_ne!(
unique_file_identifier("media.mp4"),
unique_file_identifier("other.mp4")
);
assert_ne!(id, unique_file_identifier(&other.to_string_lossy()));
}
#[test]
@@ -279,21 +261,23 @@ mod tests {
#[test]
fn conform_filename_derivation_and_range() {
let m = ConformManager::instance();
let src_dir = temp_subdir("names");
let src = std::path::Path::new(&src_dir).join("media.mp4");
std::fs::write(&src, b"x").unwrap();
let cache = temp_subdir("names");
let stub_id = fnv1a64(b"media.mp4") as i64;
let id = fnv1a64(stub_id.to_string().as_bytes()) as i64;
let id = unique_file_identifier(&src.to_string_lossy());
let base = format!("{}-0.48000.0.3", id);
let f0 = m
.get_conform_filename(&cache, "media.mp4", 0, 48000, 0x3, 0, 0)
.get_conform_filename(&cache, &src.to_string_lossy(), 0, 48000, 0x3, 0, 0)
.unwrap();
let f1 = m
.get_conform_filename(&cache, "media.mp4", 0, 48000, 0x3, 0, 1)
.get_conform_filename(&cache, &src.to_string_lossy(), 0, 48000, 0x3, 0, 1)
.unwrap();
assert_eq!(f0, format!("{}/{}.0.pcm", cache, base));
assert_eq!(f1, format!("{}/{}.1.pcm", cache, base));
// Out of range.
assert!(matches!(
m.get_conform_filename(&cache, "media.mp4", 0, 48000, 0x3, 0, 5),
m.get_conform_filename(&cache, &src.to_string_lossy(), 0, 48000, 0x3, 0, 5),
Err(crate::error::Error::NotFound)
));
}