oxideav-codec 0.1.1

Codec (Decoder + Encoder) traits and registry for oxideav — pure Rust, no C deps
Documentation

oxideav-codec

Codec traits + registry for the oxideav pure-Rust media framework. Every per-format codec crate (MP3, AAC, H.264, GIF, …) implements Decoder and/or Encoder and registers itself via a CodecImplementation; the aggregator builds one CodecRegistry with the union of every enabled feature.

  • Decodersend_packetreceive_frame, plus flush (EOS drain) and reset (state wipe after seek).
  • Encodersend_framereceive_packet, plus flush.
  • CodecCapabilities — per-impl flags (lossless, intra-only, accepted pixel formats, supported sample rates, priority) so registries can pick the right implementation for a given request.
  • CodecRegistry — factory lookup by CodecId, with fallback when the first-choice implementation refuses the input. Also implements oxideav_core::CodecResolver, so containers can delegate tag-to-codec resolution (AVI FourCC, WAVEFORMATEX wFormatTag, MP4 OTI, Matroska CodecID) to the registry without a direct dep on this crate.
  • claim_tag — codecs attach one or more CodecTags to their id at registration time, with priority + an optional probe function for the tag-collision cases (e.g. DIV3 that's actually MPEG-4 Part 2).

Zero C dependencies. Zero FFI.

Usage

[dependencies]
oxideav-codec = "0.1"

Using a codec directly (standalone, no container, no pipeline)

OxideAV's codecs are designed to be usable on their own. If you already have raw encoded packets (from disk, the network, or another parser), you only need three crates: oxideav-core for the Packet / Frame types, oxideav-codec for the Decoder / Encoder traits + registry, and the specific codec crate you want.

Example — decoding G.711 µ-law:

[dependencies]
oxideav-core = "0.1"
oxideav-codec = "0.1"
oxideav-g711 = "0.0"
use oxideav_codec::CodecRegistry;
use oxideav_core::{CodecId, CodecParameters, Frame, Packet, TimeBase};

// 1. Register the codec with a registry (one-time setup).
let mut reg = CodecRegistry::new();
oxideav_g711::register(&mut reg);

// 2. Describe the stream — here 8 kHz mono µ-law.
let mut params = CodecParameters::audio(CodecId::new("pcm_mulaw"));
params.sample_rate = Some(8_000);
params.channels = Some(1);

// 3. Build a decoder.
let mut dec = reg.make_decoder(&params)?;

// 4. Feed packets + pull frames in a loop.
let pkt = Packet::new(/* stream_index */ 0, TimeBase::new(1, 8_000), mulaw_bytes);
dec.send_packet(&pkt)?;

match dec.receive_frame()? {
    Frame::Audio(a) => {
        // `a.data[0]` is interleaved S16 PCM; `a.channels` / `a.samples`
        // / `a.sample_rate` describe the shape.
    }
    _ => unreachable!("G.711 yields audio"),
}
# Ok::<(), oxideav_core::Error>(())

The packet → frame loop

send_packet / receive_frame is a two-step state machine. Most codecs accept exactly one packet per frame (a 1:1 mapping), so the pattern is:

for pkt in stream_of_packets {
    dec.send_packet(&pkt)?;
    let frame = dec.receive_frame()?;
    // ... use frame ...
}

Some codecs buffer internally (e.g. a video codec waiting for reference frames). In that case receive_frame returns Error::NeedMore until the decoder has enough input; keep sending packets until you get a frame, and keep pulling frames after each send (a single packet may produce multiple frames for some codecs). The canonical loop:

loop {
    match dec.receive_frame() {
        Ok(frame) => { /* consume */ }
        Err(oxideav_core::Error::NeedMore) => break, // send more packets
        Err(oxideav_core::Error::Eof) => return Ok(()),
        Err(e) => return Err(e),
    }
}

Signalling end-of-stream

When you've sent every packet and want to flush any still-buffered output:

dec.flush()?;
while let Ok(frame) = dec.receive_frame() { /* trailing frames */ }

Resetting after a seek

If you reposition the input mid-stream (seek), call reset() instead of flush(). reset drops buffered packets and wipes codec-internal state (LPC memory, IMDCT overlap, reference pictures) so the first frame after the seek decodes cleanly rather than glitching:

// After repositioning your byte stream:
dec.reset()?;
// ...resume feeding packets from the new position...

Encoding is symmetric

The Encoder trait mirrors Decoder — build via reg.make_encoder(&params), feed raw Frames with send_frame, pull encoded Packets with receive_packet. See each codec's README for per-format parameter requirements (sample rate, channel count, bitrate, pixel format, etc.).

Extending the registry

Codec crates expose a single register(reg) function that pushes their implementation(s) into the registry. The registry supports multiple implementations per codec id with priority + capability fallback, so you can register a fast-but-lossy impl and a slow-but-accurate one and have the registry pick based on CodecPreferences:

pub fn register(reg: &mut oxideav_codec::CodecRegistry) {
    let caps = CodecCapabilities::audio("my_codec_sw")
        .with_lossless(true);
    reg.register_both(
        CodecId::new("my-codec"),
        caps,
        make_decoder,
        make_encoder,
    );
}

Claiming container tags

A codec can attach container-level tags to its id so demuxers resolve them automatically. Tag kinds live in oxideav_core::CodecTag — AVI FourCC, WAVEFORMATEX wFormatTag, MP4 ObjectTypeIndication, and Matroska CodecID strings. Priority breaks ties; an optional probe disambiguates genuine collisions (the DIV3-tagged-as-MPEG-4-Part-2 case that breaks naive FourCC tables):

use oxideav_core::{CodecId, CodecTag};

let cid = CodecId::new("mpeg4video");

// Unambiguous claims: just a priority.
reg.claim_tag(cid.clone(), CodecTag::fourcc(b"XVID"), 10, None);
reg.claim_tag(cid.clone(), CodecTag::fourcc(b"DX50"), 10, None);

// Ambiguous: DIV3 is usually msmpeg4v3 but sometimes real Part 2.
// Claim it at a lower priority with a probe — the demuxer decides
// per-packet by peeking at the bitstream.
reg.claim_tag(cid, CodecTag::fourcc(b"DIV3"), 5, Some(probe_is_mpeg4_part2));

Demuxers resolve via registry.resolve_tag(&tag, probe_data) — which is what oxideav-avi, oxideav-mp4, and oxideav-mkv do internally.

License

MIT — see LICENSE.