RustyOpus (rusty_opus v0.3.3)

Copy Markdown View Source

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 …) — the reencode/2 input/output.
  • PCM is a binary of 32-bit little-endian IEEE-754 f32 samples, 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

change_quality(packet, rate, channels, quality, opts \\ [])

@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 (default div(rate, 50))
  • any other RustyOpus.Settings key — 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)

contained_panic()

@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.

decode(packets, rate, channels, opts \\ [])

@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).

decode_packet(packet, rate, channels, frame_size)

@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.

encode(pcm, rate, channels, opts \\ [])

@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.

encode_pcm(pcm, rate, channels, opts \\ [])

@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.

native_smoke()

@spec native_smoke() :: {:ok, String.t()} | {:error, term()}

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.

reencode(blob, opts \\ [])

@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
  • :complexity0..10 (default 10, FFmpeg -compression_level)
  • :cbrtrue for CBR; default false (VBR, FFmpeg -vbr on)
  • :frame_duration_ms10 or 20 (default 20). 40/60 are rejected: opus-rs cannot 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
  )

transcode(packets, rate, channels, quality, opts \\ [])

@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.

translated_error()

@spec translated_error() :: {:error, RustyOpus.Error.t()}

Translates a deterministic native error into a stable RustyOpus.Error.