Pure-Rust Opus (RFC 6716) for Elixir.
Shrink an Ogg Opus blob to a numeric bitrate in one call (no ffmpeg, no C libopus). Defaults are speech-oriented (VoIP, complexity 10, VBR, 20 ms):
{:ok, smaller} = RustyOpus.reencode(ogg_blob, bitrate: 20_000)Raw packet/PCM helpers remain for codec work without a container:
{:ok, packets} = RustyOpus.encode(pcm, 16_000, 1, bitrate: 24_000)
{:ok, pcm} = RustyOpus.decode(packets, 16_000, 1)Data contract
- Ogg Opus blobs are RFC 7845 files (
OggS…) — thereencode/2input/output. - PCM is a binary of 32-bit little-endian IEEE-754
f32samples, interleaved for stereo. - Opus packets are raw binaries (no container).
Boundary
Codec and Ogg Opus reencode run in-process via Rustler. No external process, Port, or executable is used for codec work.
Summary
Functions
Changes the encoding quality of one Opus packet: decodes it to PCM and re-encodes
it at a new quality, returning the re-encoded packet.
Runs a contained native panic and reports {:error, :contained_panic}.
Decodes a list of Opus packets into one concatenated PCM binary.
Decodes a single Opus packet into PCM with a short-lived decoder.
Encodes a whole PCM buffer into a list of Opus packets.
Encodes a single PCM frame into an Opus packet with a short-lived encoder.
Returns the loaded native library version as {:ok, version}.
Reencodes an Ogg Opus blob at a target bitrate (bits/s).
Transcodes a list of Opus packets to a new quality.
Translates a deterministic native error into a stable RustyOpus.Error.
Functions
@spec change_quality( binary(), pos_integer(), 1 | 2, RustyOpus.Quality.quality(), keyword() ) :: {:ok, binary()} | {:error, RustyOpus.Error.t()}
Changes the encoding quality of one Opus packet: decodes it to PCM and re-encodes
it at a new quality, returning the re-encoded packet.
Prefer transcode/5 for whole streams. quality is a preset
(:low/:medium/:high) or a RustyOpus.Settings struct. Options:
:target_bitrate— overrides the preset bitrate:frame_size— decoded frame size in samples per channel (defaultdiv(rate, 50))- any other
RustyOpus.Settingskey — overrides the preset
Example
{:ok, high} = RustyOpus.change_quality(packet, 16_000, 1, :high)
{:ok, low} = RustyOpus.change_quality(packet, 16_000, 1, :low, target_bitrate: 12_000)
@spec contained_panic() :: {:error, :contained_panic} | {:ok, term()}
Runs a contained native panic and reports {:error, :contained_panic}.
The panic never unwinds across the NIF boundary, so the caller BEAM is untouched.
@spec decode([binary()], pos_integer(), 1 | 2, keyword()) :: {:ok, binary()} | {:error, RustyOpus.Error.t()}
Decodes a list of Opus packets into one concatenated PCM binary.
Opens a short-lived decoder, runs the bulk path, and closes it. Optional
:frame_size defaults to div(rate, 50).
@spec decode_packet(binary(), pos_integer(), 1 | 2, pos_integer()) :: {:ok, binary()} | {:error, RustyOpus.Error.t()}
Decodes a single Opus packet into PCM with a short-lived decoder.
frame_size is the number of samples per channel in the decoded frame.
Prefer decode/4 for packet lists.
@spec encode(binary(), pos_integer(), 1 | 2, keyword()) :: {:ok, [binary()]} | {:error, RustyOpus.Error.t()}
Encodes a whole PCM buffer into a list of Opus packets.
Opens a short-lived encoder, runs the bulk path, and closes it. opts accept
encoder settings (RustyOpus.Settings), optional :frame_size (default
div(rate, 50)), optional :quality (preset atom or Settings), and
:application (default :audio). A short last frame is padded with silence.
@spec encode_pcm(binary(), pos_integer(), 1 | 2, keyword()) :: {:ok, binary()} | {:error, RustyOpus.Error.t()}
Encodes a single PCM frame into an Opus packet with a short-lived encoder.
pcm must contain exactly one frame (frame_size * channels samples). opts are
passed to RustyOpus.Encoder.new/4 (see RustyOpus.Settings and RustyOpus.Quality).
Prefer encode/4 for whole buffers.
Returns the loaded native library version as {:ok, version}.
This proves the Elixir/Rust boundary is live. It fails with {:error, ...} if the
NIF is not loaded.
@spec reencode( binary(), keyword() ) :: {:ok, binary()} | {:error, RustyOpus.Error.t()}
Reencodes an Ogg Opus blob at a target bitrate (bits/s).
Demux → decode → encode → remux, all pure Rust (opus-rs + thin Ogg).
Defaults match the MemoMoo FFmpeg speech ladder as closely as opus-rs
allows: VoIP, complexity 10, VBR, 20 ms frames.
Options
:bitrate(required) — positive integer, bits per second (e.g.20_000):application—:voip(default),:audio, or:restricted_low_delay:complexity—0..10(default10, FFmpeg-compression_level):cbr—truefor CBR; defaultfalse(VBR, FFmpeg-vbr on):frame_duration_ms—10or20(default20).40/60are rejected:opus-rscannot encode those durations at 48 kHz on this path.
Example
{:ok, smaller} = RustyOpus.reencode(ogg_blob, bitrate: 20_000)
{:ok, smaller} =
RustyOpus.reencode(ogg_blob,
bitrate: 20_000,
application: :voip,
complexity: 10,
cbr: false,
frame_duration_ms: 20
)
@spec transcode( [binary()], pos_integer(), 1 | 2, RustyOpus.Quality.quality(), keyword() ) :: {:ok, [binary()]} | {:error, RustyOpus.Error.t()}
Transcodes a list of Opus packets to a new quality.
Bulk-decodes then bulk-encodes at quality (same resolution as change_quality/5).
One input packet produces one output packet, in order. opts may include
:frame_size, :target_bitrate, and other RustyOpus.Settings keys.
@spec translated_error() :: {:error, RustyOpus.Error.t()}
Translates a deterministic native error into a stable RustyOpus.Error.