examples/screenshot.rs: migrate to language_code()/set_language_code
(the Language enum is gone); only CI's example build caught it — local
--lib runs never compile examples.
oakcodec tests: build path expectations with Path::join instead of
'/'-joined literals — production uses platform-native separators, so
the derivation assertions failed on Windows ("dir\img007.jpg" vs
"dir/img007.jpg"). Behaviour unchanged; the tests were never reached
on Windows before (earlier failures aborted the run first).
oakui::ofx interact test: poll up to 5s for the destroy marker record
before asserting. The active-interact slot is process-global; a
concurrent viewer frame sync from another test's real engine can take
the interact out of the slot and be preempted between take and
destroy, so the plugin's destroy record occasionally lands a few
milliseconds after this test removed the marker env var (Linux CI:
"lifecycle actions missing: [...]" with everything but destroy
present).
oakcodec Rust crate
Status: implemented. Implements the
include/codec/*.hcontract; every function has success + failure-path tests (cargo test: unit tests insrc/, 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.Single-lib unification (M14,
docs/zh/plans/riir/single-lib.md): the C-ABI export layer (src/ffi.rs) and the module-crossing bridge (src/bridge/) are gone. Other module crates and the oakengine facade call this crate's modules directly; the facade (crates/oakengine) serves the frozeninclude/codec/*.hfunctions.
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, served by the
oakengine facade. 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→Arc. The C++Frame/Decoder/Encoderobjects are handed around as plain Rust values (Frame) orArc<dyn Decoder>/Arc<dyn Encoder>. No refcounted C-handle scaffolding remains (the formerhandle.rswas deleted in M14 R5).- 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 plus aVec<u8>pixel buffer. In Rust the params are held as anoakcommon::videoparams::VideoParamsvalue (single-lib unification dropped the refcounted oakcommon handle); the buffer is a plainVec<u8>.- No adapter layer. Codec calls the other module crates directly
(
oakcommon,oakcore-rs,oakffmpeg-link), keeping the 2026-08 decision recorded in NOTES.md §6. Only genuinely repeated conversions survive as small module-local helpers. - 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), so the facade marshals them without translation.
Layout
src/
lib.rs crate doc + module map
error.rs error codes (mirrors include/codec/error.h)
frame.rs Frame (CPU pixel buffer + VideoParams value)
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)
tests/ contract + golden tests (see test section below)
Hard rules for the implementer
- No panics cross a module boundary: the facade wraps every call in
its panic-catching shims, and callback types stay
unsafe extern "C"with panic-free bodies. - Objects leave the crate only as Rust types (
Arc, values,&refs); raw handles exist solely inside the facade. - 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.