audio-opus-bsd 0.1.0

Opus codec (RFC 6716) encoder/decoder for the sonicbrew audio-toolkit — libopus binding, worker-thread encode/decode emitting planar audio-core-bsd AudioFrames
docs.rs failed to build audio-opus-bsd-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

audio-opus-bsd

License: BSD-2-Clause Rust

Opus codec (RFC 6716) encoder and decoder. It wraps system libopus through the opus crate and emits/consumes planar AudioFrames (from the audio-core-bsd dependency). Encode and decode run on worker threads — this crate is deliberately not real-time safe; decoded frames are handed to the RT audio thread through a lock-free rtrb ring buffer.

The doc comments on each public item are the primary reference. This README is an overview only.

Overview

audio-opus-bsd is a standalone Opus codec crate: it depends only on audio-core-bsd (the shared planar AudioFrame type) and the system libopus library, and can be built and used independently of any larger audio framework. It turns planar AudioFrames into Opus packets and back. Because the opus crate is a thin FFI binding over libopus (which allocates and may make syscalls), all encode/decode work is moved onto a dedicated worker thread. The real-time audio thread touches only the lock-free rtrb::Consumer::pop — never the codec. This is the standard RT-safety boundary for FFI-based audio codecs: the real-time path stays wait-free and allocation-free.

Why a worker thread?

Layer What runs Allocation
Worker thread libopus encode/decode, planar↔interleaved conversion, packet framing allowed
RT audio thread rtrb::Consumer::pop of a pre-decoded AudioFrame none (wait-free)

The rt_alloc_free integration test asserts the RT pop path performs zero heap allocations.

Core types

Type Description
AudioEncoder The public trait: encode(&[f32]) -> Result<Vec<u8>> (interleaved PCM in) + set_bitrate.
AudioDecoder The public trait: decode(&[u8]) -> Result<Vec<f32>> (interleaved PCM out).
OpusEncoder libopus-backed encoder; also offers encode_frame(&AudioFrame) (planar in).
OpusDecoder libopus-backed decoder; also offers decode_frame(&[u8]) -> AudioFrame (planar out).
OpusDecodeWorker Spawns a decode worker thread; returns a rtrb::Consumer<AudioFrame> for the RT side.
OpusEncodeWorker Spawns an encode worker thread; returns a rtrb::Consumer<Vec<u8>> for the sender side.
OpusError Error enum (thiserror-backed): Encode, Decode, InvalidSampleRate, UnsupportedChannels, BufferTooSmall, LayoutMismatch, WorkerThread.
Result<T> Alias for core::result::Result<T, OpusError>.

Opus supports only mono (1) or stereo (2) channels and the standard 8/12/16/24/48 kHz sample rates. The canonical encode/decode block is 960 samples/channel (20 ms @ 48 kHz).

Dependencies

Dependency Version Purpose License
audio-core-bsd 0.1.0 AudioFrame (planar) I/O type BSD-2-Clause
opus 0.3 libopus FFI binding (encode_float/decode_float) MIT OR Apache-2.0
rtrb 0.3 lock-free SPSC ring buffer (RT↔worker channel) MIT OR Apache-2.0
thiserror 2.0 OpusError derive MIT OR Apache-2.0
proptest (dev) 1.11.0 property tests MIT OR Apache-2.0

System dependency — libopus

This crate links the system libopus (audio/opus port on FreeBSD, libopus-dev on Debian/Ubuntu). On FreeBSD:

sudo pkg install -y opus

On Debian/Ubuntu: sudo apt-get install -y libopus-dev.

Licensing: the opus Rust crate is MIT OR Apache-2.0; the system library it links, libopus, is BSD-3-Clause. Both are permissive and compatible with this crate's BSD-2-Clause license — no copyleft contamination.

Status

0.x — experimental. The API is not yet frozen (pre-1.0 breaking changes are allowed before 1.0).

  • edition: 2021
  • MSRV: 1.85
  • license: BSD-2-Clause

Example

Encode planar AudioFrames and decode them back through the worker-thread boundary:

use audio_opus_bsd::{OpusEncoder, OpusDecoder, AudioEncoder, AudioDecoder};
use audio_core_bsd::AudioFrame;

// 20 ms mono block @ 48 kHz.
let mut enc = OpusEncoder::new(48_000, 1, opus::Application::Audio)?;
enc.set_bitrate(64_000);

let frame = AudioFrame::silence(1, 960, 48_000);
let packet = enc.encode_frame(&frame)?;

let mut dec = OpusDecoder::new(48_000, 1)?;
let back = dec.decode_frame(&packet)?;
assert_eq!(back.num_frames(), 960);
# Ok::<(), audio_opus_bsd::OpusError>(())

For the RT-safe path (decode on a worker, consume wait-free on the RT thread), see OpusDecodeWorker and the examples/ directory.

Worker-thread safety boundary

OpusEncoder / OpusDecoder are not real-time safe — they perform FFI and heap allocation, which is correct and intended. Always move them onto a worker thread (OpusDecodeWorker / OpusEncodeWorker) and let the RT audio thread consume only from the returned rtrb::Consumer. The rt_alloc_free and worker_xrun integration tests verify this contract.

License

BSD-2-Clause. See LICENSE.