# WAI Extension: Media Identification > Status: Draft. What a file is, from its bytes alone: the formats they fit, each with the bytes > that say so and how far they were followed; what a registered probe reads of it; signatures > found inside it; and measures that hold when no format is recognised. It decodes nothing, and > what it does not establish it leaves out. The reference implementation is the `identify` module > of `wai-rs` (feature `identify`), which `wai identify` runs (operation `media.inspect`); the > corpus is [`identify-conformance/`](../identify-conformance/), and its second implementation is > `identify-conformance/identify_verify.py`. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. ## 1. Scope §1.1 `wai inspect` reads what an envelope holds. Most files a toolchain meets are not envelopes, and the first question about one is what it is. Identification answers that for any file, and it is the first step of working out how a piece of media was made. §1.2 A result states only what the bytes establish. A claim comes with the offsets of the bytes that make it, and a walk says where it stopped and why. A format is never guessed from a file name, and no confidence figure is given: a result has a strength (§4), which says what was checked. Where the bytes fit no format, the verdict is `unknown` and the measures (§8) are what is known. §1.3 What a container states is not what its samples hold. A track row (§5.1) reports what the file says of a track, and no sample is read to confirm it. No payload is decrypted, and a protected track is reported as protected and no further. §1.4 This extension adds no capability and changes no dispatch (SPEC §4). A capability in a result is the registered capability whose payload a format or track is: it says what `wai wrap` would carry it under, not that a decoder here accepts it. ## 2. The result §2.1 A result is a JSON object: | member | value | |---|---| | `format` | `wai.identify/1` | | `size` | the file's length in bytes | | `verdict` | `identified`, `ambiguous`, `signature_only` or `unknown` (§4.2) | | `candidates` | the formats the bytes fit, in the order of §4.3 | | `probes_accepted` | the capabilities whose probe accepted the bytes (§3.5) | | `embedded` | signatures found inside the file (§7), each `{at, id}` | | `embedded_capped` | whether more were found than `embedded` holds | | `measures` | §8 | §2.2 A candidate is a JSON object: | member | value | |---|---| | `id` | the format, as §5 and §6 name it, or the capability a probe alone accepted (§3.5) | | `kind` | `wai`, `container`, `audio`, `image`, `elementary`, `playlist`, `text`, `compression` or `capture` | | `strength` | `signature`, `structure` or `parameters` (§4.1) | | `evidence` | the first 8 bytes-that-say-so, each `{at, len, is}`: `len` bytes at offset `at`, which read as `is` | | `walked_to` | the offset a walk of the file's structures reached, or `null` where the format has no such walk | | `stopped` | `null`, or `{at, why}`: where a walk stopped before the file's end (§3.3) | | `facts` | what the format's headers state, by the section that defines it | | `tracks` | §5.1 | | `capabilities` | the capabilities it maps to (§1.4), sorted | ## 3. Common rules §3.1 Integers are read as each section says. `u8`, `be16`, `be24`, `be32` and `be64` are big-endian, `le16` and `le32` little-endian. All arithmetic of a result is integer arithmetic, but for the sample rate of §5.4; two implementations give the same result for the same bytes on any machine. §3.2 **What is read.** The **head** is the first 1 MiB of the file (1,048,576 bytes), or all of it if shorter. The walks of §5.5, §5.9, §5.10, §5.13, §5.17, §5.18, §5.19 and §5.20, the table of §6 and the probes read the head alone. The other walks read the file where their lengths lead. The **scan** is the first 16 MiB of the file (16,777,216 bytes), or all of it if shorter: §7 and §8 read it. §3.3 **Walks.** A walk reads a format's structures in order and is bounded: at most 4,096 structures (§5.6: 256), and 64 tracks. A walk that stops before the file's end says why, in `stopped`: | `why` | meaning | |---|---| | `overrun` | a structure declares, or needs, more than the file holds | | `invalid` | a structure's bytes are not what the format allows there | | `crc` | a checksum the format defines does not hold | | `limit` | the walk reached a bound of this extension, or the end of the head where it reads no further | | `unknown_size` | a structure of unknown size, whose end only reading it would find (§5.4) | `walked_to` and a stop's `at` are never past the end of the file: an offset past it is taken as the file's length. A walk that reads the head alone and reaches its end while the file goes on has stopped there, `limit`. §3.4 **Strings.** A string read from the file enters a result only through this filter: its first 64 bytes, each of 0x20 to 0x7E but `\` as itself, and any other byte as `\xNN` in lowercase hexadecimal. A file is untrusted input: a reader **MUST** treat these strings as data, and a tool that gives a result to an agent **MUST** mark it as holding them. §3.5 **Probes.** The probes of the table of [format-probes](format-probes.md) §2 that can name a format from the bytes (`wai.image.jxs`, `wai.video.jxs`, `wai.video.hevc`, `wai.video.mvhevc`, `wai.video.vvc`, `wai.video.lcevc`, `wai.audio.mpegh`, `wai.video.avs3`, `wai.audio.avs3`, in that order) each read the head. The other two read a payload declared to be theirs, have no signature, and say nothing of a file they are merely given, so they are not run. For each probe, in order: - Where it accepts, the capability joins `probes_accepted`. The first candidate of kind `elementary` that maps to that capability (or, for `wai.video.mvhevc`, to `wai.video.hevc`) has its strength raised to `parameters`, the capability added to its `capabilities`, and the probe's result set in its `facts.probe` under the capability. With no such candidate, a candidate of kind `elementary` is added whose `id` is the capability, of strength `parameters`, with evidence `{at: 0, len:
, is: "probe"}`, that capability and the same `facts.probe`. - Where it refuses with `no_signature`, nothing is recorded. - Where it refuses otherwise, and a candidate of kind `elementary` already maps to that capability, its `facts.probe_refused` holds the code under the capability. ## 4. Strength and verdict §4.1 **Strength** says how far a candidate's bytes were followed: - `signature`: a format's fixed bytes are where the format puts them. Nothing else was checked, and short ones occur by chance: `evidence` shows how many bytes it was. - `structure`: what follows the signature is what the format's section requires of it. - `parameters`: a probe of format-probes read the format's headers and accepted them. §4.2 **The verdict** comes from the candidates at the highest strength present: `unknown` if there are no candidates; `ambiguous` if more than one; `signature_only` if one, of strength `signature`; else `identified`. Two formats that both fit are both reported, and no ranking between them is invented. §4.3 **Order.** Candidates are in order of strength, strongest first. Within a strength they keep the order of the sections that define them: §5.2 to §5.20, then §6's table in its order, then those a probe alone added, in the order of §3.5. ## 5. Walks §5.1 **Tracks.** A row of `tracks` holds `track` (the identifier the container gives, or `null`), `media` (`video`, `audio`, `text` or `other`), `stated` (what the container calls it, through §3.4), `codec` (the name of the table below, or `null` where this extension has no name for it), and `width`, `height`, `channels`, `sample_rate` and `protected`, each `null` where not stated. `protected`, on a track the container says is protected, is `{scheme, original}`, each `null` where not stated. A row's codec adds to its candidate's `capabilities` the capability of this table, where it has one: | codec | capability | |---|---| | `avc` | `wai.video.avc` | | `hevc` | `wai.video.hevc` | | `vvc` | `wai.video.vvc` | | `av1` | `wai.video.av1` | | `vp8` | `wai.video.vp8` | | `vp9` | `wai.video.vp9` | | `evc` | `wai.video.evc` | | `lcevc` | `wai.video.lcevc` | | `jpeg-xs` | `wai.video.jxs` | | `jpeg2000` | `wai.video.j2k` | | `aac` | `wai.audio.aac` | | `opus` | `wai.audio.opus` | | `flac` | `wai.audio.flac` | | `ac4` | `wai.audio.ac4` | | `mpegh` | `wai.audio.mpegh` | | `iamf` | `wai.audio.iamf` | The names `mp3`, `vorbis`, `theora`, `speex`, `ac3`, `eac3`, `webvtt` and `ttml` map to none. §5.2 **WAI envelopes** (`wai`, kind `wai`). The head begins `WAI1` or `WAI2`: evidence `magic:WAI1` or `magic:WAI2`, 4 bytes at 0, and `facts.version`. `le32` at 4 is the manifest's length. If it is absent the walk stops at 0 (`overrun`). If it exceeds 1 MiB the walk stops at 8 (`limit`); if the file holds less the walk stops at 8 (`overrun`); if the manifest is not JSON, at 8 (`invalid`). Evidence `manifest`. For `WAI1`: `facts.capability` is `model_requirement.capability`, and `le32` after the manifest is the payload's length, with evidence `payload_length`; where it or the payload does not fit the walk stops there (`overrun`) with `walked_to` there too, else `walked_to` is past the payload. For `WAI2`: `le16` after the manifest is `facts.renditions`, evidence `rendition_count`, and `walked_to` is past it. A walk that reaches this is `structure`. `wai inspect` reads the rest. §5.3 **ISO base media** (`isobmff`, kind `container`). The four bytes at 4 are one of `ftyp`, `styp`, `moov`, `mdat`, `moof`, `free`, `skip`, `wide`, `sidx` or `pnot`. Boxes are walked from 0 to the end of the file. A box: `be32` size and four bytes of type. Every type byte is 0x20 to 0x7E or 0xA9, else `invalid`. Size 0 runs to the end of the file; size 1 means `be64` at 8 is the size, which must be at least 16; any other size below 8 is `invalid`. Fewer bytes than a header needs is `overrun`. Each box whose header reads gives evidence `box: