462 lines
31 KiB
Markdown
462 lines
31 KiB
Markdown
# WAI Extension: Platform and Hardware Encoding
|
||
|
||
> Status: Draft. Registers the record `wai.video.encode`. A sink or a packager that needs an
|
||
> AVC (ISO/IEC 14496-10) or HEVC (ISO/IEC 23008-2) rendition encodes it with an encoder the
|
||
> device already has: the operating system's media framework and its hardware media engine,
|
||
> a GPU's encode engine through the video acceleration API or through the GPU driver's own
|
||
> encode interface, or a software AVC library the user obtains separately and names at run
|
||
> time. WAI writes no AVC or HEVC encoder. This extension fixes the shape the encoder's output
|
||
> is held to (§4), what names the encoder (§5), and the **encode record**, a signed statement
|
||
> of who made an elementary stream, from what source, with which settings and at what
|
||
> measured cost (§6 to §8). A verifier holds the stream to its digest and its shape; it never
|
||
> re-encodes. An encode is never a deterministic step: no tier and no `replicate` claim covers
|
||
> the encoded pictures against the source or against a base. An encode made over a package's
|
||
> pieces is framed as one floor object per piece, each naming its encoder in `derived_from`
|
||
> as [packaging](packaging.md) §7 requires in its paragraph 7.5 (§9). No new transport. The reference
|
||
> implementation is the `platform_encode` module of `wai-rs` (feature `platform_encode`), the
|
||
> `wai encode avc|hevc`, `wai encoders` and `wai encode-record verify` commands, and
|
||
> `wai-pack encode-rung` and `wai-pack verify-rung`; the corpus is
|
||
> [`encode-conformance/`](../encode-conformance/), and its second implementation is
|
||
> `encode-conformance/encode_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
|
||
## 1. Scope
|
||
|
||
In scope: what an encode is handed and how a ladder rung's pictures are made (§4.1, §4.2);
|
||
the shape of the elementary stream (§4.3); the encoder interfaces by role and the identity
|
||
each reports (§3, §5); the encode record and its signature (§6); its verification (§7); the
|
||
energy of an encode (§8); an encode over a package's pieces (§9); conformance and
|
||
measurement (§10).
|
||
|
||
Out of scope: choosing an encoder's coding tools, which is the encoder's; rate-distortion
|
||
claims, which this extension never makes; any encoder written for WAI. The record states what
|
||
the encoder made, and a verifier checks the bytes against it.
|
||
|
||
## 2. What WAI writes, and what it does not
|
||
|
||
**2.1** WAI writes no AVC or HEVC encoder and ships none. The reference implementation links
|
||
no encoder: it loads the operating system's media framework, the video acceleration API's
|
||
library, the GPU driver's encode library, or a software library at a path the user names, each
|
||
at run time, and a build of it runs on a host without any of them and says which is missing.
|
||
|
||
**2.2** *Patent position, by mechanism.* Both standards' essential patents are licensed for
|
||
royalties (SPEC §5, *Broadcast and streaming codecs*: `patent-pool`). An encoder an operating
|
||
system or a GPU driver provides is supplied by the device's maker with the device, under that
|
||
maker's terms. A software library the user names is obtained by the user from its distributor,
|
||
separately from WAI, under the terms that distributor states, which may cover the royalties for
|
||
some uses and not for others. This extension states which encoder made a stream (§5); it states
|
||
nothing about whether a use of it is licensed. A deployer decides that from the encoder's
|
||
supplier's terms and its own use.
|
||
|
||
**2.3** *Header syntax under the video acceleration API.* That interface leaves the parameter
|
||
sets and the slice headers to the application, and the drivers reached for this revision emit
|
||
none unless the application hands them over. The reference adapter writes them (ISO/IEC
|
||
14496-10 §7.3.2.1, §7.3.2.2, §7.3.3; ISO/IEC 23008-2 §7.3.2.1 to §7.3.2.3, §7.3.6.1) from the
|
||
values it gives the driver in its parameter buffers, and chooses each picture's QP, which it
|
||
writes in both the parameter buffer and the slice header so the two agree. No picture sample
|
||
passes through it: the driver's engine codes every slice's data.
|
||
|
||
**2.4** *Not a deterministic step.* An encoder is not a pure function of its input
|
||
([packaging](packaging.md) §11): a hardware engine's output is fixed by no specification, and
|
||
the media framework's hardware encoder was measured making other bytes from the same pictures
|
||
in each round (§10.3). So no step of a pack or a verify is an encode, no verdict depends on an
|
||
encoder's output beyond the digest a record states, and an encoded rendition promises only its
|
||
capability's decoder conformance (SPEC §7: `wai.video.avc` and `wai.video.hevc` are
|
||
`StandardDefined`, so every conforming decoder outputs the same pictures from the stream).
|
||
|
||
## 3. Encoder interfaces
|
||
|
||
An encoder is reached through one of four interfaces. Each record names the one it used in
|
||
`encoder.api`.
|
||
|
||
| `api` | what it drives | where |
|
||
|---|---|---|
|
||
| `platform` | the operating system's media framework: its hardware media engine, or its software encoder where the engine is refused | the desktop, phone and TV operating systems that ship the framework |
|
||
| `va` | the video acceleration API, whose driver drives a GPU's encode engine | Linux, through a render node |
|
||
| `gpu-driver` | the encode interface a GPU's driver exports from its own library | Linux |
|
||
| `software` | a software AVC encoder library loaded at run time from a path the user names | any Unix |
|
||
|
||
**3.1** A `va` or `gpu-driver` encode is a hardware encode. A `software` encode is not, and
|
||
codes AVC only in this revision. A `platform` encode is either, and says which (§5).
|
||
|
||
**3.2** *The phone and TV operating system's codec interface (informative).* On the mobile and
|
||
TV operating system whose media codec interface exposes hardware and software encoders by
|
||
name, an adapter would: open an encoder for the codec's MIME type by name, read the name, the
|
||
canonical name and whether the interface reports it hardware-accelerated for `encoder.name` and
|
||
`encoder.hardware`; configure the codec's flexible 4:2:0 input, the target bit rate, no
|
||
B-pictures and no periodic key frame; request a key frame for exactly the pictures the plan
|
||
names; and read the device's hardware and system-on-chip identifiers and the operating system's
|
||
release for `encoder.device` and `encoder.driver`. Its output (the codec's configuration buffer,
|
||
then one buffer per access unit) is an Annex B stream held to §4.3 like any other. This revision
|
||
builds no adapter for it.
|
||
|
||
## 4. The encode
|
||
|
||
### 4.1 The source
|
||
|
||
A source is a YUV4MPEG2 stream: 8-bit 4:2:0 (`C420`, `C420jpeg`, `C420paldv`, `C420mpeg2`, or no
|
||
`C` tag), progressive (`Ip`, `I?` or no `I` tag), each side even and at most 16384, its frame rate
|
||
`F<num>:<den>` both non-zero, and at least one frame. `XCOLORRANGE=FULL` makes it full range;
|
||
anything else is the video range. Each frame is its luma plane, then its Cb and Cr planes, each
|
||
row-major with no padding. The source's **digest** is SHA-256 (FIPS 180-4) over every frame's
|
||
three planes, one frame after another, with no frame header.
|
||
|
||
### 4.2 A ladder rung's pictures
|
||
|
||
A rung asks for a size `W × H` (each even, from 16 to 8192), a frame rate (the source's), a
|
||
target average bit rate in kilobits per second (1 to 1000000) and a range (the source's). Where
|
||
the size is the source's, the encoder is handed the source's frames. Otherwise each plane is
|
||
resampled by **`wai.resample.two-tap`**, chroma at half the luma size: for an axis of `n_in`
|
||
samples resampled to `n_out`, output sample `o` reads the source at
|
||
|
||
p = ((2·o + 1) · n_in · 128) div n_out − 128
|
||
|
||
in 1/256ths of a sample, clamped to `0 ..= 256 · (n_in − 1)`; with `x0 = p >> 8`, `f = p & 255`
|
||
and `x1 = min(x0 + 1, n_in − 1)`, the sample is `(s[x0] · (256 − f) + s[x1] · f + 128) >> 8`.
|
||
Rows are resampled first, then columns, each pass rounding to 8 bits. The resampler is a pure
|
||
function in integers: every host hands its encoder the same samples.
|
||
|
||
The **IDR plan** is the frame indices at which the stream has an IDR access unit: sorted, the
|
||
first 0, each below the source's frame count. A package's plan is its pieces' starts (§9).
|
||
|
||
### 4.3 The stream
|
||
|
||
An encoder's output **MUST** be, and a producer **MUST** refuse to record one that is not:
|
||
|
||
1. an Annex B byte stream (ISO/IEC 14496-10 Annex B; ISO/IEC 23008-2 Annex B): it begins with
|
||
zero bytes and a start code prefix; a NAL unit ends where the next three bytes are
|
||
`00 00 00` or `00 00 01`; no NAL unit is empty, holds `00 00 02`, or has its forbidden bit set;
|
||
an HEVC NAL unit's `nuh_temporal_id_plus1` is not 0; every sequence parameter set reads, an
|
||
AVC one through its VUI (Annex E.1.1) where it has one and an HEVC one through its sub-layer
|
||
ordering information (`sps_max_latency_increase_plus1`); and the start of every slice header
|
||
this section reads (below) reads (`encode-stream-malformed`);
|
||
2. exactly one access unit per source frame, in presentation order (`encode-frame-count`). An
|
||
access unit begins at the first of an access unit delimiter, a parameter set, a prefix SEI or
|
||
the other non-VCL types the standard lists after the previous picture's slices (ISO/IEC
|
||
14496-10 §7.4.1.2.3, its types 6 to 9 and 14 to 18; ISO/IEC 23008-2 §7.4.2.4.4, its types 32
|
||
to 35, 39, 41 to 44 and 48 to 55), or at a slice whose `first_mb_in_slice` is 0 (AVC; the
|
||
first syntax element of its slice header) or whose `first_slice_segment_in_pic_flag` is 1
|
||
(HEVC; the first bit). NAL units after the last picture's slices that begin an access unit
|
||
holding no slice are `encode-stream-malformed`, checked with item 1;
|
||
3. an access unit's slices all IDR or none (`encode-stream-malformed`); no random access point but
|
||
an IDR (HEVC `nal_unit_type` 16 to 23 other than 19 and 20); and an IDR access unit (AVC
|
||
`nal_unit_type` 5; HEVC 19 or 20) at exactly the plan's frames (`encode-keyframe-mismatch`);
|
||
4. every IDR access unit carrying the sequence and picture parameter sets (and the video
|
||
parameter set for HEVC), the same bytes in the same order as the first IDR access unit's; no
|
||
other access unit carrying parameter set bytes the first IDR's do not; no access unit carrying
|
||
a sequence parameter set other than the stream's first; and no access unit before the first
|
||
sequence parameter set (`encode-parameter-sets`);
|
||
5. 8-bit 4:2:0 in one colour plane; progressive, which for AVC is `frame_mbs_only_flag` 1 (this
|
||
revision reads no HEVC field signalling); a single layer: no AVC NAL unit of type 14, 15, 20
|
||
or 21, and HEVC `nuh_layer_id` 0 throughout; and AVC `pic_order_cnt_type` 0 or 2, the types
|
||
this revision reads (`encode-stream-unsupported`);
|
||
6. with no reordering: HEVC `sps_max_num_reorder_pics` of the highest sub-layer 0; AVC the VUI's
|
||
`max_num_reorder_frames` 0 where the SPS has one, and, for `pic_order_cnt_type` 0, each access
|
||
unit's top field order count (§8.2.1.1: `PicOrderCntMsb + pic_order_cnt_lsb`, from the first
|
||
slice's `pic_order_cnt_lsb` and the previous reference picture's, both reset to 0 at each IDR)
|
||
above the one before it since the last IDR (`encode-stream-reordered`). The slice header is
|
||
read only as far as `pic_order_cnt_lsb`; this revision does not read `dec_ref_pic_marking`, so
|
||
a stream whose pictures carry `memory_management_control_operation` 5 may be refused here;
|
||
7. of the rung's size: the sequence parameter set's cropped picture is `W × H` (AVC frame
|
||
cropping, HEVC conformance window), and where an AVC VUI signals `video_full_range_flag` it is
|
||
the rung's range (`encode-sps-mismatch`).
|
||
|
||
Item 1 and item 2's count are checked over the whole stream first; then items 3 to 6 access
|
||
unit by access unit, in stream order, each access unit's items in that order; then item 7. The
|
||
first that fails is the reason. They hold the stream to what a piece of a package needs: each piece starts at an IDR
|
||
access unit that carries its parameter sets, so it decodes with nothing outside it, and access
|
||
unit *i* is the picture of frame *i*.
|
||
|
||
## 5. The encoder's identity
|
||
|
||
**5.1** An encode names its maker with six strings and one flag, each as the interface reports it
|
||
at run time, read as the adapter documents:
|
||
|
||
| member | meaning |
|
||
|---|---|
|
||
| `api` | the interface (§3) |
|
||
| `name` | the encoder, as the interface names it: the framework's encoder identifier and display name; the profile and entrypoint the driver offered, with the driver's vendor string; the codec and preset the GPU driver's interface names; or the library's file name with the settings it was opened with |
|
||
| `version` | the encoder's version: the framework's bundle version; the video acceleration API's version; the encode interface's version and the highest the driver supports; or the library's own version |
|
||
| `hardware` | `yes` when a fixed-function engine coded the pictures, `no` otherwise |
|
||
| `device` | the device the encoder ran on: the processor's name and model; the render node and its PCI identifiers; the GPU's name, ordinal and PCI bus |
|
||
| `driver` | the driver, or for a platform or software encoder the operating system's release and build, that ran it |
|
||
| `library_sha256` | for `software`, the SHA-256 of the file loaded; otherwise absent (`null`) |
|
||
|
||
**5.2** A producer **MUST NOT** state an identity it did not read from the interface. Where an
|
||
interface reports nothing for a member, the adapter writes what it read instead and says so in
|
||
the string; no member is empty.
|
||
|
||
**5.3** A runtime-loaded library is refused at load unless it is of the interface version the
|
||
adapter was written for: its structures may differ between versions. The record names the file
|
||
loaded by its SHA-256, so a relying party can hold it to the distributor's published digest.
|
||
|
||
## 6. The encode record
|
||
|
||
### 6.1 Members
|
||
|
||
The record is a JSON object (RFC 8259) read as [contribution](contribution.md) §2 reads a
|
||
record: strict JSON (no member named twice in one object, at most 32 levels), integers unsigned,
|
||
decimal, with no sign, fraction, exponent or leading zero and at most 2^53 − 1, no other number,
|
||
no boolean; byte strings in lowercase hexadecimal; a **term** 1 to 64 bytes of `a`–`z`, `0`–`9`,
|
||
`-`, `_` and `.`; a **date-time** RFC 3339 in UTC, `YYYY-MM-DDTHH:MM:SS`, at most nine fraction
|
||
digits, then `Z`, its day one its month has in the proleptic Gregorian calendar and its second
|
||
at most 60. It has exactly these members, at every level, none missing and none added;
|
||
a record that does not, or a member of another type or encoding, or outside the fixed words the
|
||
table gives `kind`, `hardware`, `range` and `tier`, is `record_malformed`. Every other set or
|
||
range of values is a §7 step's, and refused with that step's reason.
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `kind` | string | `"wai-encode"` |
|
||
| `capability` | string | `wai.video.avc` or `wai.video.hevc` |
|
||
| `encoder` | object | §5.1's members: `api` a term; `name`, `version`, `device`, `driver` strings of at most 1024 bytes; `hardware` `"yes"` or `"no"`; `library_sha256` 32 bytes or `null` |
|
||
| `settings` | object | `width`, `height`, `fps_num`, `fps_den`, `kbps` integers; `range` `"video"` or `"full"`; `resample` a term, `none` or `wai.resample.two-tap` (§4.2) |
|
||
| `source` | object | `frames`, `width`, `height` integers; `sha256` 32 bytes, the source's digest (§4.1) |
|
||
| `idr` | array | the IDR plan (§4.2), integers |
|
||
| `stream` | object | `sha256` 32 bytes, the stream's SHA-256; `bytes`, `units` integers; `profile_idc`, `level_idc` integers, the SPS's (`general_profile_idc` and `general_level_idc` for HEVC); `tier` `"main"` or `"high"`, HEVC's `general_tier_flag`, and `"main"` for AVC, which has none (held to the stream at §7 step 8) |
|
||
| `energy` | object or `null` | §8 |
|
||
| `wall_us` | integer | the encode's wall time, from the first picture handed over to the last byte back |
|
||
| `package` | object or `null` | §9 |
|
||
| `issued_at` | date-time | when the signer signed it, self-asserted |
|
||
| `signer_pubkey` | 32 bytes | an Ed25519 key (RFC 8032) |
|
||
| `sig` | 64 bytes | §6.2 |
|
||
|
||
### 6.2 Signature and digest
|
||
|
||
```
|
||
sig = Ed25519(signer_key, "wai:video-encode\x01" || JCS(record without "sig"))
|
||
digest = SHA-256("wai:video-encode-record\x01" || JCS(record))
|
||
```
|
||
|
||
JCS is RFC 8785. The signature is verified strictly ([SPEC](../SPEC.md) §7 *Signatures*): a key
|
||
that fails step 1 or 2 is `bad_key`, a signature that fails a later step `bad_signature`. The
|
||
record's text as the reference writes it is its JCS form. The signer is whoever ran the encode;
|
||
the record is that signer's statement of what the interface reported (§5.2).
|
||
|
||
## 7. Verification
|
||
|
||
A verifier checks, in order, and the first that fails is the reason:
|
||
|
||
1. the record reads (§6.1) (`record_malformed`);
|
||
2. its signature (§6.2) (`bad_key`, `bad_signature`);
|
||
3. `capability` is `wai.video.avc` or `wai.video.hevc` (`encode-capability`);
|
||
4. the identity: `name`, `version`, `device` and `driver` each hold a character other than
|
||
Unicode White_Space (`encode-identity-incomplete`); `api` is one of §3's; `library_sha256` is
|
||
present exactly for `software`; a `software` encode is `hardware` `"no"` and of
|
||
`wai.video.avc`; a `va` or `gpu-driver` encode is `hardware` `"yes"`
|
||
(`encode-identity-inconsistent`);
|
||
5. the settings and the source: the rung's width and height even and from 16 to 8192, the
|
||
source's even and from 2 to 16384; both rate members non-zero and at most 2^32 − 1; `kbps` from 1 to 1000000; at least one source frame; and `resample`
|
||
`none` exactly where the rung's size is the source's, `wai.resample.two-tap` otherwise
|
||
(`encode-settings-invalid`);
|
||
6. the IDR plan: its first entry 0, each above the one before, the last below `source.frames`;
|
||
and, where `package` is not `null`, its pieces' starts equal to the plan, each piece of at
|
||
least one frame and starting where the one before it ended, their frames summing to
|
||
`source.frames` (`encode-plan-invalid`);
|
||
7. the energy, where it is not `null` (§8.1) (`encode-energy-invalid`, `encode-energy-coverage`);
|
||
8. with the stream held: its SHA-256 and length are `stream.sha256` and `stream.bytes`
|
||
(`encode-stream-mismatch`); it holds to §4.3 for `source.frames` pictures and the plan, its
|
||
reasons as there; its access units number `stream.units` (`encode-frame-count`); its SPS's
|
||
cropped size is the settings' `width × height`, its profile, level and tier `stream`'s, and an
|
||
AVC VUI's signalled range the settings' (`encode-sps-mismatch`);
|
||
9. with the source held: it reads as §4.1 says, its digest is `source.sha256`, its width, height
|
||
and frame count are `source`'s, its frame rate's numerator and denominator, as its header
|
||
writes them, are the settings' `fps_num` and `fps_den`, and its range is the settings'
|
||
(`encode-source-mismatch`);
|
||
10. with the package and the floor objects held, §9.3 (`encode-package-mismatch`).
|
||
|
||
Steps 8 to 10 are made only where the verifier holds what they read; a verdict names which it
|
||
made. Nothing in them re-encodes: the source is held to its digest, and the stream to its digest
|
||
and its shape.
|
||
|
||
## 8. Energy
|
||
|
||
### 8.1 The figure
|
||
|
||
`energy` restates a measured figure with the members [jwp-receipts](jwp-receipts.md) §2.4.5
|
||
gives an energy claim, and three of this extension's own:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `joules_micro` | integer | the figure, microjoules, for the whole encode the record names |
|
||
| `energy_provenance` | string | an acquisition class of [energy-measurement](energy-measurement.md) §2 |
|
||
| `energy_uncertainty` | object or `null` | `{ "relative_ppm", "window_us" }`, integers, as §2.3 there |
|
||
| `energy_counter_filtering` | string or `null` | an `OnChipCounter`'s filtering state |
|
||
| `energy_calibration_ref` | string or `null` | a `CalibratedInstrument`'s calibration reference |
|
||
| `coverage` | string | `Whole` or `Partial` ([energy-binding](energy-binding.md) §4) |
|
||
| `counter` | term | the counter read (§8.2) |
|
||
| `attribution` | string | `task`, `rail` or `bracket` (§8.2) |
|
||
| `fabrics` | array of strings | what the counter's reading covers, each of `cpu`, `gpu`, `media`, `npu`, at least one, in that order, none twice |
|
||
|
||
It is held to jwp-receipts §2.4.5's rules 1 to 3, read as written there: a non-zero figure; a
|
||
class of energy-measurement §2 other than `HwShunt`; the class's evidence whole, including the
|
||
resolution floor `joules_micro × relative_ppm ≥ 288 676`, an `OnChipCounter`'s filtering state
|
||
of `On`, `Off` or `Unknown`, and a `CalibratedInstrument`'s non-blank calibration reference; and
|
||
to a `coverage` of `Whole` or `Partial`, an `attribution` of the three, and `fabrics` as the
|
||
table says (`encode-energy-invalid`). A **hardware** encode's figure whose `fabrics` names
|
||
neither `gpu` nor `media` counts none of the engine that coded the pictures, so it **MUST** be
|
||
`Partial` (`encode-energy-coverage`). A relying party **MUST NOT** report a `Partial` figure as
|
||
the whole cost of the encode.
|
||
|
||
### 8.2 How the reference measures it
|
||
|
||
The figure is the median of an odd number of rounds (at least three) of the same encode, after
|
||
one unmetered round that loads and warms the encoder. A round's figure is a counter's advance
|
||
over the encode (`task`), or energy-measurement §3's marginal: the advance over the encode less
|
||
the mean power over an idle window just before it, times the encode's length (`bracket`); a
|
||
round whose marginal is not above zero, or whose counter fails a read, makes the whole figure
|
||
absent rather than estimated. The declared uncertainty is the rounds' spread read as rectangular,
|
||
`(max − min) / (2·√3)` relative to the median, rounded up, or energy-measurement §2.3's rounding
|
||
floor if that is larger; its window is the median round's. The class is `OnChipCounter` with
|
||
filtering `Unknown`, and the coverage `Partial`: no counter the reference reads sees all of a
|
||
host's energy.
|
||
|
||
| `counter` | what it reads | `attribution` | `fabrics` |
|
||
|---|---|---|---|
|
||
| `task` | the operating system's account of the encoding process's own energy, with the work other processes did for it billed to it | `task` | `cpu` |
|
||
| `rapl` | the processor package zones' energy counters, summed | `bracket` | `cpu` |
|
||
| `gpu-board` | a discrete GPU driver's total-energy counter for its device | `bracket` | `gpu`, `media` |
|
||
| `rapl-and-gpu-board` | both, summed | `bracket` | `cpu`, `gpu`, `media` |
|
||
|
||
The table is the reference's practice, not a rule a verifier applies: §8.1 is. No counter is
|
||
divided among its users by busy time. A `task` figure counts none of a hardware
|
||
engine's draw, since the operating system gives an unprivileged process no account of it; a
|
||
`bracket` figure on a host other work shares is machine-wide, and its rounds' spread says how
|
||
much that work moved it.
|
||
|
||
## 9. An encode over a package's pieces
|
||
|
||
### 9.1 The rendition
|
||
|
||
An **encoded rendition** of a package ([packaging](packaging.md) §11) is one encode of a video
|
||
component's source whose IDR plan is the component's pieces' starts, as its stage index lists
|
||
them (§6.2 there), and whose rung is named by a term, its **rung id**. Each piece is framed as
|
||
one floor object (packaging §5.2, §7.2): a `WAI1` envelope (SPEC §2) whose manifest is the JSON
|
||
text, with no whitespace and its members in this order,
|
||
|
||
{"wai":"1.0","media":"video","intent":"replicate","model_requirement":{"capability":C},
|
||
"conditioning":{"kind":K},"derived_from":{"motif":M,"motif_capability":MC,
|
||
"renderer":R,"settings":D},"target":{"frames":F,"h":H,"w":W}}
|
||
|
||
with `C` the capability, `K` `avc` or `hevc`, `F` the piece's frames, `H` and `W` the rung's
|
||
height and width, each string written as JCS writes it (RFC 8785 §3.2.2.2), and the
|
||
`derived_from` members:
|
||
|
||
- `motif`: the piece's base object's content hash, the stage index's entry of role base and
|
||
layer 0 for that piece, as 64 lowercase hexadecimal digits; `motif_capability`: its capability;
|
||
- `renderer`: `<encoder.name>@<encoder.version>`;
|
||
- `settings`: `BLAKE3("wai:render-settings\x01" ‖ JCS(S))`, with `S` the object
|
||
`{"source": "encoded", "api", "hardware", "device", "driver", "library_sha256",
|
||
"stream_sha256"}`, each member but `source` the record's `encoder` member of that name, and
|
||
`stream_sha256` the record's `stream.sha256`; the digest written as 64 lowercase hexadecimal
|
||
digits.
|
||
|
||
So each object names the encoder and its version in `renderer` (packaging §7.5) and binds its
|
||
device and driver through `settings`, which any verifier holding the record recomputes. Its
|
||
payload is its piece's access units as NAL units each after the four bytes `00 00 00 01`, in the
|
||
order the stream holds them: the stream's own NAL units, re-framed with one start code length,
|
||
beginning with the piece's IDR access unit and its parameter sets.
|
||
|
||
### 9.2 The record's `package`
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `receipt_sha256` | 32 bytes | the SHA-256 of the package receipt's file (packaging §9) |
|
||
| `rung` | term | the rung id |
|
||
| `pieces` | array | one object per piece, in order: `start` and `frames` integers, the piece's first frame and frame count; `motif` 32 bytes, the base it presents; `object` 32 bytes, its floor object's content hash ([jwp-receipts](jwp-receipts.md) §2.1) |
|
||
|
||
The objects are written `objects/<object>.wai`, the stream `<rung id>.h264` or `<rung id>.h265`,
|
||
and the record `<rung id>.encode.json`, beside the package and not inside it: the package
|
||
receipt does not list them, and a package made before them is unchanged.
|
||
|
||
### 9.3 Verification against the package
|
||
|
||
With the package receipt's file, the component's stage index and the floor objects held, a
|
||
verifier checks, in order, every failure `encode-package-mismatch`: the receipt file's SHA-256
|
||
is `receipt_sha256`; the stage index's pieces, in order, have the record's starts and frame
|
||
counts (a video component's timebase unit is one frame, packaging §2), and each piece's one
|
||
entry of role base and layer 0 has the record's `motif`; and each piece's object is present,
|
||
its content hash is `object`, it is a `WAI1` envelope with exactly §9.1's manifest for this
|
||
record and piece, and its payload is §9.1's bytes from the held stream.
|
||
|
||
A sink that decodes an encoded rendition decodes each object's payload as its capability's
|
||
stream; the record tells it who made the bytes, and nothing more.
|
||
|
||
## 10. Conformance and measurement
|
||
|
||
**10.1** *The corpus.* [`encode-conformance/`](../encode-conformance/) holds small elementary
|
||
streams made by encoders through each interface (fixtures, pinned by their SHA-256 in the
|
||
corpus's manifest and never regenerated, since no encoder is a pure function), the source they
|
||
were made from, and records generated reproducibly over them from a fixed-seed key with a fixed
|
||
`issued_at`: accepted cases, and negative controls each refused with its reason; and an encoded
|
||
rendition of a packaging golden with its objects, accepted and mutated. `wai-rs`
|
||
`tests/platform_encode.rs` requires the generator to reproduce the corpus byte for byte and every
|
||
case to get its outcome; `wai-pack` `tests/encode_rung.rs` does the same for the rendition. The
|
||
second implementation, `encode-conformance/encode_verify.py`, is written from this text: it
|
||
shares no code with `wai-rs` or `wai-pack`, reads the Annex B stream, its access units and its
|
||
parameter sets itself, verifies signatures with WAI's strict Ed25519
|
||
(`claim-conformance/_ed25519.py`) and content hashes with BLAKE3
|
||
(`claim-conformance/_blake3.py`), and mutates copies of the corpus and requires every copy to
|
||
fail.
|
||
|
||
**10.2** *Decoder conformance.* Each corpus stream, and each stream the reference encoders made
|
||
for §10.3, decodes in two independent conforming decoders, a hardware decoder and a software
|
||
one, to the same pictures byte for byte, as `StandardDefined` requires.
|
||
|
||
**10.3** *Measurement (informative).* `wai_encode_measure` encodes a source through one
|
||
interface at a rung, rounds as §8.2, and decodes the result with the platform's decoder to give
|
||
PSNR (luma, each chroma plane, and the 6:1:1 weighted mean of their squared errors) and SSIM of
|
||
luma over 8×8 windows at a step of 4. A figure is of the encoder, the host, its load and its
|
||
counter at the time it was taken; none is compared with a figure taken under another counter.
|
||
|
||
## 11. Relationship to other extensions
|
||
|
||
- [packaging](packaging.md) §7, in its paragraph 7.5, requires a floor made by an encoder that is not a pure function
|
||
of its input to name the encoder and its version; §9 here is how an AVC or HEVC rendition over
|
||
a package's pieces does it. Packaging §7.1's floor remains a capability of SPEC §5's mandatory
|
||
floor or recommended set: an encoded rendition is beside it, not in its place.
|
||
- [energy-measurement](energy-measurement.md) gives the acquisition classes, the declared
|
||
uncertainty and the bracketed marginal (§2, §3); [jwp-receipts](jwp-receipts.md) §2.4.5 gives
|
||
the members and rules §8.1 restates.
|
||
- SPEC §5, *Broadcast and streaming codecs*, registers `wai.video.avc` and `wai.video.hevc`, their
|
||
payload (the Annex B byte stream) and their licence class.
|
||
|
||
## 12. Reasons
|
||
|
||
| reason | step | meaning |
|
||
|---|---|---|
|
||
| `record_malformed` | §7 step 1 | the record does not read as §6.1 says |
|
||
| `bad_key` | step 2 | the signer's key fails SPEC §7's strict rule |
|
||
| `bad_signature` | step 2 | the signature fails it |
|
||
| `encode-capability` | step 3 | not `wai.video.avc` or `wai.video.hevc` |
|
||
| `encode-identity-incomplete` | step 4 | an identity member is blank |
|
||
| `encode-identity-inconsistent` | step 4 | the interface, `hardware`, `library_sha256` and capability disagree |
|
||
| `encode-settings-invalid` | step 5 | a size, rate, bit rate, frame count or `resample` out of range or inconsistent |
|
||
| `encode-plan-invalid` | step 6 | the IDR plan, or the package's pieces, out of order or out of range |
|
||
| `encode-energy-invalid` | step 7 | the figure's members break §8.1 |
|
||
| `encode-energy-coverage` | step 7 | a hardware encode's figure that counts no engine is stated `Whole` |
|
||
| `encode-stream-mismatch` | step 8 | the stream's SHA-256 or length is not the record's |
|
||
| `encode-stream-malformed` | §4.3 items 1 to 3 | not an Annex B stream, NAL units after the last picture that hold no slice, or an access unit mixing IDR and other slices |
|
||
| `encode-frame-count` | §4.3 item 2, step 8 | another number of access units |
|
||
| `encode-keyframe-mismatch` | §4.3 item 3 | an IDR off the plan, a planned picture not an IDR, or another random access point |
|
||
| `encode-parameter-sets` | §4.3 item 4 | parameter sets missing or changed |
|
||
| `encode-stream-unsupported` | §4.3 item 5 | not 8-bit 4:2:0 progressive single-layer, or a picture order type this revision reads not |
|
||
| `encode-stream-reordered` | §4.3 item 6 | the stream reorders pictures |
|
||
| `encode-sps-mismatch` | §4.3 item 7, step 8 | the coded size, profile, level, tier or range is not the record's |
|
||
| `encode-source-mismatch` | step 9 | the source's digest is not the record's |
|
||
| `encode-package-mismatch` | step 10 | the rendition does not match the package, the stream or the record |
|
||
| `encoder-unavailable` | producer | no encoder of the interface for the codec here (not a record reason) |
|
||
| `encoder-refused`, `encoder-failed` | producer | the interface refused the settings, or failed an encode (not record reasons) |
|
||
|
||
## 13. Security considerations
|
||
|
||
- **A record is its signer's statement.** It names what the interface reported; a signer can
|
||
name an encoder that did not run. The stream is checked, not the claim of who made it: a
|
||
relying party that must know the maker trusts the signer, or runs the encode itself.
|
||
- **A runtime-loaded library is code the user chose.** The adapter refuses a library of another
|
||
interface version and states the digest of the file it loaded; it does not check the file
|
||
against any list. A deployer that must hold the library to its distributor's published digest
|
||
compares `library_sha256` with it.
|
||
- **A stream is checked before it is recorded.** §4.3 is applied by the producer before it signs,
|
||
and again by every verifier holding the stream, so a record never names a stream whose pieces
|
||
do not each start at a self-contained IDR access unit.
|