The pure-Rust FFmpeg + OpenCV replacement just decoded its first bit-exact AV1 frame — and stopped pretending on the ones it can’t.
Today we released OxiMedia 0.2.0 — a release built on three things: a real frame-level transcode engine that actually decodes, filters, and re-encodes instead of just copying streams; AV1, VP9, and VP8 key-frame/intra-frame video decoders verified bit-exact against their reference C decoders; and a broad “real or honest error” sweep that replaces silent placeholder and fabricated-success behavior with genuine implementations or explicit, testable errors across the packager, network, workflow, Python bindings, and CLI layers.
No C. No C++. No Fortran. OxiMedia’s default build has been 100% Pure Rust since 0.1.9 — zero aws-lc-sys, libsqlite3-sys, mlua-sys, shaderc-sys, zstd-sys, openssl-sys, rustfft, or realfft anywhere in the default dependency graph — and 0.2.0 adds three C reference decoders’ worth of decode logic without linking a single line of any of them. It compiles to one static binary (or to wasm32-unknown-unknown for the browser) and runs everywhere with one cargo add.
Why OxiMedia 0.2.0 is a game changer
FFmpeg and OpenCV get you most of the way there — until you hit the parts that were never really finished:
- FFmpeg is written in C, ships patent-encumbered codecs (H.264, H.265, AAC), and its trusted
-c copyfast path quietly does less than you’d expect the moment a filter or codec change actually requires re-encoding - OpenCV depends on a C++/CMake build plus a maze of optional proprietary modules just to get consistent behavior across platforms
- The reference decoders for royalty-free codecs —
dav1d,libaom,libvpx— are themselves large C codebases; reimplementing them safely, let alone verifying bit-exactness, is not a weekend project - And in any large media codebase — OxiMedia’s own earlier releases included — the least-exercised code path is exactly the one most likely to return a plausible-looking
Okfor work it never actually did
OxiMedia 0.2.0 ends all of that.
- AV1 key-frame/intra decode is real and bit-exact. A ground-up port of the AV1 intra decode path — symbol/range decoder, header parsing, transform-coefficient decode, intra prediction (including CFL), inverse transforms, deblocking, CDEF, and both Wiener and self-guided loop restoration — verified at 0 differing Y/U/V pixels against both
dav1d1.5.1 andaomdec/libaom v3.12.1 across 13 keyframe test vectors, covering lossless coding, 128×128 superblocks, and multi-tile cases. Inter-frame decode remains open for 0.2.x. - VP9 and VP8 key-frame decode join it, both bit-exact. VP9’s intra path (boolean decoder, inverse DCT/ADST/Walsh-Hadamard transforms, loop filter) matches
ffmpeg/libvpx reference decodes of real encoder output; VP8’s full RFC 6386 §11–§15 intra pipeline matches libwebp reference output — and was cross-checked against OxiMedia’s own production-verified WebP/VP8 still-image decoder, since a lossy WebP image is just a single VP8 key frame. - Transcode jobs that need re-encoding now actually re-encode. The new
oximedia-transcodeframe-level engine — a genuine decode → filter → encode pipeline — replaces the previous stream-copy-only path behindTranscodePipeline::requires_frame_level().-rframe-rate conversion is now a real drop/duplicate resampler, and new muxers (RawEsFileMuxer,FlacFileMuxer,CafAlacFileMuxer,Y4mFileMuxer) back genuine outputs. - CENC/
cbcspackager encryption now produces output real clients can decrypt.encrypt_cenc/decrypt_cencperform genuine full-sample AES-128-CTR, and SAMPLE-AES implements the actual ISO/IEC 23001-7 §9.6cbcspattern — the format FairPlay, Shaka, hls.js, and dash.js actually expect, replacing a mislabeled full-buffer CBC path no real client could ever decrypt. - A dozen-plus fabricated-success paths fail honestly instead. Python bindings, the RTMP relay,
oximedia-effectsshelf EQ, and code across the renderfarm, stabilize, VFX, accessibility, captions, automation, and conform crates that used to return a plausibleOkfor work it didn’t do now return a real result or an explicitErr— each fix pinned by its own regression test.
Technical Deep Dive: how the decode, transcode, and honesty work fit together
- Codec layer —
oximedia-codec. The newav1/kf/(bits,msac,hdr,cdfs,coef,pred,itx,recon,lf,cdef,lr) andvp9/kf/(booldec,hdr,itx,lf,pred,recon,scan,tables) modules, plusoximedia-image’swebp/vp8/keyframe/path, each port their codec’s intra decode from spec (AV1) or from libvpx/RFC 6386 (VP9/VP8), driven by a tile/partition/block decode driver with the full post-filter chain applied. Scope is explicit: 8-bit 4:2:0 profile 0, keyframe/intra only — inter-frame, 10/12-bit, film grain, and palette mode all returnCodecError::UnsupportedFeaturerather than a wrong frame. - Frame-level transcode engine —
oximedia-transcode.frame_level.rs,frame_adapters.rs,audio_adapters.rs,raw_sinks.rs,flac_bitstream.rs/flac_decode.rs, andalac_bitstream.rsimplement the actual decode→filter→encode path, gated behindrequires_frame_level()inpipeline.rsso a simple remux job still takes the fast stream-copy route. - Packager and network security layer.
oximedia-packager::encryption(real CENC/cbcsAES),oximedia-net::srt::crypto(a rewritten, real six-round RFC 3394 AES key wrap verified against the RFC’s own §4.1 test vector), and new bounds/allocation-cap hardening across MP4 box nesting, DVB subtitle regions, RTSP bodies, RTMP chunks, WebRTC SCTP reassembly, and AAF essence ranges. - CLI, Python bindings, and the honesty sweep.
oximedia-cli(~40 flags verified real, from--mapand-ss/-tto--crfand--quiet),oximedia-py’s PyO3 bindings (proxy_py.rs,cloud_py.rs,video.rs,workflow_py.rs), and crates spanning production, VFX, and accessibility all had fabricated-success paths replaced with real behavior or an honestErr, each backed by a new regression test proving the old behavior is gone.
Getting Started
cargo add oximedia
or pin the version and pick features in Cargo.toml:
[dependencies]
oximedia = { version = "0.2.0", features = ["full"] }
Probe a file and transcode it with quality control — straight from the crate’s own docs:
use oximedia::prelude::*;
// Probe a media file
let data = std::fs::read("video.webm")?;
let result = probe_format(&data)?;
println!("Format: {:?}, Confidence: {:.1}%",
result.format, result.confidence * 100.0);
// Transcode with quality control
let pipeline = TranscodePipeline::builder()
.input("input.mkv")
.video_codec(VideoCodec::Av1)
.audio_codec(AudioCodec::Opus)
.output("output.webm")
.build()?;
pipeline.run().await?;
Or reach for the CLI directly:
cargo install oximedia-cli
oximedia probe -i video.webm
oximedia transcode -i input.mkv -o output.webm --codec av1
What’s New in 0.2.0
- Added: a real frame-level transcode engine (
oximedia-transcode); AV1 key-frame/intra decode (bit-exact vsdav1d/aomdec); VP9 key-frame/intra decode (bit-exact vs libvpx); VP8 key-frame decode (bit-exact vs libwebp/RFC 6386); real CENC/cbcspackager encryption; ~40oximedia-cliflags verified real; a real RTMP ingest accept loop inoximedia-server; a real downstream-keyer auto-transition ramp inoximedia-switcher; a real 4-point DLT homography solve foroximedia-vfxplanar tracking; MatroskaSeekHeadmuxing and matching seek support. - Changed: a dozen-plus “fabricated success” code paths across
oximedia-py,oximedia-net’s RTMP relay,oximedia-effects,oximedia-renderfarm,oximedia-stabilize,oximedia-vfx,oximedia-access,oximedia-captions,oximedia-automation,oximedia-conform, andoximedia-accelnow return honest errors instead of fake success, each pinned by a regression test; Opus hybrid-mode encode and the JPEG XS decoder likewise stopped fabricating output. - Removed:
oximedia-cli transcode --resume— it was already dead code, never wired to any resume capability; clap now rejects it outright instead of silently no-opping. - Fixed: RTMP client handshake ordering (C2 now sent before S2 is validated, per spec); a workflow executor bug that silently dropped every non-root task in a dependency chain while still reporting
Completed; a hardcoded FLAC encoder frame-header CRC-8; a JPEG-LS RUN-mode out-of-bounds write; a DNG writer IFD offset bug; a wasm32 32-bit overflow in three allocation-limit constants; and a fake RFC 3394 AES key wrap in SRT key exchange, now the real six-round algorithm. - Security: parser bounds/allocation-cap hardening against malicious input across MP4 box nesting, DVB subtitle regions, RTSP bodies, RTMP chunks, WebRTC SCTP reassembly, and AAF essence ranges.
Tips
- Recheck error handling anywhere you call the newly-honest paths. If your code used
oximedia-py’sproxy_py/cloud_py/workflow_py, the RTMP relay, or the renderfarm/stabilize/vfx/access/captions/automation/conform/accel crates and assumed success, 0.2.0 may now return a realErrwhere it used to fabricate anOk— that’s the fix working as intended, not a regression. - Drop
--resumefrom any transcode scripts. It was already a silent no-op;oximedia-clinow rejects it as an unknown argument rather than pretending to honor it. - Don’t reach for AV1/VP9/VP8 decode on inter-frame content yet. All three decoders are keyframe/intra only in 0.2.0 — inter-frame call sites get an honest
CodecError::UnsupportedFeature, not a wrong picture. Track 0.2.x for inter-frame decode. - Repackage anything you encrypted with CENC/
cbcsbefore 0.2.0. The old packager output was mislabeled full-buffer AES-CBC, not real CENC/SAMPLE-AES — no compliant client could ever decrypt it. Re-run it through 0.2.0’sencrypt_cenc/encrypt_sample_aesto get output FairPlay/Shaka/hls.js/dash.js can actually play. - Use
requires_frame_level()as your mental model for job cost. Simple remuxes still take the cheap stream-copy path; anything that needs a real filter or codec change —-vf,-af,-r, or a codec swap — now routes through the new decode→filter→encode engine, so budget CPU time accordingly. - Validate your own AV1 keyframes the same way this release did. The bit-exactness harness in
av1/kf/mod.rschecks OxiMedia’s decode against realaomenc/SVT-AV1-encoded fixtures — a solid pattern to copy if you want to verify your own encoder’s keyframes against OxiMedia’s decoder.
This is the foundation
OxiMedia is the pure-Rust media and computer-vision layer of the COOLJAPAN ecosystem, leaning on its siblings directly:
- OxiONNX — the Pure-Rust ONNX runtime behind every opt-in
oximedia-mlpipeline (scene classification, shot-boundary detection, aesthetic scoring, object detection, face embedding), still zero symbols linked by default. - OxiFFT — the spectral engine behind phase correlation, log-mel spectrograms, and audio analysis.
- SciRS2 — scientific-computing primitives (linalg, SIMD) for the CV and signal paths.
- OxiArc — Pure-Rust compression (
oxiarc-archive,oxiarc-lz4,oxiarc-zstd,oxiarc-deflate) for containers and archival. - OxiSQL —
oxisql-sqlite-compatbacks every SQLite-persisted path inoximedia-server,oximedia-rights,oximedia-archive, andoximedia-dedup. - OxiCode — the COOLJAPAN serialization layer used workspace-wide.
- OxiScope and OxiLink — production consumers of the
oximedia-webWASM modules (scopes, color, scale, quality); untouched by this release’s native-side decode work but built on the same Pure-Rust discipline.
Repository: https://github.com/cool-japan/oximedia
Star the repo if you think a media framework’s decoder should either produce a real frame or say so honestly — never both claim success and hand you the wrong picture.
The era of choosing between FFmpeg’s patent-encumbered C core and a media stack that quietly fakes the hard parts is over. Pure Rust media is here — bit-exact where it counts, and honest everywhere else.
— KitaSan at COOLJAPAN OÜ July 15, 2026