zenavif-parse 0.2.1

AVIF container parser with zero-copy AvifParser API, grid images, animation, and resource limits. Fork of avif-parse.
Documentation

zenavif-parse

AVIF container parser (ISOBMFF/MIAF demuxer) that extracts AV1 payloads, alpha channels, grid tiles, and animation frames from AVIF files. Written entirely in safe Rust with fallible allocations throughout.

This is a fork of kornelski/avif-parse, which itself descends from Mozilla's MP4 parser used in Firefox. The upstream crate is battle-tested against untrusted data; this fork extends it with the features needed for a complete AVIF decoder.

What changed from avif-parse

The upstream avif-parse handles single still images well. This fork adds everything else an AVIF decoder needs:

New zero-copy APIAvifParser parses box structure and records byte offsets without copying mdat content. Data access returns Cow<[u8]> — borrowed for single-extent items (the common case), owned only when extents must be concatenated.

Grid image support — Parses iref dimg references and ImageGrid property boxes to identify grid tiles. Falls back to calculating grid layout from ispe (Image Spatial Extents) properties when no explicit grid box exists.

Animated AVIF — Parses moov/trak boxes, stts timing, stco/co64 chunk offsets, and sample tables. Frames are accessed on-demand via iterator or index.

Resource limitsDecodeConfig caps peak memory, megapixels, animation frame count, and grid tile count during parsing. Limits are checked before allocations, not after.

Cooperative cancellation — All parsing paths accept an enough::Stop token for cancellation.

Parsing fixes — Size-0 box support (ISOBMFF "extends to EOF"), idat construction method support (iloc construction_method=1), correct construction_method handling (upstream guessed based on offset value).

The original read_avif() / AvifData API is preserved for backwards compatibility, but AvifParser is preferred for new code.

Usage

Zero-copy parser (recommended)

use zenavif_parse::AvifParser;

let bytes = std::fs::read("image.avif")?;
let parser = AvifParser::from_bytes(&bytes)?;

// Primary item — zero-copy for single-extent items
let primary = parser.primary_data()?;
decode_av1(&primary)?;

// Alpha channel
if let Some(alpha) = parser.alpha_data() {
    decode_av1(&alpha?)?;
}

if parser.premultiplied_alpha() {
    // divide RGB by A after decoding
}

Three constructors, each with a _with_config variant for resource limits:

  • from_bytes(&[u8]) — borrows the input; data access is zero-copy
  • from_owned(Vec<u8>) — takes ownership; returns AvifParser<'static>
  • from_reader(impl Read) — reads into an owned buffer; returns AvifParser<'static>

Grid images

if let Some(grid) = parser.grid_config() {
    println!("{}x{} tiles, output {}x{}",
        grid.columns, grid.rows,
        grid.output_width, grid.output_height);
    for i in 0..parser.grid_tile_count() {
        let tile = parser.tile_data(i)?;
        decode_av1(&tile)?;
    }
}

Animated AVIF

if let Some(info) = parser.animation_info() {
    for frame in parser.frames() {
        let frame = frame?;
        decode_av1(&frame.data)?;
        // display for frame.duration_ms milliseconds
    }
}

AV1 metadata without decoding

let meta = parser.primary_metadata()?;
println!("{}x{}, {}bpc, chroma {:?}",
    meta.max_frame_width, meta.max_frame_height,
    meta.bit_depth, meta.chroma_subsampling);

Resource limits

use zenavif_parse::{AvifParser, DecodeConfig};

let config = DecodeConfig::default()
    .with_peak_memory_limit(64 * 1024 * 1024)   // 64MB
    .with_total_megapixels_limit(128)
    .with_max_animation_frames(500)
    .with_max_grid_tiles(64);

let parser = AvifParser::from_bytes_with_config(
    &bytes, &config, &enough::Unstoppable
)?;

Defaults: 1GB peak memory, 512MP total, 10k frames, 1k tiles. Use DecodeConfig::unlimited() to disable all limits.

Legacy API (feature = "eager")

The original read_avif() / AvifData API and C FFI are behind the eager feature flag, off by default.

[dependencies]
zenavif-parse = { version = "0.2", features = ["eager"] }
use zenavif_parse::read_avif;

let data = read_avif(&mut reader)?;
decode_av1(&data.primary_item)?;

Upstream contributions welcome

All code in this fork is available under the same MPL-2.0 license as the original. The upstream maintainers are welcome to incorporate any or all changes — no attribution to this fork required. If specific features would be useful upstream in a different form, open an issue and we can restructure to fit.

Any copyright claims to changes in this fork are released to and under the upstream license. If changes in this fork are desired upstream by the upstream maintainers, please open an issue requesting a PR.

Credits

This crate builds directly on work by:

  • Kornel Lesinski — created and maintains avif-parse
  • Mozilla — the original mp4parse crate that avif-parse forked from, used in Firefox
  • Ralph Giles, Matthew Gregan, Alfredo Yang, Jon Bauman — original mp4parse authors

License

MPL-2.0 (unchanged from upstream).

This crate doesn't include an AV1 decoder. For full AVIF decoding, see zenavif which pairs this parser with rav1d.