container-probe
Robust media container-format detection over a byte prefix — the MPEG-2 TS, ISOBMFF, Matroska/WebM, MXF, MPEG-PS, FLV, WAV, Ogg, ASF, ADTS AAC, MP3 and Annex B probers, scored and compared by an evidence-confidence model with cross-prober suppression.
#![no_std] + alloc; runtime dependency is broadcast-common only.
What it detects
Every registered prober runs over the same bytes and returns a scored candidate; the highest score wins.
| Format | Detected by | Confidence tier |
|---|---|---|
| MPEG-2 TS | sync lattice over 188/192/204/208-byte strides | LATTICE_STRONG (128) |
ISOBMFF (.mp4/.m4s) |
box-chain walk (ISO/IEC 14496-12 §4.2) | STRUCTURAL (160) |
Matroska (.mkv) |
EBML magic + DocType == "matroska" |
CERTAIN (240) |
WebM (.webm) |
EBML magic + DocType == "webm" |
CERTAIN (240) |
MXF (.mxf) |
partition-pack key + valid BER length | CERTAIN (240) |
MPEG-PS (.ps/.mpg) |
pack start code + marker bits | STRUCTURAL (160) |
FLV (.flv) |
"FLV" signature + header fields |
STRONG (192) |
WAV (.wav) |
"RIFF".."WAVE" |
STRONG (192) |
Ogg (.ogg) |
"OggS" |
STRONG (192) |
ASF (.asf/.wmv) |
16-byte header GUID | STRONG (192) |
ADTS AAC (.aac/.adts) |
frame-length chaining | LATTICE_STRONG (128) |
MP3 (.mp3) |
frame-length chaining | LATTICE_STRONG (128) |
Annex B H.264 (.h264) |
start-code NAL chaining | LATTICE_STRONG (128) |
Usage
use ;
// The first 8 bytes of a TS packet's sync-adjacent header. This is too short
// for any prober to conclude from, so `probe` reports `Insufficient` (read
// more), never `Identified`.
let bytes = ;
match probe
To probe a real file:
use fs;
let data = read.unwrap;
println!;
How detection works
Every prober is a pure function over the byte slice; all of them always run.
Each returns a Confidence tier and a Detail, and the highest score wins. If
the top two are within TIE_THRESHOLD (16) the result is Ambiguous listing
every candidate, never an arbitrary pick. Insufficient { need_at_least }
means "read more bytes"; Unknown means "stop, more bytes will not help".
Run length alone is not evidence. The test suite guards two real cases where naive magic-byte counting fails:
- A CENC-encrypted MP4 (
fixtures/mp4/cenc.mp4): its high-entropy payload aligned three consecutive0x47bytes on one of 792 TS lattice lanes purely by chance. A run-length-only TS prober called that a match; a candidate lane must now cover at least 50% of its positions with sync bytes — a real TS syncs at ~100%, noise at ~2.5%. - A TS file with 18,239 MP3 syncwords (
fixtures/ts/h264_aac.ts, plus 141 ADTS syncwords and 273 Annex B start codes): raw syncword counting would identify every container as an elementary stream. Each ES prober instead follows each frame's own length field to where the next syncword must be and counts how many chain; a real stream chains 40+, a container chains 0-1. And because ES frames genuinely appear inside container payloads, a container match atLATTICE_STRONGor above zeroes every elementary-stream candidate.
Known gaps
- Annex B detection is H.264 only. HEVC (H.265) is not detected — HEVC
uses a 2-byte NAL header with
nal_unit_typeat bits[6:1], which the H.264 1-byte-header probe does not parse, so an HEVC stream fails at the first NAL. It is deliberately not implemented: this workspace does not implement a format without a real fixture to test it against, and no HEVC Annex B fixture exists in the repo. - 204-byte-stride TS (DVB with Reed–Solomon parity) is covered only by a
marked synthetic fixture —
fixtures/container-probe/PROVENANCE.mdexplains why: no real DVB Reed–Solomon capture exists in this repository. - 208-byte stride has no fixture at all.
.tsis an ambiguous extension — TypeScript declaration files also use it — so a caller must not infer format from a.tsextension; only the probe result is authoritative.
Minimum Supported Rust Version
1.95.0
License
MIT OR Apache-2.0