Pure-Rust media transcoding and streaming. No C libraries, no FFI
wrappers, no *-sys crates β every codec, container, and filter is
implemented from the spec in safe Rust.
The two user-facing binaries:
Probe, remux, transcode, run JSON transcode graphs. Drop-in on servers and pipelines; one static binary, no system deps.
cargo install oxideav-cliβ¦or grab a pre-built binary from the latest oxideav-workspace release.
oxideav list # registered codecs + containers
oxideav probe video.mp4 # demux probe + metadata
oxideav transcode song.flac song.wav # remux or transcode
oxideav run job.json # run a JSON transcode graphSDL2 + TUI frontend. SDL2 is loaded at runtime via libloading, so the binary builds + ships without an SDL2 build-time dep (install SDL2 on the target machine; oxideplay falls back gracefully if it's missing).
cargo install oxideplayβ¦or grab it pre-built from the same oxideav-workspace release archive.
oxideplay path/to/file.mkv
oxideplay https://example.com/video.mp4
oxideplay --job job.json # render a transcode graph livePre-built releases bundle both binaries (oxideav + oxideplay)
in one per-platform archive β Linux x86_64, macOS universal (Intel +
Apple Silicon), Windows x86_64 β published as a single GitHub Release
per workspace tag.
graph LR
CLI[oxideav<br/>CLI]
PLAYER[oxideplay<br/>Player]
PIPE[oxideav-pipeline<br/>source β transforms β sink]
SRC[oxideav-source<br/>URI / file / probe]
HTTP[oxideav-http]
GEN[oxideav-generator<br/>synthetic input]
CORE[oxideav-core<br/>Decoder/Encoder traits<br/>Demuxer/Muxer traits<br/>CodecRegistry]
AF[oxideav-audio-filter]
IF[oxideav-image-filter]
PIX[oxideav-pixfmt]
SUB[oxideav-subtitle]
SCENE[oxideav-scene]
SCRIBE[oxideav-scribe<br/>shaper + rasterizer]
TTF[oxideav-ttf]
OTF[oxideav-otf]
AUDIO[Audio codecs<br/>aac Β· ac3 Β· eac3 Β· opus<br/>flac Β· mp3 Β· vorbis<br/>g.711/722/729 Β· gsm<br/>shorten Β· tta Β· wavpack<br/>cook Β· dts Β· wma Β· β¦]
VIDEO[Video codecs<br/>h.264 Β· h.265 Β· h.266<br/>av1 Β· vp8 Β· vp9 Β· vp6<br/>mpeg-1/2/4 Β· prores<br/>ffv1 Β· huffyuv Β· utvideo<br/>cinepak Β· indeo Β· svq Β· β¦]
IMAGE[Image codecs<br/>png Β· jpeg Β· webp Β· gif<br/>jpegxl Β· jpeg2000 Β· jpegxs<br/>avif Β· heif Β· tiff Β· bmp Β· ico]
CONT[Containers<br/>mp4 Β· mkv/webm Β· ogg<br/>avi Β· flv Β· iff Β· mod/s3m]
LIVE[Live transport<br/>rtmp]
CLI --> PIPE
PLAYER --> PIPE
PIPE --> SRC
PIPE --> CORE
PIPE --> AF
PIPE --> IF
SRC --> HTTP
SRC --> GEN
CORE --> AUDIO
CORE --> VIDEO
CORE --> IMAGE
CORE --> CONT
CORE --> LIVE
AF --> CORE
IF --> PIX
SUB --> SCRIBE
SCENE --> SCRIBE
SCRIBE --> TTF
SCRIBE --> OTF
oxideav-core carries the trait surface (Decoder, Encoder,
Demuxer, Muxer) plus the registry. Each codec/container crate
exposes pub fn register(&mut RuntimeContext) and the
oxideav-meta aggregator
calls every enabled sibling's register from a build-script-generated
register_all(ctx). The pipeline executor only sees the registry,
never any concrete crate.
OxideAV is a modular framework shipped as ~95 small crates. Each codec is its own repository and its own crates.io release, so you pick only what you need and their cadence stays independent of the framework.
Single-crate standalone use is the expected case:
[dependencies]
oxideav-core = "0.1" # types + Decoder/Encoder traits + registry
oxideav-g711 = "0.0" # or any other codecuse oxideav_core::{
CodecId, CodecParameters, CodecRegistry, Frame, Packet, TimeBase,
};
let mut reg = CodecRegistry::new();
oxideav_g711::register(&mut reg);
let mut params = CodecParameters::audio(CodecId::new("pcm_mulaw"));
params.sample_rate = Some(8_000);
params.channels = Some(1);
let mut dec = reg.make_decoder(¶ms)?;
dec.send_packet(&Packet::new(0, TimeBase::new(1, 8_000), ulaw_bytes))?;
let Frame::Audio(a) = dec.receive_frame()? else { unreachable!() };Or pull every codec/container/filter/source via the
oxideav-meta aggregator:
[dependencies]
oxideav-core = "0.1"
oxideav-meta = "0.0" # default-features = ["all"] pulls everythinglet mut ctx = oxideav_core::RuntimeContext::new();
oxideav_meta::register_all(&mut ctx);Slimmer subsets via Cargo features. Preset bundles (audio, video,
image, subtitles, hwaccel, source-drivers) plus per-crate flags:
# Just image codecs.
oxideav-meta = { version = "0.0", default-features = false, features = ["image"] }
# MP4 + H.264 + AAC only.
oxideav-meta = { version = "0.0", default-features = false, features = ["mp4", "h264", "aac"] }register_all is auto-generated by oxideav-meta's build.rs from
its Cargo.toml + active feature flags β only the siblings you opted
into get linked.
| Crate | Role |
|---|---|
oxideav-core |
Packet / Frame / TimeBase / PixelFormat / Error + Decoder/Encoder/Demuxer/Muxer traits + CodecRegistry |
oxideav-pipeline |
Source β transforms β sink composition + JSON transcode graph + multithreaded executor |
oxideav-source |
URI resolution, file reader, BufferedSource prefetch ring |
oxideav-http |
HTTP(S) source driver (ureq + rustls) |
oxideav-generator |
Synthetic media source (generate://, xc:, pattern:, gradient:, testsrc:, β¦) |
oxideav-pixfmt |
Pixel-format conversion, palette quantisation, dither, alpha blending |
oxideav-audio-filter |
Volume / NoiseGate / Echo / Resample / Spectrogram |
oxideav-image-filter |
Resize / crop / rotate / colour / overlay |
oxideav-id3 |
ID3v1 / v2.2 / v2.3 / v2.4 tag parser |
oxideav-rtmp |
RTMP live-streaming source |
oxideav-ttf |
TrueType parser (cmap, GSUB, GPOS, kerning, ligatures) |
oxideav-otf |
OpenType-CFF parser (Type 2 charstrings) |
oxideav-scribe |
Font rasterizer + shaper + line layout (Latin / CJK + GSUB / GPOS) |
oxideav-scene |
Time-based scene / composition model (PDF pages, NLE timelines) |
oxideav-meta |
Aggregator: register_all(ctx) over every enabled codec / container / filter / source. Cargo presets (audio / video / image / subtitles / hwaccel / all) + per-crate flags. |
oxideav-videotoolbox |
macOS hardware video codec bridge (VideoToolbox; runtime-loaded via libloading, priority 0 β preferred over pure-Rust on Mac). |
oxideav-audiotoolbox |
macOS hardware audio codec bridge (AudioToolbox; same shape as the video bridge). |
Containers β MP4 Β· Matroska / WebM Β· Ogg Β· AVI Β· FLV Β· IFF / 8SVX Β· MOD Β· S3M Β· RTMP (live source)
Audio codecs β PCM Β· ADPCM Β· AAC-LC (with ADTS + LATM/LOAS) Β· MP1 Β· MP2 Β· MP3 Β· FLAC Β· Vorbis Β· Opus Β· CELT Β· Speex Β· GSM Β· G.711 Β· G.722 Β· G.723.1 Β· G.728 Β· G.729 Β· iLBC Β· AC-3 Β· E-AC-3 Β· AC-4 Β· DTS (Core / EXSS / XCH / XXCH / X96 / XLL) Β· WMA (v1 / v2 / Pro / Lossless) Β· Cook Β· Musepack Β· WavPack Β· Shorten Β· TTA Β· aptX Β· MIDI (SMF)
Video codecs β H.261 Β· H.263 Β· H.264 (decode + CABAC encode, High10) Β· H.265 (decode + 4:4:4 12-bit IDR encode) Β· H.266 Β· MPEG-1 Β· MPEG-2 Β· MPEG-4 P2 Β· MS-MPEG-4 Β· MJPEG (with progressive + arithmetic SOF9) Β· Theora Β· Dirac (with OBMC encode) Β· ProRes (RDD 36 β all 6 profiles) Β· FFV1 Β· AV1 (decode) Β· VP6 Β· VP8 Β· VP9 Β· AMV Β· HuffYUV Β· Lagarith Β· Ut Video Β· MagicYUV Β· Cinepak Β· Sorenson SVQ1 Β· Indeo (v2 + v3/v4/v5 module-ready) Β· EVC
Image codecs β PNG / APNG Β· GIF Β· WebP (lossy + lossless + animated) Β· JPEG (via MJPEG, baseline + progressive + arithmetic) Β· JPEG XL (decoder) Β· JPEG 2000 Β· JPEG XS Β· AVIF (decoder) Β· HEIF / HEIC (via H.265 β clap / irot / imir / iovl / grid / auxC) Β· TIFF Β· BMP Β· ICO
Subtitles β SRT Β· WebVTT Β· ASS / SSA Β· TTML Β· SAMI Β· MicroDVD Β· MPL2 Β· MPsub Β· VPlayer Β· PJS Β· AQTitle Β· JACOsub Β· RealText Β· SubViewer 1/2 Β· EBU STL Β· PGS Β· DVB Β· VobSub
Each crate ships its own corpus + trace harness against an external
reference (ffmpeg / cwebp / libpng / TurboJPEG / etc.) plus a
cargo-fuzz harness for the fully-functional encode + decode paths.
Clone oxideav-workspace
plus any sibling crate you want to hack on, then run
scripts/dev-patch.sh. The generated .cargo/config.toml wires every
local sibling into the workspace via [patch.crates-io], so
cargo run -p oxideplay picks up your local edits without a
re-publish.
Three GitHub Actions reusable workflows live in this repo and are shared by every sibling crate so we don't have to keep ~95 copies of the same YAML in sync:
crate-ci.ymlβcargo build --all-targets+cargo teston the OS matrix (Linux + macOS + Windows by default),cargo fmt --check,cargo clippy --no-deps -- -D warnings, and an optional miri job (rate-limited to once-a-day per repo).crate-fuzz.ymlβ daily nightlycargo-fuzzrun for crates that ship harnesses underfuzz/. Auto-discovers targets fromfuzz/fuzz_targets/*.rs, splits the per-day budget across them, and persists the corpus across runs.crate-release.ymlβ wraps release-plz: opens/refreshes the release PR on every push tomaster, then publishes to crates.io + tags the GitHub Release once that PR is merged.
We are still in heavy development, so callers should track @master
(no pinned version yet β there's no v1 tag).
Replace the crate's .github/workflows/ci.yml with:
name: CI
on:
push: { branches: [master] }
pull_request: { branches: [master] }
jobs:
ci:
uses: OxideAV/.github/.github/workflows/crate-ci.yml@master
with:
enable_miri: false
secrets: inheritβ¦and the crate's .github/workflows/release-plz.yml with:
name: Release-plz
on:
push: { branches: [master] }
# Caller must grant the permissions the reusable workflow needs β
# workflow_call does NOT elevate the calling token.
permissions:
contents: write
pull-requests: write
jobs:
release:
uses: OxideAV/.github/.github/workflows/crate-release.yml@master
secrets: inheritOptional: add .github/workflows/fuzz.yml for nightly fuzz runs:
name: Fuzz
on:
schedule:
- cron: "37 4 * * *" # daily 04:37 UTC, jittered off the hour
workflow_dispatch:
jobs:
fuzz:
uses: OxideAV/.github/.github/workflows/crate-fuzz.yml@master
with:
extra_packages_apt: "libwebp-dev" # whatever the harness dlopens
time_budget_seconds: 1800
secrets: inherit| Input | Type | Default | Purpose |
|---|---|---|---|
enable_miri |
bool | false |
Add a miri job on nightly with -Zmiri-strict-provenance -Zmiri-disable-isolation. |
extra_test_args |
string | "" |
Appended verbatim to cargo test (e.g. --features foo). |
rust_toolchain |
string | "stable" |
Toolchain channel for the test job. |
os_matrix |
string | '["ubuntu-latest", "macos-latest", "windows-latest"]' |
JSON array of runner OSes. Override e.g. to drop Windows. |
| Input | Type | Default | Purpose |
|---|---|---|---|
time_budget_seconds |
number | 1800 |
Total fuzz time across all targets (each target gets time_budget / num_targets). |
targets |
string | "" |
Comma-separated fuzz target names. Empty = auto-discover from fuzz/fuzz_targets/*.rs. |
extra_packages_apt |
string | "" |
Space-separated apt packages to install before fuzzing (libwebp-dev libpng-dev β¦). |
extra_packages_brew |
string | "" |
Space-separated brew packages (only used when os contains macos). |
os |
string | "ubuntu-latest" |
Runner OS. Fuzz is Linux-friendly; macOS is supported but rarely needed. |
sanitizer |
string | "address" |
libfuzzer sanitizer: address / none / memory / thread. |
| Input | Type | Default | Purpose |
|---|---|---|---|
rust_toolchain |
string | "stable" |
Toolchain installed before release-plz runs. |
CARGO_REGISTRY_TOKENβ crates.io publish token (release workflow).RELEASE_PLZ_TOKEN(optional) β PAT used so the tag push triggers downstream workflows; falls back toGITHUB_TOKEN.
OxideAV is an open-source pure-Rust media stack maintained by Karpelès Lab Inc. If it saves you C dependency hell or unblocks a Rust-only build target you couldn't otherwise reach, please consider supporting continued development:
GitHub also surfaces this link automatically via the per-repository
Sponsor button (configured in .github/FUNDING.yml on this repo).
Every crate is MIT-licensed. Copyright © 2026 Karpelès Lab Inc.