Skip to main content

Crate opus_pure

Crate opus_pure 

Source
Expand description

Pure-Rust Opus audio codec (RFC 6716) with Ogg encapsulation (RFC 7845).

Encoder and decoder for all three Opus coding modes — SILK for speech, CELT for music, and the hybrid of both — plus a real Ogg container layer, so this crate reads and writes .opus files rather than only raw packets.

§Encoding to an .opus file

use opus_pure::{Application, MAX_PACKET_BYTES, OggOpusWriter, OpusEncoder, OpusHead};

let (rate, channels, frame) = (48_000, 2, 960); // 20 ms stereo
let pcm = vec![0.0f32; frame * channels * 50];  // one second of silence

let mut encoder = OpusEncoder::new(rate, channels, Application::Audio)?;
encoder.bitrate_bps = 96_000;

// The header takes its pre-skip from the encoder's own delay rather than a
// constant, which is what makes it right for every `Application`.
let head = OpusHead::for_encoder(&encoder, rate as u32);
let mut writer = OggOpusWriter::new(Vec::new(), head)?;
let mut packet = vec![0u8; MAX_PACKET_BYTES];
for block in pcm.chunks_exact(frame * channels) {
    let n = encoder.encode(block, frame, &mut packet)?;
    writer.write_packet(&packet[..n])?;
}
let file: Vec<u8> = writer.finish()?;
assert_eq!(&file[..4], b"OggS");

finish writes the end-of-stream page and must be called; dropping the writer flushes on a best-effort basis but cannot report an I/O failure.

§Integer PCM

Both directions have a 16-bit entry point, for the many callers whose audio is already i16. They are not wrappers over the float ones, any more than libopus’s are: encode_s16 declares 16 bits of input precision where encode declares 24, and decode_s16 soft-clips before converting, which decode does not. See SoftClip for why that second one matters and how to get it on the float path.

use opus_pure::{Application, MAX_PACKET_BYTES, OpusDecoder, OpusEncoder};

let mut encoder = OpusEncoder::new(48_000, 2, Application::Audio)?;
let mut decoder = OpusDecoder::new(48_000, 2)?;

let pcm = vec![0i16; 960 * 2];                  // 20 ms of stereo at 48 kHz
let mut packet = vec![0u8; MAX_PACKET_BYTES];
let n = encoder.encode_s16(&pcm, 960, &mut packet)?;

let mut out = vec![0i16; 960 * 2];
let samples = decoder.decode_s16(&packet[..n], 960, &mut out)?;
assert_eq!(samples, 960);

§Decoding one back

use opus_pure::{Application, MAX_PACKET_BYTES, MAX_PACKET_SAMPLES, OggOpusReader,
               OggOpusWriter, OpusEncoder, OpusHead, Trim};
let mut reader = OggOpusReader::new(std::io::Cursor::new(&file))?;
let head = reader.head().clone();
let channels = head.channel_count as usize;

// Carries the channel count and the header's output gain.
let mut decoder = head.decoder(48_000)?;
// Takes the encoder delay off the front and the end-trim off the back.
let mut trim = Trim::new(&head, 48_000, channels)?;

let mut block = vec![0.0f32; MAX_PACKET_SAMPLES * channels];
let mut out = Vec::new();
for packet in reader.packets() {
    let packet = packet?;
    let n = decoder.decode(&packet.data, MAX_PACKET_SAMPLES, &mut block)?;
    out.extend_from_slice(trim.keep(&packet, &block[..n * channels]));
}
// One second in, one second back, less the encoder delay that the stream
// above never flushed — see below.
assert_eq!(trim.samples_emitted(), 48_000 - u64::from(head.pre_skip));

§Where a stream begins and ends

A decoded Opus stream is longer than the audio that went into it at both ends, and RFC 7845 gives both corrections: the pre_skip at the front (§4.2, the encoder’s algorithmic delay) and an end-trim at the back (§4.4, a final granule position deliberately short of what the packets decode to). Trim applies the pair, which is worth reaching for even though it is ten lines: the first correction is conspicuous when it is missing and the second is silent, and every file opusenc writes carries one.

Writing them is the same job in reverse, and it is not automatic: OggOpusWriter documents the tail arithmetic, and write_packet_with_duration is what states the end-trim. The example above writes a whole number of frames and no end-trim, so it comes back one encoder delay short — which is what that arithmetic exists to fix.

Build the header with OpusHead::for_encoder and the pre-skip is measured from the encoder rather than assumed; OpusHead::new uses the conventional 312, which is four milliseconds too many for Application::RestrictedLowDelay.

§Working with raw packets

OpusEncoder and OpusDecoder are usable on their own when the framing comes from elsewhere (RTP, a custom container). Repacketizer combines and splits packets, and encode_parallel encodes a clip across threads by splitting it into chunks — a different encode from the serial one, and parallel is explicit about how it differs.

Re-exports§

pub use multistream::ChannelLayout;
pub use multistream::OpusMSDecoder;
pub use multistream::OpusMSEncoder;
pub use ogg::OggOpusReader;
pub use ogg::OggOpusWriter;
pub use ogg::OggPacket;
pub use ogg::OpusHead;
pub use ogg::OpusTags;
pub use ogg::Trim;
pub use packet::MAX_PACKET_SAMPLES;
pub use parallel::DEFAULT_WARMUP_MS;
pub use parallel::ParallelConfig;
pub use parallel::ParallelPlan;
pub use parallel::encode_parallel;
pub use repacketizer::Repacketizer;

Modules§

multistream
Opus multistream (surround) — port of the core of src/opus_multistream_{encoder,decoder}.c. Wraps N mono/coupled Opus coders behind a channel-mapping layout so >2-channel audio (quad, 5.1, 7.1) can be coded as a set of standard Opus streams concatenated with the self-delimited framing.
ogg
Ogg encapsulation of Opus streams — RFC 7845.
packet
Reading an Opus packet’s shape without decoding it.
parallel
Chunk-parallel Opus encoding: split the input into contiguous frame ranges and encode them on separate threads.
repacketizer
Port of libopus src/repacketizer.c + the packet helpers from src/opus.c: split Opus packets into frames and recombine/re-frame/pad them WITHOUT re-encoding. Used to merge several packets into a longer one, split a multi-frame packet, or pad a packet to a target size (e.g. for CBR transport). All frames in a repacketizer must share the same TOC config (mode/bandwidth/frame-size); only the code (0..3) and framing change.

Structs§

OpusDecoder
An Opus decoder: Opus packets in, PCM out.
OpusEncoder
An Opus encoder: PCM in, Opus packets out.
SoftClip
Carries the soft-clipping curve across frame boundaries for one stream.

Enums§

Application
What the encoder is being asked to optimise for, fixed when it is created.
Bandwidth
The audio bandwidth a packet carries, which is what Opus varies instead of the sample rate.
Error
Anything that can go wrong encoding, decoding, or parsing an Opus stream.
OpusMode
Which coding layers a packet actually used.
RateControl
How the encoder is allowed to vary the size of each packet.
Signal
OPUS_SET_SIGNAL hint: bias mode selection toward speech or music. None = OPUS_AUTO (let the analysis decide).

Constants§

MAX_PACKET_BYTES
The largest packet OpusEncoder::encode can produce, and therefore the output buffer size that never costs you bitrate.

Type Aliases§

Result
Shorthand for a codec result.