Files
oak-editor/crates/oakcodec/README.md
T
Mike-Solar b36cbd6b6f refactor: purge CHandle from module internals (M14 R5)
Module-internal object references are Rust types now (values, Arc,
Mutex); CHandle remains only at the oakengine C-ABI boundary:

- oakundo: the global stack holds UndoStack/UndoCommand values
  directly (stack token is the static's address)
- oaktimeline: marker/workarea boxes carry Arc<Mutex<T>>; commands
  share the same allocation through Arc clones (readers in oakengine
  stubs and the app's graphops updated to lock)
- oaktask/oakstorage: sessions, write-through bindings and the
  database backend pass ProjectArc; the Session drops its manual
  release bookkeeping; nodeutil keeps the CHandle<->Arc boundary
  conversion (release_project restored for the app)
- oakcodec: handle.rs deleted outright (no facade entry needed it);
  texture/block placeholders are unit structs
- oakrender: copier's project handle is an identity u64; alive-count
  machinery removed; handle.rs is make_owned/get/get_mut only
- oakplugin: the instance registry is gone (its unregister key never
  matched, leaking weak entries); handle.rs is the RefBox boundary type
- oaknode/oakcommon: only dead guard/borrow helpers removed; external
  payload handles (texture/processor) documented as the boundary

Flake hunts landed along the way: the audio recording test serializes
on the shared manager lock with a normalized state; the autocacher
cancel test uses a slow producer so cancellation is deterministic.
2026-08-17 16:40:15 +08:00

6.1 KiB

oakcodec Rust crate

Status: implemented. Implements the include/codec/*.h contract; every function has success + failure-path tests (cargo test: unit tests in src/, the contract tests in tests/, and real-media tests in src/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 frozen include/codec/*.h functions.

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)

  1. shared_ptrArc. The C++ Frame/Decoder/Encoder objects are handed around as plain Rust values (Frame) or Arc<dyn Decoder> / Arc<dyn Encoder>. No refcounted C-handle scaffolding remains (the former handle.rs was deleted in M14 R5).
  2. Inheritance → traits. The C++ Decoder/Encoder abstract bases plus their FFmpeg/OIIO subclasses become a Rust trait with two implementors. The probe/dispatch (decide which implementation recognizes a file) stays in decoder.rs. Encoder's per-codec PixelFormat/SampleFormat support is a trait query, not a virtual chain.
  3. Frame owns its params by value. olive::Frame wraps an OakVideoParams handle plus a Vec<u8> pixel buffer. In Rust the params are held as an oakcommon::videoparams::VideoParams value (single-lib unification dropped the refcounted oakcommon handle); the buffer is a plain Vec<u8>.
  4. 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.
  5. XML stays on the C++ side. EncodingParams::load/save use oakcommon's C++ XmlStreamWriter/Reader classes (src/common/src/xmlutils.h), exactly as oaknode/oakrender do — the one C++-to-C++ coupling the bridge cannot cover (NOTES.md §7).
  6. Threading. FrameManager keeps its background GC thread behind a Mutex; the C++ code's reliance on Qt's event thread is gone. The threading contract is documented per function.
  7. Enum values are the C contract. ExportFormat::Format, ExportCodec::Codec, Interlacing, VideoScalingMethod, SampleFormat::Format all 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

  1. 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.
  2. Objects leave the crate only as Rust types (Arc, values, & refs); raw handles exist solely inside the facade.
  3. Behavior parity with C++ is proven by the unchanged C ABI test suite (src/codec/tests) plus the contract tests in tests/.
  4. 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-next 9 — the FFmpeg decode/encode engine. The C++ ffmpeg_bridge library (liboakffmpeg) existed only to absorb FFmpeg API churn; the Rust crate calls ffmpeg-next directly (per the 2026-08 decision that dropped the binding-library plan). ffmpeg-next builds against the system FFmpeg via ffmpeg-sys-next (bindgen); the implementation dips into ffmpeg-sys-next (ffmpeg::ffi) only for swscale/swresample details and channel-layout construction that the safe wrapper does not expose.