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 fromsrc/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§
- Opus
Decoder - An Opus decoder: Opus packets in, PCM out.
- Opus
Encoder - An Opus encoder: PCM in, Opus packets out.
- Soft
Clip - 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.
- Opus
Mode - Which coding layers a packet actually used.
- Rate
Control - 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::encodecan produce, and therefore the output buffer size that never costs you bitrate.
Type Aliases§
- Result
- Shorthand for a codec result.