opus-rs
A pure-Rust implementation of the Opus audio codec (RFC 6716), ported from the reference C implementation (libopus 1.6).
Production-ready
Features
- Pure Rust — no C dependencies
- High Performance: Competitive with C libopus on x64/aarch64
#![no_std]+ noalloc: fully heap-free — runs on bare metal, RTOS, and WebAssembly with no global allocator
Quick Start
use ;
// Encode
let mut encoder = new.unwrap;
encoder.bitrate_bps = 16000;
encoder.use_cbr = true;
let input = vec!; // 20ms frame at 16kHz
let mut output = vec!;
let bytes = encoder.encode.unwrap;
// Decode
let mut decoder = new.unwrap;
let mut pcm = vec!;
let samples = decoder.decode.unwrap;
WAV Roundtrip
# Rust encoder/decoder
#![no_std] + no-alloc Support
opus-rs is fully heap-free: it builds as #![no_std] with no alloc crate and therefore needs no global allocator. All working buffers are inline, fixed-size arrays (sized to the Opus worst case); growable state uses an internal FixedVec<T, N> (a tiny Vec-like type over [MaybeUninit<T>; N]).
Disable the default std feature and enable libm (a pure-Rust math backend for sin/cos/sqrt/…, which core does not provide):
[]
= "0.1"
= false
= ["libm"]
The public API (OpusEncoder/OpusDecoder with encode/decode writing into caller-provided buffers) is identical to the std build.
Feature flags
| Feature | Default | Description |
|---|---|---|
std |
yes | Enables OS-backed runtime x86 SIMD detection (CPUID). Without it the crate is #![no_std]. |
libm |
no | Required for #![no_std] builds — provides float math via a pure-Rust port of musl libm. Not needed when std is on. |
Runtime dependency footprint:
stdbuild → 0 deps;no_stdbuild → 1 dep (libm, pure Rust).
Notes for no_std users
- Large structs: because every working buffer is inline,
OpusEncoder/OpusDecoderare large (~250 KB / ~175 KB each). On a constrained target, place them in astatic(wrap init in your ownOnce-like guard) or a dedicated buffer rather than on the stack. Test threads use a 16 MB stack (see.cargo/config.toml). - x86 SIMD: without
std, AVX/AVX2 dispatch falls back to compile-time detection. Build withRUSTFLAGS="-C target-feature=+avx2"to enable it. aarch64 NEON is unconditional. - Packets are capped at the RFC 6716 maximum of 1276 bytes (the heap-free range-coder buffer is sized accordingly); standalone
RangeCoder/CELT use supports up to 2048 bytes. - Verified targets:
thumbv7em-none-eabi(Cortex-M),wasm32-unknown-unknown, and any Linux no_std target. Check withscripts/check_no_std.sh.
Performance
Criterion benchmark (cargo bench --bench opus_vs_c_bench) with 20 samples, 100 ms warm-up, 500 ms measurement, real speech input (fixtures/answer_16k.wav), mono encode-only. All numbers below are wall-clock time for the full frame set.
vs C Opus (libopus 1.6.1) on x86-64 (AVX2/FMA)
Measured on AMD Ryzen 7 5700X, compiled with --release (opt-level=3 + ThinLTO).
| Config | Pure Rust | C Opus | Ratio |
|---|---|---|---|
| 8 kHz / 20 ms VoIP | 39.9 ms | 40.6 ms | 0.98× (Rust 2% faster) |
| 16 kHz / 20 ms VoIP | 66.8 ms | 67.1 ms | 1.00× (Rust 0.5% faster) |
| 16 kHz / 10 ms VoIP | 73.2 ms | 72.5 ms | 1.01× (within noise) |
| 48 kHz / 20 ms Audio | 25.1 ms | 28.4 ms | 0.88× (Rust 12% faster) |
| 48 kHz / 10 ms Audio | 29.7 ms | 31.2 ms | 0.95× (Rust 5% faster) |
vs C Opus (libopus 1.6.1) on Apple Silicon
Measured on Apple Silicon M-series (aarch64), compiled with --release (opt-level=3 + ThinLTO), latest run on 2026-04-23.
| Config | Pure Rust | C Opus | Ratio |
|---|---|---|---|
| 8 kHz / 20 ms VoIP | 31.47 ms | 31.20 ms | 1.01× (C 0.9% faster) |
| 16 kHz / 20 ms VoIP | 51.19 ms | 52.81 ms | 0.97× (Rust 3.1% faster) |
| 16 kHz / 10 ms VoIP | 55.69 ms | 55.49 ms | 1.00× (within noise) |
| 48 kHz / 20 ms Audio | 13.97 ms | 19.39 ms | 0.72× (Rust 28% faster) |
| 48 kHz / 10 ms Audio | 16.19 ms | 20.28 ms | 0.80× (Rust 20% faster) |
Release Notes
0.1.28
- Fix: high-bitrate bitstream interop with libopus (issue #11). The ported
BAND_ALLOCATIONtable was missing one200in its final row (7 entries instead of 8), shifting the whole row. That row is only selected by the allocator at high bitrates, so encoded stereo streams above ~160 kbps decoded to garbage in libopus (and the crate decoder mis-decoded libopus streams at the same rates). Table now byte-identical to libopus. - Fix:
celt_pvq_u/celt_pvq_vpanic fork >= 129.compute_u's fixed-size buffer is now domain-checked (k <= MAX_PVQ_K = 128); out-of-domain calls returnu32::MAXinstead of panicking or silently wrapping. - Fix: silent u32 overflow in PVQ codebook sizes.
compute_u/unext/celt_pvq_vuse saturating arithmetic, so an out-of-domain(n, k)can never produce a plausible-but-wrong codebook size. - Fix:
celt_pvq_u_lookuprow-extent aliasing. Lookups whosemax(n, k)exceeds a row's actual coverage now compute instead of reading the next row's block. - Fix:
OpusEncoder::encodenow returns an error when the range coder overflows its packet budget (previously bytes were silently dropped). - Tests: add full-domain PVQ table verification vs exact big-int recurrence, out-of-domain saturation checks, and a high-bitrate (128–192 kbps) stereo interop test that cross-checks against libopus.
0.1.27
#![no_std]with noalloc: the crate is now fully heap-free — no global allocator is required. AllVec/Boxworking buffers were replaced with an internalFixedVec<T, N>(inline[MaybeUninit<T>; N]);std::sync::LazyLockwas replaced with a heap-freeOnceCell; no_std float math (sin/cos/sqrt/…) is routed through an optionallibmfeature (pure-Rust musl port).- Verified on
thumbv7em-none-eabi(Cortex-M),wasm32-unknown-unknown, and Linux no_std.scripts/check_no_std.shreproduces. - Feature flags:
std(default) → 0 runtime deps;default-features = false, features = ["libm"]→ 1 dep (libm). - Encoder/decoder structs are inline and large (~250 KB / ~175 KB); place them in a
staticor dedicated buffer on constrained targets.
- Verified on
- Performance: unchanged vs 0.1.26 (measured within ±0.5% on
opus_real; no per-frame allocation overhead). - Conformance: RFC 6716 1276-byte packet cap enforced on the Opus path; the standalone
RangeCoder/CELT buffer supports up to 2048 bytes. wav_testexample now tags output files by build (_std/_nostd) for A/B listening across build modes.
License
See COPYING for the original Opus license (BSD-3-Clause).
Links
- RustPBX: https://github.com/restsend/rustpbx
- RustRTC: https://github.com/restsend/rustrtc
- SIP Stack: https://github.com/restsend/rsipstack
- Rust Voice Agent: https://github.com/restsend/active-call