mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-29 04:27:47 -06:00
New L0 crate written from the AVI RIFF file reference and OpenDML 1.02: avih and per-stream strh / strf (BITMAPINFOHEADER, WAVEFORMATEX(TENSIBLE)) / strn, and chunk tables from the best index a file has: the OpenDML indx super index and ix## standard indexes (field indexes too), else idx1 (offsets relative to movi or absolute) plus a scan of RIFF AVIX extensions it cannot see, else a scan of the movi lists (key frames then unknown). Frames are read on demand through a ByteSource. Sizes are clamped to the file, so a cut-off recording opens with what is there; rec lists nest at most 3 deep and tables are bounded by what the file could hold. Tests: hand-built files for every index path, truncation, and every single-byte mutation and truncation (no panic); ffmpeg-made files against ffprobe; an ignored test on a 1.16 GB OpenDML file with AVIX extensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
44 lines
2.2 KiB
Markdown
44 lines
2.2 KiB
Markdown
# filmcraft-avi
|
|
|
|
A clean-room AVI demuxer, written from Microsoft's published AVI RIFF file reference and the
|
|
OpenDML AVI File Format Extensions 1.02. Layer L0: no dependencies beyond `std`, no `unsafe`, builds
|
|
for `wasm32-unknown-unknown`. No FFmpeg, libavformat or other AVI implementation was read; ffmpeg
|
|
and ffprobe are used only as external **test oracles**.
|
|
|
|
## API
|
|
|
|
```rust
|
|
use filmcraft_avi::{open, StreamKind};
|
|
|
|
let file = open(&bytes)?; // any ByteSource: a slice, a file, a web Blob…
|
|
for (i, s) in file.streams.iter().enumerate() {
|
|
match s.kind {
|
|
StreamKind::Video => println!("{:?} {} fps", s.video.as_ref().map(|b| b.compression), s.rate as f64 / s.scale as f64),
|
|
StreamKind::Audio => println!("format 0x{:04x}", s.audio.as_ref().map_or(0, |w| w.format_tag)),
|
|
_ => {}
|
|
}
|
|
let first = file.read_chunk(&bytes, i, 0)?; // frame data, on demand
|
|
}
|
|
```
|
|
|
|
## What it reads
|
|
|
|
| Part | |
|
|
|---|---|
|
|
| Headers | `avih`; per stream `strh`, `strf` (`BITMAPINFOHEADER`, `WAVEFORMATEX` / `WAVEFORMATEXTENSIBLE`), `strn` |
|
|
| Index | OpenDML `indx` super index → `ix##` standard indexes (field indexes too), else `idx1` (offsets relative to `movi` or absolute), else a scan of the `movi` lists |
|
|
| Large files | `RIFF AVIX` extensions: through the OpenDML index, or scanned after `idx1`, which only covers the first RIFF |
|
|
| Key frames | from the index; a scan marks every chunk and says so (`keyframes_known`) |
|
|
| Damage | sizes clamped to the file (a cut-off recording opens with what is there), `rec ` lists nested at most 3 deep, tables bounded by what the file could hold |
|
|
|
|
`filmcraft-codecs` maps the streams onto decoders (`AviSource`): Motion JPEG, H.264, HEVC and
|
|
uncompressed RGB / YUV video; PCM, MP3, MP2 and AC-3 audio.
|
|
|
|
## Tests
|
|
|
|
`cargo test -p filmcraft-avi`: hand-built files for every index path (`idx1` relative and absolute,
|
|
`rec ` lists, OpenDML standard indexes, a scan), truncation, and every single-byte mutation and
|
|
truncation of them (never a panic). `tests/ffmpeg_oracle.rs` checks ffmpeg-made files (Motion JPEG,
|
|
H.264 with B-frames, BGR24, YUY2; PCM and MP3) against ffprobe; the ignored `opendml_file_past_1_gb`
|
|
checks a 1.16 GB OpenDML file with `AVIX` extensions.
|