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
+24 -14
View File
@@ -26,12 +26,22 @@
use std::path::Path;
use std::sync::{Arc, Mutex, OnceLock};
use oakcommon::cancelatom::CancelAtom;
use oakcore_rs::{Rational, TimeRange};
use crate::bridge::render::{OakCancelAtom, OakRenderTexture};
use crate::footagedescription::FootageDescription;
use crate::frame::Frame;
/// `OakRenderTexture` — refcounted GPU texture handle (an oakrender type,
/// opaque to oakcodec). The codec crate cannot depend on oakrender (the
/// dependency cycle), so it only ever produces an empty handle — the
/// shared [`crate::handle::CHandle`] carries that value unchanged.
pub type OakRenderTexture = crate::handle::CHandle;
/// `OakNodeBlock` — opaque node-block handle owned elsewhere; the codec
/// only stores and forwards it (borrowed, never dereferenced).
pub type OakNodeBlock = crate::handle::CHandle;
/// `oakcodec_video_stream_info` — POD probe output describing one video
/// stream; see `include/codec/decoder.h`.
#[repr(C)]
@@ -158,7 +168,7 @@ pub enum RetrieveState {
pub struct CodecStream {
filename: String,
stream: i32,
block: Option<crate::bridge::common::OakNodeBlock>,
block: Option<OakNodeBlock>,
}
impl CodecStream {
@@ -175,7 +185,7 @@ impl CodecStream {
pub fn with_block(
filename: String,
stream: i32,
block: Option<crate::bridge::common::OakNodeBlock>,
block: Option<OakNodeBlock>,
) -> Self {
CodecStream {
filename,
@@ -212,7 +222,7 @@ impl CodecStream {
}
/// Associated timeline block (borrowed; only compared, never used).
pub fn block(&self) -> Option<crate::bridge::common::OakNodeBlock> {
pub fn block(&self) -> Option<OakNodeBlock> {
self.block.clone()
}
}
@@ -241,7 +251,7 @@ pub trait Decoder: Send + Sync {
fn probe(
&self,
filename: &str,
cancelled: Option<&OakCancelAtom>,
cancelled: Option<&CancelAtom>,
) -> Option<FootageDescription>;
/// Open `stream` for decoding. Thread-safe.
@@ -281,7 +291,7 @@ pub trait Decoder: Send + Sync {
sample_rate: i32,
channel_layout: u64,
sample_format: i32,
cancelled: Option<&OakCancelAtom>,
cancelled: Option<&CancelAtom>,
) -> crate::error::Result<()>;
/// Offset of the audio start relative to the video (rational seconds).
@@ -326,7 +336,7 @@ impl Decoder for UnimplementedDecoder {
fn probe(
&self,
_filename: &str,
_cancelled: Option<&OakCancelAtom>,
_cancelled: Option<&CancelAtom>,
) -> Option<FootageDescription> {
None
}
@@ -377,7 +387,7 @@ impl Decoder for UnimplementedDecoder {
_sample_rate: i32,
_channel_layout: u64,
_sample_format: i32,
_cancelled: Option<&OakCancelAtom>,
_cancelled: Option<&CancelAtom>,
) -> crate::error::Result<()> {
Err(crate::error::Error::Failed(
"decoder not yet implemented".to_string(),
@@ -400,13 +410,13 @@ pub fn create_from_id(id: &str) -> Option<Arc<dyn Decoder>> {
/// not injected, in which case the built-in list below is used.
static TEST_DECODERS: OnceLock<Mutex<Vec<Arc<dyn Decoder>>>> = OnceLock::new();
/// Serializes every test that reads the built-in decoder registry. The ffi
/// decoder tests inject through `crate::ffi::lock_tests()` (the shared
/// `TEST_LOCK`), so the registry assertions below take that same lock to
/// never race with an injected list.
/// Serializes every test that reads the built-in decoder registry. Tests
/// inject through [`set_test_decoders`] under [`crate::lock_tests`] (the
/// shared test lock), so the registry assertions below take that same lock
/// to never race with an injected list.
#[cfg(test)]
fn registry_guard() -> std::sync::MutexGuard<'static, ()> {
crate::ffi::lock_tests()
fn registry_guard() -> crate::TestLock {
crate::lock_tests()
}
/// Replace the decoder registry with `list`; pass an empty list to restore