- tooling/ffmpeg/build-ffmpeg.sh builds release/8.0 static+PIC into .cache/ffmpeg: GPL/version3, every free-license external codec lib probed via pkg-config (enabled when present), per-OS hardware acceleration (VideoToolbox/AudioToolbox, VAAPI/VDPAU/libdrm, D3D11VA/DXVA2/MediaFoundation, nvenc when ffnvcodec exists) - tooling/install-deps.sh installs those libraries on Homebrew / MSYS2 UCRT64 / Debian-Ubuntu / Fedora / Arch; nothing in the build sudo's - ffmpeg-next's own build feature is unusable (every crate-version to FFmpeg-release pairing is broken upstream: 9.0.0->FF9 AVCodec fields, 8.1.0->FF8.1 new enum variants, 8.0.0->FF8 FF_PROFILE rename), so ffmpeg-next 9 + FFmpeg 8.x headers via FFMPEG_DIR it is - new links-crate oakffmpeg-link emits the static FFmpeg's transitive link flags from its .pc files (cargo only propagates them from links crates, and rustc prunes the flags unless the rlib is referenced — hence the force_link statics) - docs/build.md updated for the Rust workspace flow
oakcodec Rust crate
Status: implemented. Implements
include/codec/*.hverbatim (src/ffi/); every export has success + failure-path tests (cargo test: unit tests insrc/ffi/*.rs, the contract tests intests/, and real-media tests insrc/realmedia_tests.rs). The FFmpeg engine is fully implemented through the [ffmpeg-next] crate (decode, probe, audio conform, encode); the OIIO engine remains a stub in this build. This crate mirrors thecrates/oaknode/template (same FFI discipline, same testing layers).
Scope
Replaces the C++ oakcodec module (src/codec/src, ~10k lines): CPU
frame buffers (Frame), the frame pool (FrameManager), media
decoders/encoders with their FFmpeg and OIIO implementations, audio
conform and proxy generation managers, export format/codec tables,
encoding parameters, and the background-task submit hook.
Public contract: include/codec/*.h (7 headers: frame.h, decoder.h,
encoder.h, conform.h, proxy.h, task.h, error.h) — frozen, implemented
verbatim by src/ffi.rs. Interim state (pre-M8) is documented in
src/codec/NOTES.md: conform/proxy work is delegated to the global
task submit callback and otherwise reports unavailable, never crashes
and never blocks.
Key architectural decisions (C++ → Rust mapping)
shared_ptr→ refcountedRefBoxhandle. The C++Frame/Decoder/Encoderobjects are heap boxes behind the neutral by-value handle struct{ctx, addref, release, abi_version}(seehandle.rs), exactly as oaknode/oakplugin do. Handles are deliberately duplicated per module: the function pointers always point into the DLL that created the object.- Inheritance → traits. The C++
Decoder/Encoderabstract bases plus their FFmpeg/OIIO subclasses become a Rust trait with two implementors. The probe/dispatch (decide which implementation recognizes a file) stays indecoder.rs.Encoder's per-codecPixelFormat/SampleFormatsupport is a trait query, not a virtual chain. Frameowns its params by value.olive::Framewraps anOakVideoParamshandle (an oakcommon by-value handle, NOT owned by codec) plus aVec<u8>pixel buffer. In Rust the params are held as the oakcommon handle (refcounted throughbridge::common) so the byte-level ABI stays unchanged; the buffer is a plainVec<u8>.- No adapter layer. Codec calls other modules' C ABIs directly
(
bridge/common.rs,bridge/render.rs), keeping the 2026-08 decision recorded in NOTES.md §6. Only genuinely repeated conversions survive as small module-local helpers (e.g.fill_render_params,cancel_atom_is_cancelled). - XML stays on the C++ side.
EncodingParams::load/saveuse oakcommon's C++XmlStreamWriter/Readerclasses (src/common/src/xmlutils.h), exactly as oaknode/oakrender do — the one C++-to-C++ coupling the bridge cannot cover (NOTES.md §7). - Threading.
FrameManagerkeeps its background GC thread behind aMutex; the C++ code's reliance on Qt's event thread is gone. The threading contract is documented per function. - Enum values are the C contract.
ExportFormat::Format,ExportCodec::Codec,Interlacing,VideoScalingMethod,SampleFormat::Formatall stay as the raw int values the C ABI documents (oakengine/encoding.h), soffi.rsmarshals them without translation.
Layout
src/
lib.rs crate doc + module map
error.rs error codes (mirrors include/codec/error.h)
handle.rs refcounted-handle scaffolding (same pattern as node)
frame.rs Frame (CPU pixel buffer + OakVideoParams handle)
framemanager.rs FrameManager (buffer pool + background GC thread)
decoder.rs Decoder trait + CodecStream + RenderMode + probe
ffmpeg.rs FFmpegDecoder / FFmpegEncoder (ffmpeg-next)
oiio.rs OIIODecoder / OIIOEncoder (OpenImageIO)
oiioframebridge.rs oiioutils frame<->buffer conversion
encoder.rs Encoder trait (abstract base)
encodingparams.rs EncodingParams (flattened ABI POD + generate_matrix)
exportcodec.rs ExportCodec enum + codec-name table
exportformat.rs ExportFormat enum + extension/format table
conformmanager.rs ConformManager (stateless, task-callback driven)
proxymanager.rs ProxyManager (stateless, task-callback driven)
task.rs OakCodecTaskKind / OakCodecTaskRequest / submit hook
timecodemetadata.rs TimecodeMetadata (SMPTE/BWF parsers)
footagedescription.rs FootageDescription (codec-internal stream desc)
planarfiledevice.rs PlanarFileDevice (stdio plane-channel I/O)
realmedia_tests.rs real-media tests (demo.mp4, H.264 round-trip)
bridge/ C ABI imports: common.rs, render.rs
ffi.rs include/codec/*.h export layer
tests/ contract + golden tests (see test section below)
Hard rules for the implementer
- Every
extern "C"body goes throughhandle::guard*; no panic crosses FFI. - The handle is the only way out of the crate; the public API never
hands out raw
&Frame/&Decoderreferences. - Behavior parity with C++ is proven by the unchanged C ABI test
suite (
src/codec/tests) plus the contract tests intests/. - Where C++ behavior is genuinely load-bearing but ugly, port the
behavior, not the aesthetics; leave a
// CPP-PARITY:comment with the C++ file:line.
Dependency policy
Prefer mature third-party crates (MIT/Apache-2.0/BSD, GPL-compatible) over hand-rolling; register each addition (name + reason) here. Large existing C++ libraries (OTIO, OCIO, OIIO, FFmpeg) are NEVER rewritten — they are consumed through their C ABI / bridge layers.
Dependencies
oakcore-rs(path) — oakcore value types (Rational, TimeRange, PixelFormat/SampleFormat) mirrored as Rust enums.ffmpeg-next9 — the FFmpeg decode/encode engine. The C++ffmpeg_bridgelibrary (liboakffmpeg) existed only to absorb FFmpeg API churn; the Rust crate callsffmpeg-nextdirectly (per the 2026-08 decision that dropped the binding-library plan).ffmpeg-nextbuilds against the system FFmpeg viaffmpeg-sys-next(bindgen); the implementation dips intoffmpeg-sys-next(ffmpeg::ffi) only for swscale/swresample details and channel-layout construction that the safe wrapper does not expose.