Every Ed25519 signature WAI defines is verified strictly, in order: a canonical key, a key not of small order, S < L, R not of small order, and the cofactorless equation with R compared byte for byte. OER/2's chain signature keeps the rule oer-receipts §5 settles. jwp-receipts §3.1 step 3, http-delivery's playlist receipt and packaging's receipt cite it instead of restating it; the worlds, avatars, feeds, spatial-audio, haptics, volumetric, score, film, provenance, ternary and quantum receipt texts cite it where they name their signature. CHANGELOG records the sweep. Co-Authored-By: Claude Opus 5.5 <[email protected]>
998 lines
64 KiB
Markdown
998 lines
64 KiB
Markdown
# WAI Extension: Packaging
|
||
|
||
> Status: Draft. A packager turns sources into WAI objects under a declared policy;
|
||
> this extension fixes what a verifier recomputes. This revision defines the policy,
|
||
> its digest and what it may ask a packager to emit (§3), how a source is cut into
|
||
> pieces (§4), the objects a packager writes (§5), the stage index (§6), the floor
|
||
> (§7), what each class is offered over HTTP and MoQ (§8), the package receipt (§9),
|
||
> and the package and its verification (§11). Reference impl: `wai-pack` — `policy`,
|
||
> `capcheck`, `source`, `scenecut`, `classify`, `encode`, `floor`, `cmaf`, `catalog`,
|
||
> `pack`, `verify`; in `wai-rs`, the encoder `int_video_enc`, the stage index
|
||
> `stage_index` and the receipt `package`; the corpus
|
||
> [`pack-conformance/`](../pack-conformance/). Keywords **MUST**, **MUST NOT**,
|
||
> **SHOULD**, **MAY** are RFC 2119/8174.
|
||
|
||
## 1. Scope
|
||
|
||
A **packager** turns one or more sources into WAI objects, stage indexes, catalogs
|
||
and a package receipt, under a declared **policy**. This extension fixes what a
|
||
package contains and what a verifier recomputes. It fixes an encoder's choices only
|
||
where it names them.
|
||
|
||
## 2. Terms
|
||
|
||
- **Component**: one media stream of a package (for example video, or audio).
|
||
- **Piece**: a span of a component that decodes with no reference outside itself.
|
||
- **Stage**: one object of a piece in staged order: the base is layer 0, refinement
|
||
*k* is layer *k* ([staged-delivery](staged-delivery.md)).
|
||
- **Rung**: one entry of the policy's ladder: a capability a component's pieces are
|
||
coded in, with its role (base, refinement or floor) and parameters.
|
||
- **Class**: a delivery profile the policy names.
|
||
- **Moment**: a declared span of presentation time with tags.
|
||
- **Floor**: a rendition in a standard codec, for sinks that lack the primary.
|
||
- **Timebase** of a component: `num / den` units per second. For video one unit is
|
||
one frame; for audio, one sample.
|
||
|
||
## 3. Policy
|
||
|
||
**3.1** A package is made under exactly one policy, a TOML 1.0 document of schema
|
||
`wai.pack.policy/1`, in UTF-8 with no byte-order mark.
|
||
|
||
**3.2** The **effective policy** is the policy with every default written in (§3.3)
|
||
and every override the packager applies (such as a command line forcing
|
||
`royalty_free_only`). An override only ever forces `royalty_free_only` on; it never
|
||
turns off what the policy asks for. Its JSON form is §3.3's members, with an optional member that
|
||
has no default omitted when absent, and arrays of tables in document order. Its
|
||
**policy digest** is
|
||
|
||
BLAKE3("wai:pack-policy\x01" ‖ JCS(effective policy as JSON))
|
||
|
||
where JCS is RFC 8785. Two spellings of one policy (member order, inline or standard
|
||
tables, dotted keys, string or integer notation, comments, an omitted default or the
|
||
default written out) therefore have one digest; the order of an array of tables is
|
||
part of the policy.
|
||
|
||
A packager **MUST** refuse with `policy-toml`, before anything else, a policy whose
|
||
bytes are not UTF-8, that begins with a byte-order mark, that is not TOML 1.0 (an
|
||
integer outside TOML's 64-bit range included), or that nests arrays and tables more
|
||
than 32 deep: the document's own table is at depth 1, and an array or a table
|
||
directly inside one at depth *k* is at depth *k* + 1. Then, before it reads any key, it
|
||
**MUST** refuse one holding anywhere a float (`policy-float`), a date or time
|
||
(`policy-datetime`), a negative integer (`policy-negative`), or an integer above
|
||
`2^53 − 1` (`policy-integer-range`), visiting members in byte order of their keys and
|
||
arrays in order.
|
||
|
||
**3.3** *Schema.* Within each table, a key this section does not define is refused
|
||
first (`policy-unknown-key`, the first in byte order), then each member in the order
|
||
listed: a required member that is absent (`policy-missing-key`), a member of another
|
||
type (`policy-type`), a value outside its range or vocabulary (`policy-value`). An
|
||
absent table reads as empty.
|
||
|
||
| Member | Type | Default | Range |
|
||
|---|---|---|---|
|
||
| `schema` | string | required | `wai.pack.policy/1` (else `policy-schema`) |
|
||
| `asset_id` | string | required | 1 to 255 bytes |
|
||
| `royalty_free_only` | boolean | `false` | |
|
||
| `lead` | string | required | 1 to 255 bytes |
|
||
| `component` | array of tables | required | |
|
||
| `component.id` | string | required | 1 to 255 bytes |
|
||
| `component.media` | string | required | `video`, `image`, `audio`, `world`, `feed`, `avatar`, `film` |
|
||
| `component.source` | string | absent | |
|
||
| `component.timebase` | table | required | members `num`, `den`, each 1 to `2^32 − 1`, required |
|
||
| `segmentation.target_ms` | integer | 2000 | |
|
||
| `segmentation.min_ms` | integer | 1000 | |
|
||
| `segmentation.max_ms` | integer | 4000 | |
|
||
| `segmentation.scene_cut` | string | `wai.pack.scenecut/1` | `wai.pack.scenecut/1` |
|
||
| `segmentation.cut_abs_permille` | integer | 300 | 1 to 1000 |
|
||
| `segmentation.cut_rel_permille` | integer | 3000 | 0 to 1000000 |
|
||
| `segmentation.cut_window` | integer | 8 | 3 to 64 (a window of fewer than 3 scores never applies `cut_rel_permille`, §4.3) |
|
||
| `segmentation.lookahead_frames` | integer | 6 | 0 to 64 |
|
||
| `segmentation.closed` | boolean | `true` | |
|
||
| `ladder` | array of tables | empty | |
|
||
| `ladder.id` | string | required | 1 to 255 bytes |
|
||
| `ladder.component` | string | required | 1 to 255 bytes |
|
||
| `ladder.capability` | string | required | |
|
||
| `ladder.role` | string | required | `base`, `refinement`, `floor` |
|
||
| `ladder.refines` | string | absent | |
|
||
| `ladder.params` | table | empty | by capability, below |
|
||
| `floor.supplied_listing` | string | absent | 64 lowercase hexadecimal digits |
|
||
| `floor.extra_keyframes` | boolean | `false` | |
|
||
| `live.layer_lag_ms` | array of integers | one 0 per layer | §3.4 |
|
||
| `class` | array of tables | empty | |
|
||
| `class.id` | string | required | 1 to 255 bytes |
|
||
| `class.max_layer` | integer | 0 | 0 to 255 |
|
||
| `class.max_kbps` | integer | absent | 1 to `2^32 − 1` |
|
||
| `class.floor_only` | boolean | `false` | |
|
||
| `moment` | array of tables | empty | |
|
||
| `moment.id` | string | required | 1 to 255 bytes |
|
||
| `moment.from_ms`, `moment.to_ms` | integer | required | `to_ms` above `from_ms` |
|
||
| `moment.tags` | array of strings | empty | |
|
||
| `rule` | array of tables | empty | |
|
||
| `rule.id` | string | required | 1 to 255 bytes |
|
||
| `rule.when.piece_kind` | array of strings | absent | `natural`, `static`, `synthetic`, `motion_high` |
|
||
| `rule.when.moment_tag` | array of strings | absent | |
|
||
| `rule.then.ladder` | array of strings | absent | |
|
||
| `rule.then.max_layer` | integer | absent | 0 to 255 |
|
||
| `rule.then.deadline_ms` | integer | absent | 1 to `2^32 − 1` |
|
||
| `defaults.deadline_ms` | integer | 1500 | 1 to `2^32 − 1` |
|
||
| `classify.static_permille` | integer | 5 | 0 to 1000 |
|
||
| `classify.static_mad_milli` | integer | 300 | |
|
||
| `classify.synthetic_bins` | integer | 12 | 0 to 64 |
|
||
| `classify.motion_high_milli` | integer | 9000 | |
|
||
|
||
A rung's `params` are those of its capability; every other capability takes none.
|
||
|
||
| Capability | Parameter | Default | Range |
|
||
|---|---|---|---|
|
||
| `wai.video.int_motion` | `block` | 16 | 4, 8, 16, 32 |
|
||
| | `search` | 7 | 0 to 32 |
|
||
| | `mv_precision` | 2 | 1, 2 |
|
||
| | `q_candidates` | `[8]` | a non-empty array, each 1 to 255 |
|
||
| | `max_sse_px_milli` | 0 | |
|
||
| `wai.video.int_refine` | `q` | required | 1 to 255 |
|
||
| `wai.video.av1`, `wai.video.av1.lossless` | `source` | `supplied` | `supplied`, `encode` |
|
||
| `wai.audio.int_refine` | `to_rate`, `q` | required | 1 to 384000; 1 to 65535 |
|
||
| `wai.audio.flac` | `source` | `encode` | `encode`, `supplied` |
|
||
|
||
`wai.audio.int_layered` is reserved for the layered integer audio base: it is not
|
||
registered yet, and its parameters are defined when it is.
|
||
|
||
**3.4** *Constraints.* A policy that passes §3.3 is checked in this order, and the
|
||
first that fails is the refusal:
|
||
|
||
1. 1 to 8 components (`policy-too-many-components`); at most 64 rungs
|
||
(`policy-too-many-rungs`), 32 classes (`policy-too-many-classes`), 32 rules
|
||
(`policy-too-many-rules`) and 32 moments (`policy-too-many-moments`).
|
||
2. Ids unique among components, rungs, classes, moments and rules taken together, so
|
||
a reference never names two things (`policy-duplicate-id`).
|
||
3. `lead` names a component (`policy-lead`).
|
||
4. `segmentation.closed` is true: every piece is closed, §4.1 (`policy-open-gop`).
|
||
5. `1 ≤ min_ms ≤ target_ms ≤ max_ms` (`policy-piece-bounds`).
|
||
6. Every rung names a component (`policy-ladder-component`).
|
||
7. A rung carries `refines` exactly when its role is `refinement`
|
||
(`policy-refines-role`); `refines` names a rung (`policy-dangling-refines`); no
|
||
chain of `refines` returns to a rung it passed (`policy-refines-cycle`); every
|
||
chain stays in its component and ends at a base (`policy-refines-base`); no rung is
|
||
refined twice (`policy-refines-branch`).
|
||
8. Every component has a base rung (`policy-no-base`).
|
||
9. A rung's **layer** is 0 for a base or a floor, and one more than the rung it
|
||
refines for a refinement; the policy has one more layer than its highest rung
|
||
layer. No class's and no rule's `max_layer` exceeds the highest
|
||
(`policy-class-layer`).
|
||
10. A rule's `then.ladder` names known rungs, all of one component, at least one of
|
||
them a base (`policy-rule-ladder`).
|
||
11. `live.layer_lag_ms` has one entry per layer, the first 0, the entries
|
||
non-decreasing (`policy-layer-lag`).
|
||
12. The deadline values (`defaults.deadline_ms` and every rule's `then.deadline_ms`)
|
||
number at most 4 (`policy-too-many-deadlines`), and for every refinement layer
|
||
*k* and every deadline value *d*, `d − layer_lag_ms[k] ≥ 1`
|
||
(`policy-deadline-unreachable`).
|
||
|
||
Rules fold in document order: every matching rule applies, and a later `then` member
|
||
overrides an earlier one.
|
||
|
||
**3.5** A package receipt carries the policy digest. A verifier holding the effective
|
||
policy recomputes it; it cannot check that the packager followed the policy, except
|
||
by packing again.
|
||
|
||
**3.6** *What a packager may emit.* A packager **MUST** refuse a policy with a rung it
|
||
may not emit, reading SPEC §5's registry, never the policy. For each rung in ladder
|
||
order the first rule that holds is its refusal, and a policy's refusal is its first
|
||
rung's:
|
||
|
||
1. SPEC §5 does not register the capability: `capability-unknown:<cap>`.
|
||
2. SPEC §5 registers it as a record or a derivation operation, which names no payload
|
||
format: `capability-not-payload:<cap>`.
|
||
3. It is a candidate, whose payload format no revision has pinned (SPEC §5,
|
||
*Candidate capabilities*): `capability-candidate:<cap>`.
|
||
4. A base or a floor whose capability is not standalone media, or a refinement whose
|
||
capability is not refinement-only: `capability-role:<cap>`.
|
||
5. Under `royalty_free_only`, a capability the registry gives no licence class:
|
||
`license-unclassified:<cap>`; one of any class other than royalty-free:
|
||
`license-refused:<cap>:<label>`, with the class's SPEC §5 label. The class is the
|
||
registry's, "licensed royalty-free by its standards body"; it is not an opinion on
|
||
freedom to operate, and a package states nothing about patents.
|
||
|
||
## 4. Pieces
|
||
|
||
**4.1** Every piece **MUST** be closed: its first frame, sample or tick is coded with
|
||
no reference outside the piece, and no later object of the package references into
|
||
it.
|
||
|
||
**4.2** Piece boundaries are a pure function of the source frames and the effective
|
||
policy. Where the policy names `wai.pack.scenecut/1`, the boundaries **MUST** be those
|
||
§4.3 computes. The list of piece starts, in timebase units, is the **plan**, and its
|
||
digest is
|
||
|
||
BLAKE3("wai:pack-plan\x01" ‖ u64 LE n ‖ n × u64 LE start)
|
||
|
||
**4.3** `wai.pack.scenecut/1`. All arithmetic is integer; `>>` is a shift and every
|
||
division rounds down. For each RGB8 frame *f* of `w × h` pixels:
|
||
|
||
1. Luma `Y = (77R + 150G + 29B + 128) >> 8`, and the histogram `H_f` of 64 bins,
|
||
`Y >> 2`.
|
||
2. `D(a, b) = (Σ_i |H_a[i] − H_b[i]|) · 1000 / (2wh)`: the share of pixels, in
|
||
thousandths, whose bin differs. The score of frame *f* ≥ 1 is `s_f = D(f, f − 1)`.
|
||
3. The current piece starts at frame *p*, and `len = f − p`. Its window `W_f` is the
|
||
scores `s_g` for `p < g < f`, the last `cut_window` of them; `m_f` is their lower
|
||
median (sorted ascending, the element at index `(|W_f| − 1) / 2`).
|
||
4. **Hard cut** at *f*: `len ≥ min`, `s_f ≥ cut_abs`, and either `|W_f| < 3` or
|
||
`s_f · 1000 ≥ cut_rel · m_f`.
|
||
5. **Soft cut** at *f*: `len ≥ target` and `2 · s_f ≥ cut_abs`.
|
||
6. **Return clause.** A cut at *f* is rejected when `D(f + k, f − 1) < cut_abs` for
|
||
some *k* in `1..=min(L, last − f)`: the content came back within *L* frames, so *f*
|
||
starts a flash.
|
||
7. **Revisit clause.** A cut at *f* is rejected when `D(f, f − 1 − j) < cut_abs` for
|
||
some *j* in `1..=min(L, f − 1)`: *f* returns to content seen within *L* frames, so it
|
||
ends a flash. Frames before *p* count.
|
||
8. A hard or soft cut that clauses 6 and 7 do not reject starts a piece at *f*.
|
||
Otherwise, when `len ≥ max`, a piece starts at *f*.
|
||
|
||
Frame 0 starts the first piece. *L* is `lookahead_frames`, *last* the index of the
|
||
source's last frame, `cut_abs` and `cut_rel` the policy's permille values, and `min`,
|
||
`target` and `max` the policy's milliseconds on the component's timebase (§4.4). A
|
||
decision about frame *f* reads no frame after `f + L`, so a live segmenter decides it
|
||
once frame `f + L` has arrived, and a live and an on-demand segmenter with equal *L*
|
||
produce equal boundaries. A piece that follows a cut is flagged a scene cut; one
|
||
started at `max`, forced.
|
||
|
||
**4.4** A piece's duration is an integer number of timebase units, and lies between
|
||
`min` and `max` except the last piece of a source, which may be shorter than `min`.
|
||
A duration in milliseconds converts to timebase units as
|
||
`floor(ms · num / (1000 · den))`; a result of 0 becomes 1, and one above `2^64 − 1`
|
||
becomes `2^64 − 1`.
|
||
|
||
**4.5** *Components.* Every component of a package shares one presentation timeline
|
||
that starts at 0. The policy names one **lead** component; every other component cuts
|
||
its pieces at the lead's boundary instants, each converted to its own timebase as
|
||
`floor(start_lead · num_c · den_lead / (den_c · num_lead))` (above `2^64 − 1`,
|
||
`2^64 − 1`). A sink presents piece *p* of every component from that start.
|
||
|
||
**4.6** *Classification.* Each video piece has a kind, which rules match
|
||
(`rule.when.piece_kind`). Over the piece's consecutive frame pairs, with `Y` the luma
|
||
of §4.3:
|
||
|
||
- `mad_milli = Σ |Y_f − Y_{f−1}| · 1000 / (wh · (n − 1))`, and 0 when the piece has
|
||
one frame (*n* = 1);
|
||
- `changed_permille` = the pixels whose luma differs, `· 1000 / (wh · (n − 1))`, and 0
|
||
when *n* = 1;
|
||
- `occupancy` = the non-empty histogram bins of the piece's first frame.
|
||
|
||
The kind is the first that holds of: **static** (`mad_milli ≤ static_mad_milli` and
|
||
`changed_permille ≤ static_permille`); **synthetic** (`occupancy ≤ synthetic_bins`);
|
||
**motion_high** (`mad_milli ≥ motion_high_milli`); **natural**.
|
||
|
||
**4.7** *Sources.*
|
||
|
||
- **Y4M**: 8-bit 4:2:0 (`C420jpeg`, `C420paldv`, `C420mpeg2`, `C420`, or no `C`
|
||
parameter) or 4:4:4 (`C444`), progressive. A header that is not `YUV4MPEG2` with a
|
||
width and a height is refused `y4m-header`; an interlaced stream or frame,
|
||
`y4m-interlaced`; another colour space, `y4m-colorspace`; a `FRAME` line that sets
|
||
the width, height or colour space, `y4m-size-change`; a frame that ends early or a
|
||
line that is not `FRAME`, `y4m-truncated`. Each pixel converts to RGB8 by BT.709
|
||
limited range, with 4:2:0 chroma taken at `(y >> 1, x >> 1)`:
|
||
- `R = clip((298(Y−16) + 459(Cr−128) + 128) >> 8)`
|
||
- `G = clip((298(Y−16) − 55(Cb−128) − 136(Cr−128) + 128) >> 8)`
|
||
- `B = clip((298(Y−16) + 541(Cb−128) + 128) >> 8)`
|
||
|
||
where `>>` is the arithmetic shift and `clip` clamps to 0..=255.
|
||
- **WAV**: RIFF/WAVE with a PCM `fmt ` chunk (format tag 1) of 16-bit samples, block
|
||
alignment `2 · channels`. Anything else is refused `wav-format`; a chunk that ends
|
||
early, `wav-truncated`.
|
||
|
||
## 5. Objects
|
||
|
||
**5.1** *One envelope.* Each object is exactly one `WAI1` or `WAI2` envelope and
|
||
nothing after it ([http-delivery](http-delivery.md) §5.1).
|
||
|
||
**5.2** *Position independence.* An object's bytes **MUST NOT** depend on its position
|
||
in a package or on the package it belongs to: no member of its manifest names a piece,
|
||
a time, an asset, a class or a component, so two pieces with identical content are one
|
||
object. The reference packager writes a base's manifest as SPEC §3 orders it, with
|
||
`media` the component's media, `intent` `replicate`, `model_requirement` the rung's
|
||
capability with no `fallback` member, `conditioning.kind` the capability's last
|
||
dot-separated segment (`int_motion`, `av1`), and `target` `{"frames": n, "h": h, "w": w}`
|
||
(the piece's frame count and dimensions; for a floor, its sequence header's maximum
|
||
frame height and width); a floor's `derived_from` (§7.2) comes after `conditioning`.
|
||
|
||
**5.3** *Name.* An object is named by its content hash ([jwp-receipts](jwp-receipts.md)
|
||
§2.1), `BLAKE3("moq-jwp:object\x01" ‖ envelope)`, as `objects/<64 lowercase hex>.wai`:
|
||
the same value is its stage-index entry (§6), its receipt leaf, its MoQ content hash and
|
||
its HTTP strong `ETag` ([http-delivery](http-delivery.md) §5.2).
|
||
|
||
**5.4** *Refinements.* A refinement object carries `refines`
|
||
([staged-delivery](staged-delivery.md) §3): `base` the content hash of its piece's base,
|
||
`layer` its layer, and as producers in `prev` only objects of the layers below it, the
|
||
object its entry refines (§6.2) among them. The reference packager does not write
|
||
refinements yet.
|
||
|
||
**5.5** *Integer video.* An object of `wai.video.int_motion` carries a `WIV1` payload
|
||
with keyframe 0, as SPEC §5 pins it ([integer-payloads](integer-payloads.md) §3); this
|
||
extension defines no other layout for it, and its decode is that section's. The
|
||
reference packager writes these with one encoder (`wai-rs` `int_video_enc`) at the step
|
||
§5.6 chooses. The encoder's choices
|
||
make a payload a function of the source frames, the rung's `block`, `search` and
|
||
`mv_precision` (§3.3), and the step `q`:
|
||
|
||
- **Motion search** (frames 1 on): blocks in raster order; for each, every vector
|
||
`(dy, dx)` with both components in `−R..=R`, `R = search · mv_precision` (at most
|
||
127), `dy` outer and `dx` inner, each ascending. Its cost is the sum, over the
|
||
block's pixels inside the frame and the three channels, of `|source − prediction|`,
|
||
where the prediction is the decode's (integer-payloads §3) from the previous frame
|
||
as decoded. The least cost wins; a tie goes to the smaller `|dy| + |dx|`, then to the
|
||
earlier vector.
|
||
- **Residual**: each frame, each pixel in raster order, each of R, G, B: the
|
||
prediction (128 for frame 0) is subtracted from the source and divided by `q`,
|
||
rounding halves away from zero; the frame as decoded is `clamp(prediction + r·q, 0,
|
||
255)`, and the next frame predicts from it.
|
||
- **Table**: one residual table for the clip, with `offset` the least residual, `top`
|
||
the greatest, and `h_i` the count of residual `offset + i`, for
|
||
`n = min(top − offset + 1, 4095)` symbols. With `R = 2^16 − n − 1` and `H = Σ h_i`, symbol *i* gets frequency
|
||
`1 + ⌊R·h_i / H⌋`; the `R − Σ⌊R·h_i / H⌋` left over go one each to the symbols with
|
||
the largest `R·h_i mod H`, the lower symbol first on a tie; the escape gets 1. A
|
||
residual past the table's span is coded through the bypass escape
|
||
(integer-payloads §2).
|
||
- A clip of one frame is written with `block` 1 and `mv_precision` 1, as the format
|
||
requires.
|
||
|
||
The encoder also states the SSE of its reconstruction against the source: the sum of
|
||
squared differences over every sample. It codes in two passes (the first finds the
|
||
vectors, the reconstruction and the residuals' counts; the second derives each
|
||
residual again and codes it last first), so beyond its input it holds the
|
||
reconstruction, the vectors, the stream and the residual table: at most
|
||
`n·h·w·3 + 24n + 12·v + 3·(2·n·h·w·3 + 16) + T` bytes for `n` frames and `v` motion
|
||
vectors (`int_video_enc::working_bytes`). `T` is the widest table's, of `S = 2·255 + 1`
|
||
symbols: its fit, `16·(S + 1) + 2·8·S + 4·(S + 2)` bytes (a frequency for each symbol
|
||
and the escape, an index for each symbol and the sort's scratch, the table), and the
|
||
table's three copies and the payload's header, `3·4·(S + 2) + 64`. For a clip past the
|
||
format's caps, which the encoder refuses before it allocates, the figure is `2^64 − 1`.
|
||
|
||
**5.6** *Choosing the step.* For each piece, the base encode of a `wai.video.int_motion`
|
||
rung chooses its step from `q_candidates`, read as a set (a value listed twice is one
|
||
candidate). It encodes the piece at each candidate, from the largest down, and chooses
|
||
the first whose `floor(SSE · 1000 / (3·w·h·n)) ≤ max_sse_px_milli`, where `SSE` is the
|
||
encoder's (§5.5) and *n* the piece's frames: the largest step that meets the bound, so
|
||
candidates that meet it alike are decided by their step. Where no candidate meets it,
|
||
it chooses the smallest. The function the reference packager's base encode calls is
|
||
`wai_pack::encode::choose_q`.
|
||
|
||
## 6. Stage index
|
||
|
||
**6.1** A package carries, per component, one stage index, the file `index-<id>.wsix`
|
||
(`WSIX`), per window: one window for on-demand content, a chain of windows for live
|
||
(§9.2). A window holds whole pieces. Integers are little-endian.
|
||
|
||
**6.2** *Layout.* A 160-byte header, then `n_pieces` piece records of 32 bytes,
|
||
`n_entries` entry records of 128 bytes, `n_classes` class records of 16 bytes, the class
|
||
plan, and the string table. Nothing follows the string table. Reserved and padding
|
||
bytes are zero.
|
||
|
||
| Offset | Size | Header field |
|
||
|---|---|---|
|
||
| 0 | 4 | `"WSIX"` |
|
||
| 4 | 2 | version, 1 |
|
||
| 6 | 2 | `media_class`: 0 video, 1 image, 2 audio, 3 world, 4 feed, 5 avatar, 6 film |
|
||
| 8 | 4 | `tb_num` |
|
||
| 12 | 4 | `tb_den` |
|
||
| 16 | 4 | `n_pieces` |
|
||
| 20 | 4 | `n_entries` |
|
||
| 24 | 4 | `n_strings` |
|
||
| 28 | 4 | `n_classes` |
|
||
| 32 | 32 | `policy_digest` (§3.2) |
|
||
| 64 | 32 | `prev_index_digest`: the previous window's index digest; zero for on-demand content and a first window |
|
||
| 96 | 8 | `first_piece`: the absolute number of the window's first piece |
|
||
| 104 | 8 | `first_entry`: the absolute ordinal of the window's first entry across the chain |
|
||
| 112 | 32 | `plan_digest` (§4.2) of the pieces' starts |
|
||
| 144 | 2 | `component`: the component's id, as a string index |
|
||
| 146 | 14 | reserved |
|
||
|
||
| Offset | Size | Piece record field |
|
||
|---|---|---|
|
||
| 0 | 8 | `start`, in timebase units |
|
||
| 8 | 4 | `duration`, in timebase units |
|
||
| 12 | 4 | `first_entry`, window-relative |
|
||
| 16 | 2 | `n_entries` |
|
||
| 18 | 1 | `kind`: 0 natural, 1 static, 2 synthetic, 3 motion_high (§4.6), 4 world segment, 5 audio |
|
||
| 19 | 1 | flags: bit 0 scene cut, bit 1 forced at `max` (§4.3), bit 2 started by a cue, bit 3 aligned to the lead (§4.5) |
|
||
| 20 | 4 | `rules`: bit *i* set when rule *i* matched the piece (§8.1) |
|
||
| 24 | 4 | `moments`: bit *i* set when the piece lies in moment *i* (§8.1) |
|
||
| 28 | 4 | reserved |
|
||
|
||
| Offset | Size | Entry record field |
|
||
|---|---|---|
|
||
| 0 | 32 | `content_hash` (§5.3) |
|
||
| 32 | 8 | `byte_len`: the whole envelope's length |
|
||
| 40 | 8 | `piece`: the absolute piece number |
|
||
| 48 | 4 | `refines`: the window-relative index of the entry this one refines; `0xFFFFFFFF` for none |
|
||
| 52 | 2 | `capability`, as a string index |
|
||
| 54 | 1 | `role`: 0 base, 1 refinement, 2 floor, 3 snapshot, 4 ops, 5 receipt, 6 asset |
|
||
| 55 | 1 | `layer` |
|
||
| 56 | 1 | `prefix_tier`: 0 none, 1 entropy-consistency, 2 decode-equivalence (§6.4) |
|
||
| 57 | 1 | flags: bit 0 deferrable, bit 1 duplicate (an earlier entry of the index has the content hash), bit 2 the manifest carries `derived_from` |
|
||
| 58 | 2 | reserved |
|
||
| 60 | 4 | `deadline_ms`: 0 for none (§8.6) |
|
||
| 64 | 32 | `prefix_digest` (§6.5) |
|
||
| 96 | 8 | `sse` (§6.6); `2^64 − 1` when not computed |
|
||
| 104 | 4 | `manifest_len`: the envelope's `man_len` |
|
||
| 108 | 4 | reserved |
|
||
| 112 | 8 | `payload_len`: the envelope's payload bytes (for a `WAI2`, every component's) |
|
||
| 120 | 8 | reserved |
|
||
|
||
`byte_len`, `manifest_len` and `payload_len` are the envelope's own lengths; for an
|
||
object whose payload format WAI checks they are its counted bytes
|
||
([staged-measurement](staged-measurement.md) §3), so a verifier also requires each such
|
||
payload to parse as its format, after the other object rules of §6.3
|
||
(`object-lengths`).
|
||
|
||
A class record is `u16` name (a string index), `u8 max_layer`, `u8` flags (bit 0
|
||
floor only), `u32 max_kbps` (0 for none) and 8 reserved bytes, in the policy's class
|
||
order. The class plan is `n_classes` rows of `n_pieces` bytes, each the highest layer
|
||
the class lists for the piece or `0xFF` for the floor only (§8.1), zero-padded to a
|
||
multiple of 8. The string table is `n_strings` strings, each a `u16` length and that
|
||
many bytes of UTF-8, at least one, zero-padded to a multiple of 8. It is the component's
|
||
id, then the class ids in policy order, then each capability at its first use, and a
|
||
package's verifier holds an index to that (§11.3).
|
||
|
||
index_digest = BLAKE3("wai:pack-stage-index\x01" ‖ file)
|
||
leaf_i = BLAKE3("wai:pack-stage\x01" ‖ entry record i)
|
||
stage_root = the jwp-receipts §2.2 Merkle root over the leaves
|
||
|
||
A reader **MUST** refuse a file that is not this layout, the first that applies, in
|
||
this order:
|
||
|
||
1. its first four bytes are not `WSIX`, a file of fewer included (`index-magic`);
|
||
2. a file of fewer than six bytes (`index-truncated`), or a version other than 1
|
||
(`index-version`);
|
||
3. a file shorter than the header, or than the tables its counts give: the piece, entry
|
||
and class records, the class plan with its padding, each string's length and bytes,
|
||
and the string table's padding, every size taken from its count before anything is
|
||
allocated (`index-truncated`);
|
||
4. a reserved or padding byte that is not zero (`index-reserved`);
|
||
5. a media class with no meaning (`index-media-class`); then, entry by entry, a role,
|
||
then a tier, with none (`index-role`, `index-tier`);
|
||
6. a string that is empty or not UTF-8, then a string index past the table
|
||
(`index-string`);
|
||
7. bytes after the string table's padding (`index-trailing`).
|
||
|
||
**6.3** *Rules.* An index lists every WAI object of its component, in piece order.
|
||
Files that are not WAI envelopes (CMAF initialization segments and fragments, FLAC
|
||
files) **MUST NOT** appear in it. A verifier **MUST** refuse an index that breaks one
|
||
of these, checked in this order:
|
||
|
||
1. A timebase of zero (`index-timebase`). An index with no previous window
|
||
(`prev_index_digest` zero: on-demand content, or a chain's first window) whose
|
||
`first_piece` or `first_entry` is not 0, or whose first piece does not start at 0, the
|
||
start of the presentation timeline (§4.5) (`window-start`). Then piece by piece: a
|
||
piece that lasts no time or does not start where the one before it ended
|
||
(`piece-order`); whose
|
||
`first_entry` is not the number of entries before it, or that has none
|
||
(`piece-entries`); of a kind or with a flag that has no meaning (`piece-kind`). Then
|
||
pieces whose entries are not the index's entries (`piece-entries`).
|
||
2. Entry by entry, in order: an entry that does not name its piece's absolute number
|
||
(`piece-entries`); a flag with no meaning (`entry-flags`); within its piece, a role
|
||
before one that comes earlier in the order base, refinement, floor, any other, or a
|
||
refinement at a lower layer than the refinement before it (`entry-order`); an entry
|
||
that is not a refinement above layer 0, a refinement at layer 0, or `refines` on an
|
||
entry that is not a refinement or missing from one (`layer-role`); a
|
||
refinement whose `refines` is outside the window (`refines-outside-window`), is not
|
||
an earlier entry of its own piece (`refines-mismatch`) or is not one layer below it
|
||
(`layer-gap`); its tier (§6.4: `tier-entropy-passthrough`, `tier-weakest`, then
|
||
`tier-mismatch`); a non-zero `prefix_digest` with a tier other than
|
||
decode-equivalence (`digest-without-tier`); a deadline on an entry that is not a
|
||
refinement (`deadline-role`); a floor without flag bit 2 (`floor-derived-from`); a
|
||
content hash an earlier entry has at another length (`dup-length`); the duplicate
|
||
flag other than set exactly when an earlier entry has the content hash (`dup-flag`).
|
||
3. Class by class: a flag with no meaning (`class-flags`); a plan value other than
|
||
`0xFF` for a floor-only class, or other than `0xFF` or at most both the class's
|
||
`max_layer` and the piece's highest layer otherwise (`class-plan`).
|
||
|
||
With the objects in hand, a verifier **MUST** also refuse, entry by entry: bytes of
|
||
another length (`object-length`) or that do not hash to the entry's content hash
|
||
(`object-hash`); bytes that are not one `WAI1` or `WAI2` envelope and nothing else
|
||
(`non-envelope-entry`); a `man_len` or a payload length other than the entry's
|
||
(`object-lengths`); a capability other than the entry's (`object-capability`; for a
|
||
`WAI2`, its first entry's); a refinement whose object is not a `WAI1` whose `refines`
|
||
names its piece's base, its layer and producers of the layers below among which the
|
||
entry it refines (§5.4, `object-refines`); and `derived_from` (SPEC §7.1) present
|
||
otherwise than bit 2 says, or not well formed (`object-derived-from`). A member whose
|
||
value is `null` is absent; for a `WAI2`, `derived_from` is present when any entry
|
||
carries it, and well formed when every entry that carries it is. A manifest that does
|
||
not parse, or names a member twice, is not an envelope (`non-envelope-entry`).
|
||
|
||
**6.4** *Tier.* The tier a capability's own decode holds is decode-equivalence for a
|
||
capability whose registry class reconstructs exactly (`StandardDefined`, and
|
||
`NeuralIntegerExact`, whose pin a sink verifies before it decodes),
|
||
entropy-consistency for `NeuralEntropyExact`, and none otherwise (SPEC §5, §7).
|
||
|
||
- An entry at layer 0 states its capability's tier, except a floor, which states none
|
||
(§7.4).
|
||
- A prefix with one or more refinements holds decode-equivalence only if every object
|
||
along it does, and none otherwise ([derived-tracks](derived-tracks.md) §3, rules 1
|
||
and 2): a refinement's tier is decode-equivalence exactly when the entry it refines
|
||
states decode-equivalence and its own capability's tier is decode-equivalence.
|
||
- An entry with `layer` > 0 **MUST NOT** state entropy-consistency
|
||
(`tier-entropy-passthrough`); one with `layer` > 0 that states decode-equivalence over
|
||
a prefix that does not hold it is refused `tier-weakest`; any entry stating another
|
||
tier than the one this section gives, `tier-mismatch`.
|
||
|
||
**6.5** `prefix_digest` is the SHA-256 decoded-output digest (SPEC §7, *Digests*) of the
|
||
prefix the entry completes when `prefix_tier` is decode-equivalence, and all zero
|
||
otherwise: for video, the clip digest over RGB8 frames; for an image, the RGB8 frame
|
||
digest; for audio, the digest of the s16le interleaved samples; for world, feed, avatar
|
||
and film entries, zero (their state hashes live in their receipts). A verifier decodes
|
||
the prefix and compares.
|
||
|
||
**6.6** `sse` is the integer sum, over every output sample of the prefix, of the
|
||
squared difference from the source sample the packager coded (RGB8 channels, or s16
|
||
samples). It is the producer's statement: a verifier holding the source recomputes it,
|
||
and one without the source cannot. A floor's `sse` is `2^64 − 1`.
|
||
|
||
## 7. Floor and `derived_from`
|
||
|
||
**7.1** Every piece of a video, image or audio component **MUST** have a floor rendition
|
||
in a capability of SPEC §5's mandatory floor or recommended set; a packager refuses a
|
||
video component with no floor rung (`floor-missing`). A film or world package SHOULD
|
||
have one. This revision packs a supplied AV1 floor (`wai.video.av1` or
|
||
`wai.video.av1.lossless`, `source = "supplied"`).
|
||
|
||
**7.2** *Floor objects.* Each piece's floor is one object of the floor rung's capability
|
||
(§5.2), whose manifest carries `derived_from` (SPEC §7.1, *Derived renditions*) naming
|
||
the piece's base: `motif` the base's content hash, `motif_capability` its capability,
|
||
`renderer` the floor's maker, and `settings` `BLAKE3("wai:render-settings\x01" ‖
|
||
JCS(settings))` (§3.6). For a supplied floor, `renderer` is `supplied@<the supplied
|
||
listing's SHA-256>` and the settings are `{"source": "supplied", "supplied_listing":
|
||
"<that SHA-256>"}`, so neither names a position. This profile fixes these members: they
|
||
are not the producer's statement here (SPEC §7.1), and a verifier holds every floor
|
||
object to them, to its framing header and to its target (§11.3). An AV1 floor object's payload is the
|
||
length-prefixed framing the reference sink decodes (`wai-rs` `codecs::video`): `n`,
|
||
`h`, `w`, `fps_num`, `fps_den` as `u32` little-endian (the piece's temporal units, the
|
||
sequence header's maximum frame height and width, the component's timebase `num` and
|
||
`den`), then each temporal unit as a `u32` little-endian length and its bytes as the
|
||
stream holds them.
|
||
|
||
**7.3** *A supplied floor* is a directory holding, per video component, `<id>.obu`: an
|
||
AV1 stream in the low-overhead OBU format (every OBU with its size field), one temporal
|
||
unit per source frame. Its listing (SPEC §3.1) of every file is pinned by the policy's
|
||
`floor.supplied_listing`. A packager **MUST** refuse, before it writes anything:
|
||
|
||
- a directory whose listing does not hash to `floor.supplied_listing`
|
||
(`floor-listing-mismatch`), and a component with no stream in it
|
||
(`floor-stream-missing`);
|
||
- a stream that does not split into temporal units each holding a frame, whose first
|
||
unit holds no sequence header, with a sequence header whose bytes differ from the
|
||
first's, or with a unit that starts at a shown key frame (below) without a sequence
|
||
header (`floor-av1-malformed`);
|
||
- another number of temporal units than source frames (`floor-frame-count`);
|
||
- a unit that does not start at a shown key frame at a piece's first frame, or one that
|
||
does elsewhere unless `floor.extra_keyframes` is true (`floor-keyframe-mismatch`). A
|
||
unit starts at a shown key frame when its first frame header has `frame_type`
|
||
`KEY_FRAME` and `show_frame` 1, or the sequence uses the reduced still-picture header;
|
||
a unit that shows an existing frame does not.
|
||
|
||
The plan a supplied floor is made against is the **keyframe plan**, the file
|
||
`plan-<id>.txt`: each piece's first frame in decimal, one per line, each line ended by a
|
||
line feed. Its pieces' starts are the plan of §4.2, and `wai-pack plan` writes it from the
|
||
source and the policy before any floor is encoded.
|
||
|
||
**7.4** The floor is presentation. Its output is never compared with the primary's, and
|
||
no tier or `replicate` claim covers it.
|
||
|
||
**7.5** A floor produced by an encoder that is not a pure function of its input (§11)
|
||
**MUST** name that encoder and its version in `derived_from.renderer`.
|
||
|
||
**7.6** *CMAF.* The floor of a video component is also written as CMAF files, which no
|
||
stage index lists: `floor/<id>/init.mp4`, and one fragment per piece,
|
||
`floor/<id>/<SHA-256 of the fragment>.m4s`. Box layouts are ISO/IEC 14496-12's, with the
|
||
AV1 binding's sample entry; every integer is big-endian and every time 0:
|
||
|
||
- `init.mp4`: `ftyp` (major brand `cmfc`, minor version 0, compatible `cmfc`, `iso6`,
|
||
`av01`), then `moov` holding `mvhd` (version 0, timescale `tb_num`, rate 1, volume 1,
|
||
the unity matrix, next track 2), `trak` (`tkhd` version 0 with flags 3, track 1,
|
||
layer, alternate group and volume 0, the unity matrix, the width and height in 16.16; `mdia`: `mdhd` version 0, timescale
|
||
`tb_num`, language `und`; `hdlr` `vide` named `video` with its terminating zero byte;
|
||
`minf`: `vmhd` with flags 1,
|
||
`dinf` with one self-contained `url `, and `stbl`: `stsd` with one `av01` sample entry
|
||
of the width and height, data reference 1, resolution 72 dpi, frame count 1, an empty
|
||
compressor name, depth 24 and pre-defined −1, holding `av1C`, then empty `stts`,
|
||
`stsc`, `stsz` and `stco`), and `mvex` with `trex` (track 1, sample description 1).
|
||
`av1C` is marker and version 1, then the sequence header's profile, level, tier,
|
||
bit depth, monochrome and chroma fields, no initial presentation delay, then the
|
||
sequence header OBU as the stream holds it. The width and height are the sequence
|
||
header's maximum frame width and height.
|
||
- A fragment: `moof` holding `mfhd` (sequence number piece + 1) and `traf` (`tfhd` with
|
||
default-base-is-moof, track 1; `tfdt` version 1, the piece's start × `tb_den`; `trun`
|
||
version 0 with data offset, sample duration, size and flags, the offset pointing at
|
||
the `mdat` payload), then `mdat`. Each sample is a temporal unit without its temporal
|
||
delimiter, lasting `tb_den`; its flags are `0x02000000` at a shown key frame and
|
||
`0x01010000` otherwise.
|
||
|
||
The codecs string of the floor is the AV1 binding's short form,
|
||
`av01.<profile>.<level, two digits><M or H>.<bit depth, two digits>`.
|
||
|
||
## 8. Classes, catalogs and delivery
|
||
|
||
**8.1** *What a class lists.* The stages listed for a class are a pure function of the
|
||
class, the piece and the effective policy, so every viewer of a class receives the same
|
||
catalog bytes.
|
||
|
||
- A piece that starts at unit `start` and lasts `duration` units spans the
|
||
milliseconds from `s_ms` up to `e_ms`, the floors of `start · den · 1000 / num` and of
|
||
`(start + duration) · den · 1000 / num`; it lies in moment *i* when `s_ms < to_ms` and
|
||
`e_ms > from_ms`. Rule *i* matches it when each condition the rule states holds: its
|
||
kind is in `when.piece_kind`, and a tag of a moment it lies in is in
|
||
`when.moment_tag`. Matching rules fold in document order (§3.4).
|
||
- A floor-only class lists the floor (`0xFF`). Otherwise the class lists
|
||
`min(max_layer, the rules' then.max_layer, the piece's highest layer)`; under
|
||
`max_kbps`, while the listed prefix needs more than `max_kbps`, that is
|
||
`ceil(8 · bytes · num / (1000 · duration · den))` kilobits per second over the bytes of
|
||
the listed bases and refinements, the top layer is dropped, and a base that alone
|
||
needs more makes the piece floor-only for the class.
|
||
|
||
**8.2** A catalog **MUST NOT** list a refinement without the stages below it.
|
||
|
||
**8.3** *HTTP.* A package is offered in [http-delivery](http-delivery.md)'s Mode N
|
||
(§11.1 there): the classical presentation is the floor alone, and WAI objects are
|
||
offered in playlists of their own.
|
||
|
||
- A WAI object is none of the media segment formats of RFC 8216 §3. A WAI playlist is
|
||
offered only to a sink that recognizes its codecs value from its own capability list,
|
||
and a packager **MUST** write a classical master that names nothing of WAI.
|
||
- A browser's media-capabilities query answers only for the browser's own decoders,
|
||
and **MUST NOT** be used to decide whether a `wai1.*` value can be decoded
|
||
([http-delivery](http-delivery.md) §4.3).
|
||
- Each class's WAI playlist **MUST** name its class and its stage view.
|
||
|
||
Durations: a piece of *d* units lasts `floor(d · den · 1000 / num)` milliseconds,
|
||
written `S.mmm` (whole seconds, a full stop, three digits). A target duration is the
|
||
largest `ceil(d · den / num)`. A rate is `ceil(8 · bytes · num / (d · den))` bits per
|
||
second; a variant's `BANDWIDTH` is the largest over the pieces it lists and
|
||
`AVERAGE-BANDWIDTH` the rate of all their bytes over all their durations. A floor
|
||
variant's bytes per piece are its fragment's; a WAI variant's are the listed bases' and
|
||
refinements', over the pieces the class does not take as floor only. Every playlist
|
||
line ends with a line feed. For component *c*, of frame size `W×H`, whose floor has
|
||
codecs string *F* and maximum frame size `FW×FH`:
|
||
|
||
- `hls/classic/master.m3u8`: `#EXTM3U`, `#EXT-X-VERSION:7`,
|
||
`#EXT-X-INDEPENDENT-SEGMENTS`, `#EXT-X-STREAM-INF:BANDWIDTH=<b>,AVERAGE-BANDWIDTH=<a>,CODECS="F",RESOLUTION=FWxFH`,
|
||
`../classic/floor-<c>.m3u8`: a URI that names the same playlist from every class's
|
||
directory, so each class's master differs from this one only by the session-data
|
||
line, as [http-delivery](http-delivery.md) §11.1 and §11.8 require.
|
||
- `hls/classic/floor-<c>.m3u8`: `#EXTM3U`, `#EXT-X-VERSION:7`,
|
||
`#EXT-X-TARGETDURATION:<t>`, `#EXT-X-MEDIA-SEQUENCE:0`, `#EXT-X-PLAYLIST-TYPE:VOD`,
|
||
`#EXT-X-INDEPENDENT-SEGMENTS`, `#EXT-X-MAP:URI="../../floor/<c>/init.mp4"`, then per
|
||
piece `#EXTINF:<S.mmm>,` and `../../floor/<c>/<sha256>.m4s`, then `#EXT-X-ENDLIST`.
|
||
- `hls/<class>/master.m3u8`: the classical master, byte for byte, with, when the class
|
||
lists a WAI object, `#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8"`
|
||
after `#EXT-X-INDEPENDENT-SEGMENTS`, and no other change.
|
||
- `hls/<class>/wai.m3u8`, when the class lists a WAI object: `#EXTM3U`,
|
||
`#EXT-X-VERSION:7`, `#EXT-X-INDEPENDENT-SEGMENTS`,
|
||
`#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="<class>"`,
|
||
`#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/<class>.json"`,
|
||
`#EXT-X-STREAM-INF:BANDWIDTH=<b>,AVERAGE-BANDWIDTH=<a>,CODECS="<union>",X-WAI-FLOOR="<floor>",RESOLUTION=WxH`,
|
||
`wai-<c>.m3u8`. The union and the floor are [http-delivery](http-delivery.md) §4.2's
|
||
over the objects the class lists.
|
||
- `hls/<class>/wai-<c>.m3u8`: the floor playlist's lines without `#EXT-X-MAP`, adjacent
|
||
to it ([http-delivery](http-delivery.md) §11.2); per piece, `#EXT-X-GAP` when the
|
||
class takes it as floor only, `#EXTINF:<S.mmm>,`, each refinement the class lists,
|
||
in entry order, as `#WAI-LAYER:N=<layer>,URI="../../objects/<hash>.wai#n=<byte_len>"`,
|
||
then its base as `../../objects/<hash>.wai#n=<byte_len>`: a layer line follows its
|
||
segment's `EXTINF` ([http-delivery](http-delivery.md) §10.4).
|
||
|
||
Every HLS file above and every MPD below is a rendering of
|
||
[http-delivery](http-delivery.md)'s (Appendix G for a media playlist): the reference
|
||
packager writes them with the binding's one HLS and one DASH renderer.
|
||
|
||
DASH, the same as two MPDs ([http-delivery](http-delivery.md) §11.1), each
|
||
`<?xml version="1.0" encoding="UTF-8"?>`, then
|
||
`<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:full:2011" type="static" mediaPresentationDuration="PT<S.mmm>S" minBufferTime="PT<t>S">`
|
||
over the component's whole duration and its target duration, `<Period id="p0" start="PT0S">`,
|
||
one `AdaptationSet`, `</Period>` and `</MPD>`, indented by two spaces a level:
|
||
|
||
- `dash/classic.mpd`: `<AdaptationSet id="1" contentType="video" mimeType="video/mp4" codecs="F" width="FW" height="FH" segmentAlignment="true" startWithSAP="1">`
|
||
holding `<Representation id="<c>-floor" bandwidth="<b>">` with
|
||
`<SegmentList timescale="<num>">`: `<Initialization sourceURL="../floor/<c>/init.mp4"/>`,
|
||
a `SegmentTimeline` of one `<S t="<(start − first start)·den>" d="<d·den>"/>` per
|
||
piece, and one `<SegmentURL media="../floor/<c>/<sha256>.m4s"/>` per piece.
|
||
- `dash/<class>.mpd`, when the class lists a WAI object:
|
||
`<AdaptationSet id="1" contentType="video" mimeType="application/wai" codecs="<union>" width="W" height="H">`
|
||
holding `<EssentialProperty schemeIdUri="tag:transaction.science,2026:wai/http-delivery"/>`,
|
||
`<SupplementalProperty>`s of schemes `tag:transaction.science,2026:wai/floor` (value the
|
||
floor), `tag:transaction.science,2026:wai/class` (the class) and
|
||
`tag:transaction.science,2026:wai/stages` (`../stages/<class>.json`), then
|
||
per layer *k* up to the highest the class lists, a `Representation` with attributes
|
||
`id="<c>-l<k>"`, for *k* > 0 `dependencyId="<c>-l<k−1>"`, and `bandwidth` the peak rate of its own
|
||
layer's objects over the pieces it lists, whose
|
||
`SegmentList` has a timeline entry and a `SegmentURL media="../objects/<hash>.wai"`
|
||
for each piece the class lists to layer *k* (a gap where it does not).
|
||
|
||
The media type `application/wai` is proposed, not registered
|
||
([http-delivery](http-delivery.md) §3). This revision writes the catalogs of a package
|
||
of one component; how the variants and groups of several components are written is
|
||
defined with the packing of several components.
|
||
|
||
**8.4** *MoQ.* `moq/catalog.json` is RFC 8785 canonical JSON and a line feed:
|
||
`{"tracks": [...], "version": 1}`, the tracks by layer, then by deadline ascending,
|
||
then the floor. Each (component, layer, deadline) is one track of
|
||
`packaging` `"wai"`: name `<c>/l<k>`, or `<c>/l<k>@<deadline>` for a refinement whose
|
||
deadline is not `defaults.deadline_ms`; `renderGroup` 1; `priority` §8.5's;
|
||
`wai_capabilities` its entries' capabilities by first appearance; `wai_layer` *k*;
|
||
`wai_receipts` false (no object of this revision carries a `WAI_RECEIPT` property);
|
||
`wai_determinism` the weakest tier of its entries, absent when that is none. The base
|
||
track has `altGroup` 1 when the component has a floor. MSF's catalog defines `depends`
|
||
as the tracks on which a track is dependent, and this section has a sink receive every
|
||
one of them (below): a dependency is all of its tracks, not any one. So a refinement track
|
||
`depends` on every track that carries an entry its own entries refine, in track order —
|
||
the layer below at the same deadline, where a piece's layers share its deadline as the
|
||
reference packager writes them — and never on a track the catalog does not hold. It
|
||
carries `wai_delivery_timeout_ms` (§8.6). The floor is a track
|
||
`<c>/floor` of `packaging` `"cmaf"` with `altGroup` 1, `renderGroup` 1, `codec`,
|
||
`width` and `height`. A sink **MUST NOT** subscribe to a track unless it also receives
|
||
every track that track depends on, so a sink that selects the floor subscribes to no
|
||
layer track. `wai_layer` and `wai_delivery_timeout_ms` are catalog fields this
|
||
extension defines under the `wai_` prefix of
|
||
[moq-streaming-format](moq-streaming-format.md) §4; a later revision of that extension
|
||
may define them there instead.
|
||
|
||
**8.5** *Priority.* The base track's publisher priority is 64 and layer *k*'s
|
||
`64 + 32·k`: a refinement track's value is strictly greater than that of the track it
|
||
depends on, so it is delivered after it (MOQT: a lower value is delivered first).
|
||
|
||
**8.6** *Deadline.* An entry's `deadline_ms` is the longest a refinement may arrive
|
||
after the base of its piece was published and still be presented; the rules' or
|
||
`defaults.deadline_ms`. A refinement track carries DELIVERY_TIMEOUT
|
||
`deadline_ms − live.layer_lag_ms[k]` (§3.4 has refused a policy where that is below 1).
|
||
Entries of one layer with different deadlines travel on different tracks. A relay may
|
||
drop a late refinement (MOQT §5.2), so a refinement never delays the base.
|
||
|
||
**8.7** *Presentation.* A sink presents piece *p* at the highest prefix whose objects
|
||
all arrived and verified by *p*'s presentation instant, and **MUST NOT** delay the base
|
||
to wait for a refinement. What the receipts of a dropped object say is
|
||
[jwp-receipts](jwp-receipts.md)' to define, not this extension's.
|
||
|
||
**8.8** *Stage views.* `stages/<class>.json` is RFC 8785 canonical JSON and a line feed:
|
||
`{"class": "<class>", "components": {"<c>": {"media_playlist":
|
||
"../hls/<class>/wai-<c>.m3u8", "pieces": [...]}}, "policy_digest": "<hex>"}`, with
|
||
`media_playlist` only when the class lists a WAI object, and each piece
|
||
`{"dur", "p", "start", "stages": [...]}` with `"floor_only": true` when the class takes
|
||
it as floor only (and `stages` empty), and each listed stage `{"bytes", "h", "layer"}`,
|
||
a refinement's with `"refines"` (the content hash of the entry it refines) and
|
||
`"deadline_ms"`.
|
||
|
||
## 9. Package receipt
|
||
|
||
**9.1** A version-2 package receipt binds the asset id; the policy digest; its parent
|
||
kind and parent receipt hash; per component its id, media class, index digest and stage
|
||
root; the entry, distinct-object, referenced-byte and stored-byte counts, which a
|
||
verifier recomputes from the indexes; the packager `builtin:<crate>@<version>`; the
|
||
energy figure with its class, or the unmetered form; the grant; and the signer. Its
|
||
signing payload, big-endian:
|
||
|
||
labelled_domain("wai:pack-receipt\x01", label)
|
||
u32 len ‖ asset_id ‖ policy_digest[32]
|
||
u8 parent_kind (0 none, 1 live-window, 2 re-pack) ‖ [parent_receipt_hash[32] if not 0]
|
||
u32 n ‖ n × ( u32 len ‖ id ‖ u8 media_class ‖ index_digest[32] ‖ stage_root[32] )
|
||
u64 total_entries ‖ u64 unique_objects ‖ u64 total_bytes ‖ u64 stored_bytes ‖ u64 work_units
|
||
u32 len ‖ packager
|
||
u64 joules_micro ‖ write_label(label)
|
||
grant_hash[32] ‖ u32 len ‖ capability ‖ u64 joule_ceiling_micro ‖ u8 0 | u8 1 ‖ u64 funds_ceiling
|
||
signer_pubkey[32] ‖ u32 len ‖ signer_id
|
||
|
||
receipt_hash = BLAKE3("wai:video-package-receipt-id\x01" ‖ payload ‖ sig)
|
||
|
||
`labelled_domain` and `write_label` are [energy-measurement](energy-measurement.md)
|
||
§2.8's: unmetered, the domain as written and no label; metered, its final byte `\x02`
|
||
and the class's signed encoding. `total_entries` counts every entry; `unique_objects`
|
||
the distinct content hashes over every component; `total_bytes` the entries'
|
||
`byte_len`; `stored_bytes` the `byte_len` of each content hash's first entry;
|
||
`work_units` equals `total_entries`. A receipt whose content hash comes at two lengths
|
||
has no counts. The signature is Ed25519 by `signer_pubkey`, verified as SPEC §7
|
||
*Signatures* requires; a signature or a key that rule refuses is `receipt-signature`.
|
||
|
||
Its JSON form, `package.receipt.json`, is RFC 8785 canonical, hexadecimal lowercase,
|
||
integers as numbers no greater than `2^53 − 1` (a packager refuses to seal anything
|
||
else, `receipt-json`): `asset_id`; `components`, each `{"id", "index":
|
||
"index-<id>.wsix", "index_digest", "media_class", "stage_root"}`; `grant`
|
||
`{"capability", "funds_ceiling", "grant_hash", "joule_ceiling_micro"}` with `null` for
|
||
no ceiling (a joule ceiling of `2^64 − 1`); `joules_micro`; `kind` `"package"`;
|
||
`packager`; `parent_kind` (`none`, `live-window` or `re-pack`); `parent_receipt_hash`
|
||
(`null` for none); `policy_digest`; `receipt_hash`; `sig`; `signer_id`;
|
||
`signer_pubkey`; `stored_bytes`; `total_bytes`; `total_entries`; `unique_objects`;
|
||
`version` 2; `work_units`; `energy_provenance`, the class's label (energy-measurement
|
||
§2.7) or `null` when unmetered; and the class's evidence members exactly as its class
|
||
carries them. Every member is present, with `null` where it has no value
|
||
(`parent_receipt_hash`, `energy_provenance`, a grant's ceilings) and nowhere else.
|
||
|
||
A reader refuses, before anything else, JSON that is not RFC 8259 or that names a member
|
||
twice in one object, at any depth (`receipt-json`). It then reads `"version"`: absent or
|
||
1 is a version-1 receipt (§9.3), 2 a version-2 one, anything else `receipt-version`. It
|
||
then refuses (`receipt-json`) a member it does not define or one missing, `null` where
|
||
the member has a value, a value of another form (a number that is not an integer of its
|
||
range, `-0` among them; a string that is not Unicode scalar values), evidence other than
|
||
exactly what the class carries, a class label energy-measurement §2.7 refuses, a parent
|
||
kind and hash that disagree, an `index` other than `index-<id>.wsix`, and a
|
||
`receipt_hash` that is not the receipt's. A reader does not require the canonical form;
|
||
a writer writes it, and a package's `package.receipt.json` must be it (§11.3).
|
||
|
||
A verifier of a receipt against its indexes, in its components' order, refuses, the
|
||
first that applies: no component, or another number of indexes than components, a live
|
||
window's receipt included (`receipt-index-count`); an index that breaks §6 (its
|
||
reason); one that does not hash to its `index_digest` (`receipt-index-digest`), has
|
||
another stage root (`receipt-stage-root`), names another component or media class
|
||
(`receipt-component`) or another policy digest (`receipt-policy-digest`); two components
|
||
of one id (`receipt-component`); other counts, or a content hash at two lengths in any of the
|
||
indexes (`receipt-counts`); a parent kind that does not
|
||
match the indexes' `prev_index_digest`, which is zero in every index for none and a
|
||
re-pack and non-zero in every one for a live window (`receipt-parent-kind`); an energy
|
||
figure and class that do not cohere, or a legacy label (`receipt-energy`); a signature
|
||
that does not verify (`receipt-signature`).
|
||
|
||
**9.2** Live windows chain by the parent link with parent kind `live-window`. A re-pack
|
||
of an earlier package **MAY** name it with parent kind `re-pack`.
|
||
|
||
**9.3** A version-1 receipt (`wai.video.package`'s segment map) keeps its bytes and
|
||
still verifies. Its JSON form is RFC 8785 canonical with `version` 1: `asset_id`,
|
||
`energy_provenance` (`null` when unmetered) with its evidence, `grant`, `joules_micro`,
|
||
`kind` `"package"`, `parent_receipt_hash`, `receipt_hash`, `segment_map_root`,
|
||
`segments` (each `{"byte_len", "content_hash", "duration_ms", "index"}`), `sig`,
|
||
`signer_id`, `signer_pubkey`, `stored_bytes`, `total_bytes`, `total_segments`,
|
||
`unique_segments`, `work_segment_ops`. A reader reads one without `version` as version 1,
|
||
and, because version-1 receipts were issued before this form, keeps them verifying:
|
||
`energy_provenance` may be absent (read as unmetered), an evidence member written `null`
|
||
is absent, and an on-chip counter's filtering, absent, reads as unknown. It refuses a
|
||
member named twice as version 2's reader does.
|
||
Its signing payload, big-endian, is
|
||
|
||
"wai:video-package-receipt\x01"
|
||
u32 len ‖ asset_id ‖ segment_map_root[32]
|
||
u64 total_segments ‖ u64 unique_segments ‖ u64 total_bytes ‖ u64 stored_bytes ‖ u64 work_segment_ops
|
||
u64 joules_micro ‖ class (u8 0 when unmetered, else the class's signed encoding)
|
||
the grant as §9.1 writes it
|
||
u8 0 | u8 1 ‖ parent_receipt_hash[32]
|
||
u64 n ‖ n × BLAKE3("wai:video-package-seg\x01" ‖ u64 index ‖ content_hash[32] ‖ u64 duration_ms ‖ u64 byte_len)
|
||
signer_pubkey[32] ‖ u32 len ‖ signer_id
|
||
|
||
`segment_map_root` is the jwp-receipts §2.2 Merkle root over those segment leaves, the
|
||
counts are the segments' (the bytes of one copy per content hash for `stored_bytes`), and
|
||
`receipt_hash` is version 2's.
|
||
Both versions share the identity domain, so the figure profile
|
||
`wai:video-package-receipt-id` ([energy-measurement](energy-measurement.md) §6.1)
|
||
covers both; a version-2 receipt's figure is read only with the indexes it verifies
|
||
against.
|
||
|
||
**9.4** Where the energy figure is not the unmetered form, the packager **MUST** publish
|
||
a `measurement` claim about it ([jwp-receipts](jwp-receipts.md) §2.4.8,
|
||
[energy-measurement](energy-measurement.md) §6) as
|
||
`claims/<receipt_hash>.measurement.json`: subject the receipt, figure profile
|
||
`wai:video-package-receipt-id`, signed with the package key and checked against the
|
||
receipt before it is written. A verifier refuses a metered package without it
|
||
(`measurement-claim-missing`) or with one that does not verify and check against the
|
||
receipt (`measurement-claim-invalid`).
|
||
|
||
## 10. Royalty-free packaging
|
||
|
||
A packager run with `royalty_free_only = true` **MUST** refuse to emit any object whose
|
||
capability the registry does not classify as royalty-free, including one it does not
|
||
classify at all (§3.6, rule 5). A package receipt says nothing about patents.
|
||
`royalty_free_only` records the registry class the packager read, "licensed
|
||
royalty-free by its standards body"; it is not a freedom-to-operate opinion.
|
||
|
||
## 11. Package, re-pack equivalence and verification
|
||
|
||
**11.1** *The package* is a directory of these files and no other file or directory
|
||
(every directory below it holds one of them):
|
||
|
||
policy.toml policy.json plan-<c>.txt index-<c>.wsix package.receipt.json
|
||
objects/<content hash>.wai …
|
||
floor/<c>/init.mp4 floor/<c>/<sha256>.m4s …
|
||
hls/classic/… hls/<class>/… dash/… moq/catalog.json stages/<class>.json
|
||
claims/<receipt_hash>.measurement.json (a metered package)
|
||
LISTING
|
||
|
||
`policy.toml` is the policy as given; `policy.json` its effective canonical JSON
|
||
(§3.2). `LISTING` is the listing (SPEC §3.1) of every other file, and its SHA-256 is the
|
||
**pack digest**. A component or class id that cannot be one path segment of a listing,
|
||
two class ids equal once `A`–`Z` are mapped to `a`–`z`, and a class named `classic` in
|
||
any case (ids name paths, and paths fold case) are refused (`pack-id`).
|
||
|
||
**11.2** *Re-pack equivalence.* Given the same source bytes, effective policy, supplied
|
||
floor, signing key and packager identity, a conforming reference packager **MUST**
|
||
produce byte-identical package files on every machine, except the energy figure and
|
||
measurement claim of a metered package and floor objects §7.5 covers. A verifier
|
||
**MAY** pack again and compare listings. A packager whose identity differs is not held
|
||
to this.
|
||
|
||
**11.3** *Verification.* A verifier of a package, without its sources, applies these
|
||
checks in this order and refuses at the first that fails; a check that reads files a
|
||
failed check names is not applied:
|
||
|
||
1. Every file below the package's directory, read strictly — a name beginning with `.`
|
||
and a `*.sha256` file included, as a listing (SPEC §3.1) would not skip them — is a
|
||
regular file whose path a listing can carry; a symbolic link, a directory that
|
||
cannot be read or holds nothing, or anything else is refused (`unexpected-file`).
|
||
2. `LISTING` is present (`listing-missing`) and is the listing of every other file
|
||
(`listing-mismatch`).
|
||
3. `package.receipt.json` reads as a version-2 receipt (§9.1: `receipt-json`,
|
||
`receipt-version`) and is that receipt's canonical form, byte for byte
|
||
(`receipt-json`).
|
||
4. `policy.json` hashes to the receipt's policy digest (`policy-digest-mismatch`);
|
||
`policy.toml` is a policy (its own reason, §3.2–§3.4); `policy.json` is its
|
||
effective canonical JSON, `royalty_free_only` forced on where `policy.json` says
|
||
`true` (`policy-json-mismatch`; an override never turns it off, §3.2, so a
|
||
`policy.json` that says `false` over a `policy.toml` that says `true` is refused
|
||
here); its ids can name files (`pack-id`, §11.1); and under that effective policy it
|
||
asks for nothing a packager may not emit (§3.6's reason; under `royalty_free_only`, a
|
||
patent-pool floor is `license-refused`).
|
||
5. The receipt's components are the policy's, by id, in order, each of the policy's
|
||
media (`policy-component-mismatch`), so a package is never a receipt over fewer
|
||
components than its policy has; each index is present (`index-missing`); the receipt
|
||
verifies against them (§9.1, the index rules of §6 among its reasons).
|
||
6. Per component, in order:
|
||
- the index against the policy (`index-policy-mismatch`): its timebase and media
|
||
class are the component's; its string table is the component's id, the class ids
|
||
in policy order, then each capability at its first use; its class records are the
|
||
policy's classes, in order, with their `max_layer`, floor-only flag and `max_kbps`;
|
||
every entry's role, capability and layer are a rung's of its component (a base, a
|
||
refinement at the rung's layer, or a floor); every piece's kind is one a video piece
|
||
has, and its rules, moments and class plan are §8.1's from its kind, span and
|
||
entries;
|
||
- the plan file holds the index's starts and its digest is the header's
|
||
(`plan-mismatch`);
|
||
- every entry's object is present (`object-missing`) and keeps the object rules of
|
||
§6.3, its counted bytes included;
|
||
- every decode-equivalent prefix decodes to its `prefix_digest` (`prefix-digest`);
|
||
- when all of that holds, the floor: per piece, one floor object of the floor rung's
|
||
capability whose renderer, settings, framing header, unit count and target are
|
||
§7.2's (`floor-object-mismatch`) and which presents the piece's base
|
||
(`floor-motif-mismatch`); its units each one temporal unit holding a frame, the
|
||
first of the first piece carrying a sequence header, every sequence header the
|
||
first's bytes, every shown key frame carrying one (`floor-av1-malformed`); each
|
||
piece opening on a shown key frame, and no other unit being one unless the policy
|
||
allows extra key frames (`floor-keyframe-mismatch`); and the CMAF files are the
|
||
ones its temporal units give (§7.6, `floor-cmaf-mismatch`);
|
||
- then the catalogs are the ones the index, the objects and the floor give, and an
|
||
index no catalog can be written for (a start past `2^53 − 1`) is refused
|
||
(`catalog-mismatch`).
|
||
7. A metered package's measurement claim (§9.4: `measurement-claim-missing`,
|
||
`measurement-claim-invalid`); an object file no entry names (`object-unlisted`); a
|
||
catalog file past the ones given (`catalog-unexpected`); any other file
|
||
(`unexpected-file`).
|
||
|
||
**11.4** *What this revision packs.* The reference packager packs one video component
|
||
with a `wai.video.int_motion` base (§5.5, §5.6) and a supplied AV1 floor (§7.3). It
|
||
refuses by name, before writing anything, what it does not pack yet: another medium
|
||
(`pack-media-unsupported`), a refinement rung (`pack-refinement-unsupported`), a rule's
|
||
ladder subset (`pack-rule-ladder-unsupported`), a floor to encode
|
||
(`pack-floor-encode-unsupported`), and a rung of a capability it does not encode
|
||
(`pack-capability-unsupported:<cap>`, every one naming the capability of the rung it
|
||
does not pack); and a component with no source (`pack-source-missing`) or whose source's
|
||
frame rate is not its timebase (`pack-source-rate`). Where its own work fails it
|
||
refuses by name too, and writes nothing: an encode the encoder refuses, such as a piece
|
||
past the samples it holds, or an object it wrote that does not count (`pack-encode`); a
|
||
piece past `2^32 − 1` units (`pack-piece-length`); catalogs it cannot write, the case
|
||
§11.3 refuses as `catalog-mismatch` (`pack-catalog`); a meter that was asked for and
|
||
gives no figure, a span too short to reach a microjoule or a figure past `2^64 − 1`
|
||
(`pack-meter`, never an unmetered package in its place); a measurement claim it cannot
|
||
write (`pack-measurement-claim`); and a listing it cannot write (`pack-listing`). It
|
||
writes a package only into a directory that is new or holds nothing, and refuses any
|
||
other before it packs (`pack-out-not-empty`): written among another package's files, it
|
||
would leave behind the ones it does not write.
|
||
|
||
## 12. Reasons
|
||
|
||
| Reason | Where |
|
||
|---|---|
|
||
| `policy-toml` (not UTF-8, a byte-order mark, not TOML 1.0, nesting past 32), `policy-float`, `policy-datetime`, `policy-negative`, `policy-integer-range` | §3.2 |
|
||
| `policy-unknown-key`, `policy-missing-key`, `policy-type`, `policy-value`, `policy-schema` | §3.3 |
|
||
| `policy-too-many-components`, `policy-too-many-rungs`, `policy-too-many-classes`, `policy-too-many-rules`, `policy-too-many-moments`, `policy-duplicate-id`, `policy-lead`, `policy-open-gop`, `policy-piece-bounds`, `policy-ladder-component`, `policy-refines-role`, `policy-dangling-refines`, `policy-refines-cycle`, `policy-refines-base`, `policy-refines-branch`, `policy-no-base`, `policy-class-layer`, `policy-rule-ladder`, `policy-layer-lag`, `policy-too-many-deadlines`, `policy-deadline-unreachable` | §3.4 |
|
||
| `capability-unknown`, `capability-not-payload`, `capability-candidate`, `capability-role`, `license-unclassified`, `license-refused`, each naming the capability (and the licence label) | §3.6 |
|
||
| `y4m-header`, `y4m-interlaced`, `y4m-colorspace`, `y4m-size-change`, `y4m-truncated`, `wav-format`, `wav-truncated` | §4.7 |
|
||
| `index-magic`, `index-version`, `index-truncated`, `index-reserved`, `index-media-class`, `index-role`, `index-tier`, `index-string`, `index-trailing` | §6.2 |
|
||
| `index-timebase`, `window-start`, `piece-order`, `piece-entries`, `piece-kind`, `entry-flags`, `entry-order`, `layer-role`, `refines-outside-window`, `refines-mismatch`, `layer-gap`, `tier-entropy-passthrough`, `tier-weakest`, `tier-mismatch`, `digest-without-tier`, `deadline-role`, `floor-derived-from`, `dup-length`, `dup-flag`, `class-plan`, `class-flags` | §6.3, §6.4 |
|
||
| `object-length`, `object-hash`, `non-envelope-entry`, `object-lengths`, `object-capability`, `object-refines`, `object-derived-from` | §6.3 |
|
||
| `floor-missing`, `floor-listing-mismatch`, `floor-stream-missing`, `floor-av1-malformed`, `floor-frame-count`, `floor-keyframe-mismatch` | §7.1, §7.3 |
|
||
| `floor-object-mismatch`, `floor-motif-mismatch`, `floor-av1-malformed`, `floor-keyframe-mismatch`, `floor-cmaf-mismatch`, against a package | §11.3 |
|
||
| `receipt-json`, `receipt-version`, `receipt-index-count`, `receipt-index-digest`, `receipt-stage-root`, `receipt-component`, `receipt-policy-digest`, `receipt-counts`, `receipt-parent-kind`, `receipt-energy`, `receipt-signature` | §9.1 |
|
||
| `measurement-claim-missing`, `measurement-claim-invalid` | §9.4 |
|
||
| `unexpected-file`, `listing-missing`, `listing-mismatch`, `policy-digest-mismatch`, `policy-json-mismatch`, `policy-component-mismatch`, `index-missing`, `index-policy-mismatch`, `plan-mismatch`, `object-missing`, `prefix-digest`, `catalog-mismatch`, `catalog-unexpected`, `object-unlisted` | §11.3 |
|
||
| `pack-id`, `pack-source-missing`, `pack-source-rate`, `pack-media-unsupported`, `pack-refinement-unsupported`, `pack-rule-ladder-unsupported`, `pack-floor-encode-unsupported`, `pack-capability-unsupported` (naming the capability) | §11.1, §11.4 |
|
||
| `pack-encode`, `pack-piece-length`, `pack-catalog`, `pack-meter`, `pack-measurement-claim`, `pack-listing`, `pack-out-not-empty` | §11.4 |
|