open-standards/wai/extensions/platform-encode.md
Transaction Science 19b8efbd99 An encode record states null when nothing was measured, and run keeps its record then
The brief's form: quality is the figures, or null when not measured, and record::body writes null
until with_quality sets them. A record with no such member reads as before and states no figures
either, so every earlier record stands; a quality of any other form is record_malformed. The
corpus case that refused null is now accepted with pictures held and no verdict, and an empty
object takes its place among the malformed spellings. QualityBasis::Absent is NotMeasured.

wai_encode_measure run no longer fails after encoding when a decode gives nothing to measure. It
writes the record with null and says why in quality_not_measured, and --no-decode asks for that.
An Apple-gated test now drives run itself for each codec: a verifier's own decode recomputes
what it signed, with the verdict decoded, and the not-measured path keeps its record.

record::sign writes quality at its section 6.1 place, as its comment says, and the change note no
longer states a count of cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 03:01:50 -05:00

38 KiB
Raw Permalink Blame History

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, and, where its signer decoded the stream, the quality of the pictures it decodes to (§6 to §8). A verifier holds the stream to its digest and its shape; it never re-encodes. Decoding the stream itself and holding the source, it recomputes the quality; pictures handed to it show only that the figures are theirs (§7). 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 §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/, 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 and the quality 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; any claim about an encoder's rate and distortion beyond the quality of one stream (§8.3), which a record states and never compares; 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 §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 §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; a decimal a string of 1 to 39 decimal digits with no leading zero but 0 itself, an integer below 2^128; a signed decimal a decimal, or - and a decimal other than 0, an integer from −2^32 to 2^32. It has exactly these members, at every level, none missing and none added, but quality: a record with no such member, as one written before the member was, reads as a record whose quality is null does, and neither states any figure (§8.3.2). A record that does not hold to this, or a member of another type or encoding (any quality that is neither null nor an object of §8.3's members among them), 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
quality object or null §8.3: the quality of the pictures the stream decodes to, null when the signer did not measure it
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 §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);
  11. with the source and pictures held, where quality is not null: the pictures are source.frames pictures of the settings' width × height, their SHA-256 is quality.decoded_sha256, and each of sse_y, sse_cb, sse_cr and ssim_y_q32 is the figure §8.3 computes from them and the pictures the encoder was handed (encode-quality-mismatch).

Steps 8 to 11 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, the stream to its digest and its shape, and the pictures to their digest.

Step 11 reads pictures, and only a decode ties pictures to a stream: quality.decoded_sha256 is the signer's statement of what the stream decodes to, and a signer can state any figures and supply pictures that give them. So a verdict on a record stating figures also names what they rest on:

verdict when what it says of the figures
decoded step 11 was made over pictures the verifier decoded itself, with a conforming decoder, from the stream step 8 held they are the stream's
handed step 11 was made over any other pictures, whoever made them they are those pictures', and nothing of the stream
stated step 11 was not made they are the signer's statement

A relying party MUST NOT read a figure as the stream's on a verdict other than decoded. A record stating no figures has no step 11 and no such word, whatever pictures are held.

8. Energy and quality

8.1 The figure

energy restates a measured figure with the members jwp-receipts §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 §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 §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.

8.3 Quality

A record whose signer decoded the stream states the quality of the decoded pictures against the pictures the encoder was handed (§4.2) in quality, an object of exactly:

member type meaning
decoded_sha256 32 bytes the SHA-256 of the decoded pictures: each picture's luma plane, then its Cb and Cr planes, row-major with no padding, one picture after another in output order, as §4.1 digests a source
sse_y, sse_cb, sse_cr decimal Σ (aᵢ − bᵢ)² over that plane of every picture, a the samples the encoder was handed and b the decoded ones
ssim_y_q32 signed decimal the structural similarity of the luma planes: staged-measurement §4's, its windows and integers as its paragraph 4.2 gives them, with each plane's samples read as its luma, pooled over every picture as its paragraph 4.5 pools units, one mean over every window of every picture, in Q32 (2^32 is 1)

The decoded pictures are source.frames pictures of settings.width × settings.height, 8-bit 4:2:0. The handed pictures are the source's frames where resample is none, and their wai.resample.two-tap resampling to the rung's size (§4.2) otherwise.

8.3.1 A figure of the stream. Every conforming decoder outputs the same pictures from the stream (§2.4: StandardDefined), and the figures are integers computed from those pictures and the handed ones alone. So the figures a stream gives are the same whichever conforming decoder made the pictures, and a verifier that decodes the stream itself recomputes them exactly (§7, the verdict decoded). A record signs them for that reason. Pictures handed to a verifier show only that the record's figures are theirs: nothing but a decode ties them to the stream (§7, the verdict handed). The time and the energy of an encode are figures of the encoder, the host, its load and its counter, and §8.1, §8.2 and §10.3 state them as such.

8.3.2 A record whose signer did not measure the stream's pictures, because it did not decode the stream or its decode gave no pictures to measure, states quality null: the encode stands, and the record says nothing of its quality. The reference's wai_encode_measure writes the figures from the pictures the platform's decoder makes, or from pictures another conforming decoder made, which it is handed (§10.3). PSNR, 10·log10(255²·N / sse) over a plane's N samples, is derived from the sums for display only, as staged-measurement §4 derives it in its paragraph 4.7, and a sum of 0 has no finite PSNR and shows as exact; a record carries the integers. The figures state the distortion of one stream against the pictures it was made from; this extension compares no figure with another.

8.3.3 A record given figures after it was signed is a new statement: its signer signs it again, and its issued_at is when (§6.1). Another key does not add to a record: the record is its signer's (§6.2).

9. An encode over a package's pieces

9.1 The rendition

An encoded rendition of a package (packaging §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 §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/ 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, the pictures a conforming decoder made from one of them (a fixture too, pinned in decoded.json), and records generated reproducibly over them from a fixed-seed key with a fixed issued_at: accepted cases, among them a record with quality over a resampled rung, one stating null, and records with no such member, and negative controls each refused with its reason, among them that record with each figure one away from the pictures' and with another's pictures; and an encoded rendition of a packaging golden with its objects, accepted and mutated. A case with pictures states how the verifier came by them, decoded or handed, and an accepted record stating figures states §7's verdict. The pinned pictures stand in for a verifier's own decode in a case of decoded, since §10.2 holds them to what a conforming decoder makes of the stream, and a verifier with a decoder may decode the stream instead. The corpus records what pictures handed over cannot show: a record stating the perfect score of the encoder's own input, which its stream does not give, is accepted as handed with that input (handed/, generated) and refused as decoded. wai-rs tests/platform_encode.rs requires the generator to reproduce the corpus byte for byte and every case to get its outcome and its verdict; 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, decodes nothing, resamples the source and recomputes every quality figure 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. 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, or reads the pictures another conforming decoder made, to write the record's quality (§8.3), from which it prints PSNR for display (luma, each chroma plane, and the 6:1:1 weighted mean of their squared errors) and which pictures it read. With no pictures to measure it writes the record with quality null and says why (§8.3.2). Its time and energy figures are informative: each is of the encoder, the host, its load and its counter at the time it was taken, and none is compared with a figure taken under another counter.

11. Relationship to other extensions

  • packaging §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 gives the acquisition classes, the declared uncertainty and the bracketed marginal (§2, §3); jwp-receipts §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
encode-quality-mismatch step 11 the pictures held are not the record's, or a quality figure is not the one they give
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.
  • Quality is the signer's statement until a verifier decodes the stream. A verifier that decodes the stream it holds with a conforming decoder recomputes every figure from what it decoded and the source, and its verdict is decoded (§7). Pictures handed to it, whose digest is decoded_sha256 and which give every figure, show only that the figures are theirs: a signer can state any figures and supply such pictures, so that verdict is handed, and of the stream the figures stay their signer's.
  • 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.