# WAI Extension: Access Services > Status: Draft. Registers the record `wai.access.services`: the access services of a room > session, and figures about them that any verifier recomputes from signed records. Captions > are [timed-text](timed-text.md)'s, unchanged. This extension adds a **quality figure** for a > caption track: its **latency**, the room-clock instant a cue was first shown less the > capture instant of the speech it carries; its **stability**, the revisions after first > display and the words they changed; and its **accuracy**, the word errors of the first > revision shown against a reviewed final revision or a signed reference transcript. Each is > defined in integer arithmetic over the signed revisions, so two verifiers reach the same > numbers, and a figure a vendor states is checked by recomputing it. **Audio description** is > a signed description track, a script of timed descriptions with their mixing instructions > (the programme audio ducked under each) and their voicing, a separate audio track or a voice > the sink synthesizes, each labelled human or machine; it maps onto a document of the Dubbing > and Audio description Profiles of TTML2. **Sign language** is a signing track, a > `wai.avatar.pose` stream or the video of a person signing, whose objects are bound by digest > to the caption revisions they translate, with a reviewer's optional sign-off. A > **completeness claim** states, for a room session, that each access track it declares was > delivered complete and in sync, measured from signed times against the tolerances the texts > below state; a verifier checks it against the records. No new transport. The reference > implementation is the `access_services` module of `wai-rs` (feature `access_services`); the > corpus is [`live-conformance/access/`](../live-conformance/access/), and its second > implementation is `live-conformance/access_services_verify.py`. > Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. > > Texts read for this revision, each pinned to the version named: Directive (EU) 2019/882, > Annex I, Section IV, point (b)(ii), and Article 31(2), from a reproduction of the text as > adopted (the Official Journal could not be fetched for this revision); EN 301 549 V4.1.1 > (2026-09), clauses 4.2.4, 6.5.2, 6.5.3, 7.1.2, 7.1.5, 7.2.1 to 7.2.3, 7.3, 12.4 and 12.5; > Web Content Accessibility Guidelines (WCAG) 2.2, Success Criteria 1.2.2 to 1.2.7; 47 CFR > 79.1(j)(2), (j)(3) and (k)(2), and 47 CFR 79.3(a)(3), as in force on 2026-10-01; Dubbing and > Audio description Profiles of TTML2 (DAPT), W3C Candidate Recommendation Draft of > 2026-06-26 (`CRD-dapt-20260626`), §4.1 to §4.10, §5.2, §5.3, §5.5 to §5.7 and Appendix > H.2.2; EBU Tech 3370, EBU-TT Part 3, version 1.0 (May 2017), §2.3.2 and §2.3.3; and two > statements of the United Kingdom's communications regulator, *Measuring the quality of live > subtitling* (16 October 2013), §1.3, §3.65 to §3.67 and footnote 21, and *Ensuring the > quality of TV and on-demand services* (15 April 2024), §3.3, §5.17, §5.23, §5.25, §5.29, > §6.2 and §6.14. A section number of one of those texts in this extension is that version's, > and is written with the text's name; a bare section number is this extension's. DAPT is a > Candidate Recommendation Draft, whose features may change before it is a Recommendation, > and one that §5.5 uses is at risk there. ## 1. Scope In scope: the records and their encoding (§2); words, and the alignment of two lists of words (§3); the quality figure of a caption track: the sources that place its words on the room clock, a reference transcript, latency, stability, accuracy, and the figure record (§4); audio description: the description record, its script, mixing and voicing, its synchronisation, its DAPT document, and what a player owes its listeners (§5); sign language: the signing record, its binding to caption revisions, and a review (§6); the completeness claim (§7); verification (§8); and what each figure measures in the texts above (§9). Out of scope: a transport; recognising speech, writing descriptions, translating into a sign language and synthesizing speech, which produce the content; grading the severity of an error, which needs a person (§9); decoding a voicing track or a signing track, whose objects this extension names by digest; spoken subtitles and extended audio description, which pause or lengthen the programme; how a sink renders a description or a signer; and what any law requires. Relationship to the core spec: `wai.access.services` is a **record** ([SPEC](../SPEC.md) §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. Relationship to [timed-text](timed-text.md): captions are its live captions, verified as its §4.3 verifies them, and this extension changes nothing in them. The audio description that timed-text §1 leaves out of its scope is this extension's §5. Relationship to [avatars](avatars.md) and [capture-binding](capture-binding.md): a signing track in pose form is a `wai.avatar.pose` world, whose op batches are its objects (avatars §5); a source on a take is placed on the room clock by the take's clock mapping (capture-binding §9.3). Relationship to [synthesis](synthesis.md): the origins this extension's records state are a caption's, which synthesis §1 maps onto its classes. A description voiced or written by a model, and a signing a model generates, is synthesized media in its sense: class `synthesized`, step `generate`, with the operation `speech-synthesis` for a voice, and oversight `fully_autonomous` for origin `machine` or `human_validated` for origin `machine-reviewed` (synthesis §3.1, §3.2). The publisher of such a track signs its origin claim there; this extension's label is the same statement, carried where a description or a signing is read. A review of §6.4 reviews a translation against caption revisions; a review of one exact reconstruction of a generated track, which every exact sink then shows, is synthesis §9.1's. Duties this extension supports: the access services of Directive (EU) 2019/882, Annex I, Section IV(b)(ii) ("subtitles for the deaf and hard of hearing, audio description, spoken subtitles and sign language interpretation … fully transmitted with adequate quality for accurate display, and synchronised with sound and video, while allowing for user control of their display and use"); EN 301 549 V4.1.1 clauses 7.2.1 to 7.2.3, 7.3 and 12.4, beside the caption clauses timed-text §11 answers; WCAG 2.2 Success Criteria 1.2.3, 1.2.5 and 1.2.6, beside the caption criteria 1.2.2 and 1.2.4; and the caption quality standards of 47 CFR 79.1(j). WAI states what its records carry and what a verifier checks. It makes no statement about any regime, and a deployment confirms what a regime asks of it against that regime's text. ## 2. Records Every record of this extension is a JSON object (RFC 8259), read as [jwp-receipts](jwp-receipts.md) §3.1 step 1 reads a claim: RFC 8259's grammar, no byte-order mark, no member named twice in one object, no `\u` escape that leaves a surrogate unpaired, and at most 32 levels of nesting. Its values use the encodings of jwp-receipts §2.4.3: byte strings in lowercase hexadecimal; integers unsigned, at most 2^53 − 1, in plain decimal; no other number and no boolean. The types in this extension's tables are: - `hex32`, `hex64`: 64 or 128 lowercase hexadecimal digits; - `int`: such an integer; - `sint`: a **signed integer**, written as a JSON string: `0`, or an optional `-` then a digit 1 to 9 then any digits, whose value lies in −2^63 to 2^63 − 1; - `string`: a JSON string; `tag`: a string that live-events §4.10 reads as a language tag (checked by the rules of the record's section); - a list of quoted values: a string equal to one of them; - `object`, `array`: as the table under it says; `null`, written where a member may be null. A record has exactly the members its table lists, at every level, none missing and none added, each of its type, and a `kind` that names it. One that is not JSON, or does not have that shape, is `record_malformed`. Every record carries `signer_pubkey` (`hex32`, the Ed25519 key of RFC 8032 that signs it) and `sig` (`hex64`): ``` sig = Ed25519(signer_key, "wai:access\x01" || JCS(record without "sig")) record id = SHA-256(JCS(record)) ``` JCS is RFC 8785, which on these values reduces to what jwp-receipts §2.4.3 states. The signature is verified strictly ([SPEC](../SPEC.md) §7 *Signatures*): a key that fails step 1 or 2 is `bad_key`, and a signature that fails a later step `bad_signature`. A record is named by its record id wherever another names it. A member `room` names the room by its id (live-events §2); a record whose `room` is not the verifier's room is `wrong_room`. Which signers a verifier trusts for each kind of record is its policy. This extension checks what each record states against the events and the other records; a figure's weight comes from its recomputation (§4.7), not from its signer. ## 3. Words and their alignment ### 3.1 Words The **words** of a text are found in two steps: 1. The text is split at every U+0020 and U+000A, and empty pieces are dropped. 2. In each piece, every code point from U+0041 to U+005A is replaced by the one 0x20 above it (ASCII letters to lower case), and code points of the set *P* are removed from its start and its end, repeatedly, until it neither starts nor ends with one. A piece left empty is dropped; the rest are the words, in order. *P* is U+0021 `!`, U+0022 `"`, U+0027 `'`, U+0028 `(`, U+0029 `)`, U+002C `,`, U+002D `-`, U+002E `.`, U+003A `:`, U+003B `;`, U+003F `?`, U+005B `[`, U+005D `]`, U+00A1 `¡`, U+00AB `«`, U+00BB `»`, U+00BF `¿`, U+2013 `–`, U+2014 `—`, U+2018 `‘`, U+2019 `’`, U+201C `“`, U+201D `”` and U+2026 `…`. No other normalisation is made: letters outside ASCII keep their case, and two words are equal when their code points are. The words so found ignore the case of ASCII letters and the punctuation at a word's edges, so an error of capitalisation or of such punctuation is not a word error here (§9). ### 3.2 Alignment The alignment of a **hypothesis** *H* (words *h*₁ … *h*ₘ) to a **reference** *R* (words *r*₁ … *r*ₙ) is the table *D* of integers, for 0 ≤ *i* ≤ *m* and 0 ≤ *j* ≤ *n*: - *D*[0][*j*] = *j*, and *D*[*i*][0] = *i*; - *D*[*i*][*j*] = the least of *D*[*i* − 1][*j* − 1] + *c*, *D*[*i* − 1][*j*] + 1 and *D*[*i*][*j* − 1] + 1, where *c* is 0 when *h*ᵢ = *r*ⱼ and 1 otherwise. Its **end** is a column *j*\*: - a **full** alignment ends at *j*\* = *n*; - a **prefix** alignment ends at the greatest *j* for which *D*[*m*][*j*] is the least value in row *m*: the reference is read as far as the hypothesis reaches, and no further. Its **errors** are *E* = *D*[*m*][*j*\*] and its **reference words** *N* = *j*\*. They are split into substitutions, deletions and insertions by walking back from (*m*, *j*\*) to (0, 0), taking at each step the first of these that holds: 1. *i* > 0, *j* > 0 and *D*[*i*][*j*] = *D*[*i* − 1][*j* − 1] + *c*: to (*i* − 1, *j* − 1), a **substitution** when *c* is 1; 2. *i* > 0 and *D*[*i*][*j*] = *D*[*i* − 1][*j*] + 1: to (*i* − 1, *j*), an **insertion** (a hypothesis word the reference does not have); 3. otherwise: to (*i*, *j* − 1), a **deletion** (a reference word the hypothesis does not have). The substitutions, deletions and insertions add up to *E*. ## 4. Caption quality ### 4.1 The caption track and its cues A **caption track** is the caption events of one principal *P* in a room's stream, verified as [timed-text](timed-text.md) §4.3 verifies them. Its **cues** are the chains of its revisions (timed-text §2): each from its first revision *r*₀ to its last *r*ₖ. A cue is named by the digest of *r*₀'s `caption` event (live-events §3.2). *P*'s captions are **whole** when *P* has at least one cue, the verifier holds every `seq` of *P* from 0 to the last it holds (no delivery gap of *P*, live-events §6.2), and, for every cue, *r*ₖ is final and its annotation is not unknown (timed-text §4.3). They are **resolved** when they are whole and every *r*ₖ has a `duration_ms` above 0, so that each cue resolves (timed-text §4.5). A figure is computed only over whole captions. The **events digest** of *P* is SHA-256 over the digests of *P*'s `caption` events the verifier holds, 32 bytes each, in `seq` order. ### 4.2 Sources A **sources** record places the words of each cue on the room clock: where the speech the cue carries was captured. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-sources"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P*, whose cues it places | | `sources` | array | one entry per cue it places, each an object below | | `signer_pubkey`, `sig` | | §2 | | member of an entry | type | meaning | |---|---|---| | `cue` | `hex32` | the cue, by the digest of its first revision | | `basis` | `"take"`, `"room"` | what `start` and `end` are measured on | | `take` | `hex32` or `null` | for `take`, the take id (capture-binding §3.2); `null` for `room` | | `start`, `end` | `int` | the capture of the cue's first word and of its last: device-clock microseconds of the take, or room-clock microseconds | Its signer is whoever aligned the words to the media: the captioner, or anyone holding the media afterwards. The alignment of a word to the media is the signer's statement; the media's own time is not. On the basis `take`, the instants are positions in the master of a take, and the take's clock mapping places them on the room clock: the offset *o* of the take's opening commitment (capture-binding §9.3), which the capturing device signed while it captured. A **source instant** *d* maps to the room-clock instant *d* + *o*. On the basis `room`, the instants are on the room clock as stated. Rules, in order: 1. `room` is the verifier's: otherwise `wrong_room`. 2. No sources record of *P* comes before it in the order given: otherwise `record_conflict`. 3. Each entry: `take` is a `hex32` exactly when `basis` is `take`, and `start` ≤ `end`; no cue is named twice: otherwise `record_malformed`. 4. *P*'s captions are whole (§4.1): otherwise `stream_incomplete`. 5. Each entry's `cue` names a cue of *P*'s captions: otherwise `source_unknown`. 6. Each entry on the basis `take`: among the events the verifier accepted there is a `take_commit` event whose payload capture-binding §5.2 reads, whose `take` is the entry's take, and which is an opening commitment (`n` 0, `phase` 1, capture-binding §5.3); the first such in stream order is the take's opening commitment, its author *A* and its offset *o*. The take is **derived**: an accepted `control` event whose command is `record_start` and whose `target` is *A* has a digest *s* for which SHA-256 of `wai:capture-take`, 0x01, the room id, *A* and *s* is the take (capture-binding §3.2). And `start` + *o* is not below 0. Otherwise `source_take_unknown`. *P*'s **sources record** is the first sources record of *P* in the order given, and its **reference record** the first reference record (§4.3); a later one of either is `record_conflict`. Who may start a take, and which device captures for which principal, are live-rooms' and capture-binding's checks; this extension uses the take's mapping as capture-binding §9.3 defines it, and its bound, *J*, holds where a take verifier gives the take `single-take` and `aligned` (capture-binding §9.4, §10). ### 4.3 The reference transcript A **reference** record gives, for each cue of *P*, the text a reviewer holds correct. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-reference"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P* | | `entries` | array | one per cue, each `{"cue": hex32, "text": string}` | | `signer_pubkey`, `sig` | | §2 | Rules, in order: `room` is the verifier's (`wrong_room`); no reference record of *P* comes before it in the order given (`record_conflict`); no cue is named twice (`record_malformed`); each text meets timed-text §3.2 rules 1 to 3 (`text_not_in_subset`); *P*'s captions are whole (`stream_incomplete`); each entry names a cue of *P* (`reference_unknown`); and every cue of *P* has an entry (`reference_incomplete`). ### 4.4 Latency The latency of a cue that a sources entry places is *L* = `t`(*r*₀) − *S*, in microseconds, where `t`(*r*₀) is the room-clock instant its first revision was shown from (timed-text §4.1) and *S* is its entry's `start` on the room clock (§4.2). *L* is negative when the caption was shown before the speech began. Over the cues of *P* that the sources record places, *n* of them, with their latencies in ascending order *L*₁ ≤ … ≤ *L*ₙ: - `min` = *L*₁, `max` = *L*ₙ; - `mean` = ⌊(*L*₁ + … + *L*ₙ) / *n*⌋, rounded toward negative infinity; - `p50` and `p95`: the latency at rank ⌈50 × *n* / 100⌉ and ⌈95 × *n* / 100⌉, counting from 1. Each is `null` when *n* is 0. The **class** of the latency is `none` when *n* is 0; otherwise `capture` when every entry is on the basis `take`, and `stated` when any is on the basis `room`. ### 4.5 Stability For one cue, its **revisions** are *k*, the revisions after the first, and its **words changed** the sum, over each revision *r*ᵢ and the one that supersedes it *r*ᵢ₊₁, of the substitutions and insertions of the full alignment of the words of *r*ᵢ (the hypothesis) to the words of *r*ᵢ₊₁ (the reference): the words a viewer saw that the next revision did not keep. A word added at the end, as a partial grows, is a deletion there and is not counted. The track's figures are the sums over its cues of revisions and of words changed, and `cues_corrected`, the number of cues whose words changed is above 0. ### 4.6 Accuracy The reference of a cue is its entry's text in *P*'s reference record, when *P* has one; otherwise the text of *r*ₖ, the cue's final revision. The **class** of the accuracy is: - `transcript` when *P* has a reference record; - `reviewed` when it has none and every cue's *r*ₖ has origin 1 (human) or 3 (machine-reviewed): a person wrote or reviewed the reference; - `self` otherwise: the reference is the machine's own final text, and the figure measures how far its first display was from its own last. Two figures, each the sums over the cues of the counts of §3.2: - `first`: the prefix alignment of the words of *r*₀ to the words of the reference. It counts the errors in what the viewer first saw, against as much of the reference as that revision reaches, so the words a partial has not reached yet are not errors. - `final`: the full alignment of the words of *r*ₖ to the words of the reference, when the class is `transcript`; `null` otherwise. Each is `{"words", "errors", "substitutions", "deletions", "insertions"}`: *N*, *E* and its three parts. ### 4.7 The figure A **quality** record states the figures of *P*'s track. | member | type | meaning | |---|---|---| | `kind` | `"wai-caption-quality"` | | | `room` | `hex32` | the room | | `principal` | `hex32` | *P* | | `events` | `hex32` | *P*'s events digest (§4.1) | | `sources` | `hex32` or `null` | the record id of *P*'s sources record, or `null` when there is none | | `reference` | `hex32` or `null` | likewise, of its reference record | | `cues` | `int` | the number of *P*'s cues | | `latency` | object | `{"class", "n", "min", "max", "mean", "p50", "p95"}`: a string of §4.4, an `int`, and five `sint` or `null` | | `stability` | object | `{"revisions", "words_changed", "cues_corrected"}`, each an `int` | | `accuracy` | object | `{"class", "first", "final"}`: a string of §4.6, an object of §4.6, and one or `null` | | `signer_pubkey`, `sig` | | §2 | Rules, in order: 1. `room` is the verifier's: otherwise `wrong_room`. 2. *P*'s captions are whole (§4.1): otherwise `stream_incomplete`. 3. `events`, `sources` and `reference` are as the table says: otherwise `figure_inputs_mismatch`. 4. Every other member but `kind`, `signer_pubkey` and `sig` equals the figure the verifier computes from §4.4 to §4.6: otherwise `figure_mismatch`. A figure that passes is the figure of exactly those revisions, placed by exactly those sources and measured against exactly that reference. A verifier needs no record of its own to compute a figure: the **figure** of a whole track is the object a quality record would carry, without `kind`, `signer_pubkey` and `sig`. ## 5. Audio description ### 5.1 The description record A **description** record is an audio description track: a script of timed descriptions of what is seen, the mixing of each into the programme audio, and its voicing. Times are media time in milliseconds, on the timeline of the media the descriptions accompany, as timed-text §2 defines it. | member | type | meaning | |---|---|---| | `kind` | `"wai-description"` | | | `lang` | `tag` | the language of the descriptions | | `script` | object | `{"origin", "model"}`: who wrote the descriptions | | `voicing` | object | `{"form", "origin", "model", "track"}`: who or what speaks them | | `events` | array | the descriptions, each an object below | | `signer_pubkey`, `sig` | | §2 | `origin` is `"human"`, `"machine"` or `"machine-reviewed"`, the names timed-text §10.5 gives the origins of a caption, and `model` is a `hex32` naming the model that wrote or spoke the text, or `null`. `form` is `"track"`, the voicing is a separate audio track named by `track` (a `hex32`, the track's digest, live-rooms §6.2), or `"sink"`, the sink synthesizes the voice from the text with the model `model`, and `track` is `null`. | member of an event | type | meaning | |---|---|---| | `id` | `string` | its identifier | | `begin`, `end` | `int` | the interval it describes, the description's time slot | | `represents` | `"visual"`, `"visual.nonText"`, `"visual.text"`, `"visual.text.title"`, `"visual.text.credit"`, `"visual.text.location"`, `"visual.dialogue"` | what of the image it describes: DAPT's content descriptors of the visual image (DAPT §4.7 and Appendix H.2.2) | | `text` | `string` | the description | | `duck` | object | `{"gain", "ramp"}`, each an `int`: the programme audio's gain under the description in thousandths, and the milliseconds it takes to fall to it and to rise back | | `voice` | object or `null` | `{"begin", "end"}`, each an `int`: where the voicing track speaks it; `null` when the form is `sink`, or when the track has not voiced it | ### 5.2 Rules In order; the first that fails names the refusal: 1. `lang` reads as live-events §4.10 reads a tag: otherwise `description_malformed`. 2. `script.model` is a `hex32` exactly when `script.origin` is `machine` or `machine-reviewed`; likewise `voicing.model` for `voicing.origin`; `voicing.track` is a `hex32` exactly when `form` is `track`; and when `form` is `sink`, `voicing.origin` is not `human`: otherwise `description_malformed`. 3. There is at least one event; each `id` is one of `A`–`Z`, `a`–`z` or `_`, then up to 63 of those, `0`–`9`, `.` and `-`; no `id` twice; each `begin` < `end` ≤ 359 999 999; each event's `begin` is at or after the `end` of the event before it (one voice describes one thing at a time): otherwise `description_malformed`. 4. Each `text` meets timed-text §3.2 rules 1 to 3: otherwise `text_not_in_subset`. 5. Each `duck`: `gain` ≤ 1000; `ramp` is 0 when `gain` is 1000 and above 0 otherwise; and 2 × `ramp` ≤ `end` − `begin`. Each `voice`: `null` when the form is `sink`, and otherwise `null` or within its event, the event's `begin` ≤ `voice.begin` < `voice.end` ≤ the event's `end`, since a Script Event's audio is truncated at its end (DAPT §4.9.1): otherwise `description_malformed`. 6. The synchronisation of §5.3: otherwise `description_out_of_sync`. ### 5.3 Synchronisation The programme audio falls to the event's gain from `begin` to `begin` + `ramp`, and rises back from `end` − `ramp` to `end`. The description is **spoken** over the interval between, from *a* = `begin` + `ramp` to *z* = `end` − `ramp`. Of a voiced event (its `voice` not `null`): - `voice.begin` ≥ *a* − 35 and `voice.begin` ≤ *a* + 45; - `voice.end` ≤ *z* + 45. These are DAPT §5.5's bounds on a presentation processor: audio presentation changes no more than 35 ms before the time a document specifies and no more than 45 ms after it, which DAPT derives from EBU R 37's bounds on audio relative to image. A voice that starts before the programme audio has fallen, or runs on after it has risen, is out of those bounds. EN 301 549 V4.1.1 clause 7.2.2 requires the synchronisation to be preserved and states no figure. The **offset** of a voiced event is `voice.begin` − *a*, a signed number of milliseconds; an unvoiced event has none. ### 5.4 Description over dialogue A description is spoken in the pauses of the dialogue (47 CFR 79.3(a)(3): "into natural pauses between the program's dialogue"), though the regulator's 2024 statement accepts describing over dialogue where what is seen matters more (§6.14 there). So this is a figure, not a refusal. Against the dialogue of one or more caption tracks, whose resolved cues (timed-text §4.5) cover the union *U* of their intervals, each from its `start` up to, not including, its `end`, an event's **overlap** is the milliseconds of its spoken interval that lie in *U*: from `voice.begin` up to `voice.end` when it is voiced, and from *a* up to *z* otherwise. The figure is the sum of the overlaps and the number of events whose overlap is above 0. ### 5.5 The DAPT document A description record maps onto one DAPT document, given, for the form `track`, the voicing track's URI `U` and media type `T`, each one to 2048 of the ASCII characters `!` to `~` other than `"`, `&`, `<` and `>` (otherwise `parameter_malformed`). It is written as UTF-8 with U+000A line ends: ``` EVENTS ``` - `LANG` is `lang`. `REPS` is each `represents` value of the events once, in the order each first appears, joined by U+0020: every event's value is then one of them (DAPT §4.1.1). `TYPE` is `asRecorded` for the form `track` and `preRecording` for the form `sink`. - `SO` and `VO` are the script's and the voicing's origins. After `wai:script="SO"` comes ` wai:script-model="M"` when the script has a model, and after `wai:voice="VO"`, ` wai:voice-model="M"` when the voicing has one, `M` the digest in lowercase hexadecimal. - `EVENTS` is, for each event in order: ```

CONTENT

``` `B` and `E` are `begin` and `end` written `HH:MM:SS.mmm` (timed-text §5.1); `RAMP`, `FALL` and `DUR` are `ramp`, `end` − `begin` − `ramp` and `end` − `begin`, written the same way; `G` is the gain: `0` for 0, and otherwise `0.` and its three decimal digits with trailing zeros removed. The two `animate` lines are written only when the gain is below 1000. - `CONTENT` is the text, its lines separated by `
` and `&`, `<` and `>` written `&`, `<` and `>`: - for the form `track` and a voiced event, ``, with `VB` = `voice.begin` − `begin`, `VE` = `voice.end` − `begin`, `CB` = `voice.begin` and `CE` = `voice.end`, each written `HH:MM:SS.mmm`. The voicing track is on the programme's timeline (an audio description stem), so the event's audio is the interval of it that the event's voice states; - for the form `track` and an event not yet voiced, the text alone; - for the form `sink`, `TEXT`. The document's features are DAPT's: a Script Event per description, a `div` with `xml:id`, `begin`, `end` and `daptm:represents` (DAPT §4.3); its Mixing Instructions as `animate` children of the Script Event, which adjust the programme audio during it (DAPT §4.3, §4.10); an Audio Recording by `src` and `type` with `clipBegin` and `clipEnd` (DAPT §4.9.1); and Synthesized Audio by `tta:speak` (DAPT §4.9.2). `ttp:contentProfiles` names the DAPT content profile, and no `ttp:profile` is written (DAPT §5.6). The document's time base is media time, times are clock times without frames (DAPT §5.7), and `daptm:langSrc` is `zxx`, content without an inherent language (DAPT §4.5). Two choices are this extension's: - `src` on `audio`, for an external resource, is at risk in the Candidate Recommendation (its Issue 218). If it does not stay, a later revision of this mapping names the voicing track another way DAPT §4.9.1 keeps. - For the form `sink`, DAPT says a pre-recording script SHOULD NOT contain Audio objects, and an as-recorded script SHOULD NOT contain Synthesized Audio. There is no recording: the sink's voice is the recording. The document is the script it speaks, marked as one, with `tta:speak` saying that it is to be spoken. The attributes in the namespace `tag:transaction.science,2026:wai/access` are foreign vocabulary, which DAPT §5.2 has a processor keep or prune. They carry the origin labels of §5.6, which DAPT has no field for. ### 5.6 Obligations of a WAI player These are requirements on a WAI player that plays audio description or shows sign language. Each cites the provision it answers by identifier; they are engineering requirements, and meeting them is not a statement about any law. 1. **Description can be played.** A player of video with synchronised audio MUST let the listener select an available description track and play it to the default audio output (EN 301 549 V4.1.1 clause 7.2.1; WCAG 2.2 Success Criteria 1.2.3 and 1.2.5, which ask content to provide it). 2. **In sync.** A player that mixes a description MUST apply its mixing instructions and play each voice at the times its event states, within §5.3's bounds (EN 301 549 V4.1.1 clause 7.2.2). 3. **Preserved.** A player or converter that transmits, converts or records a description MUST keep every event's timing, text, mixing instructions, voicing and labels (EN 301 549 V4.1.1 clause 7.2.3): §5.5's document keeps them all. 4. **One operation.** A player whose purpose is video MUST offer turning description on and off as a single operation, at the same level as the volume control (EN 301 549 V4.1.1 clause 7.3), as timed-text §11 item 5 has it for captions. 5. **Labelled.** A player MUST show, with a description whose voicing is `machine` or `machine-reviewed`, that the voice is synthesized, in a way the listener perceives without turning on another feature, as timed-text §11 item 7 has it for a machine caption, and SHOULD let the listener learn whether the script was machine-written. The script's origin and the voicing's are the description's labels, as a caption's origin is its label (timed-text §10.1); a synthesized voice's origin, with the model's digest, is the statement its origin claim makes of it (synthesis §3.1), and a player shows its label as synthesis §10 has it show a synthesized object's. 6. **Signing under the viewer's control.** A player that shows a signing track MUST let the viewer choose to show it, and SHOULD let the viewer choose its size and position (Directive (EU) 2019/882 Annex I Section IV(b)(ii), "user control of their display"); a pose stream leaves both to the sink (§6.1). It MUST show, with a signing track whose origin is `machine` or `machine-reviewed`, that the signing is generated. ## 6. Sign language ### 6.1 The signing record A **signing** record is a sign language track: a `wai.avatar.pose` stream (avatars §1), or a video track of a person signing. Its objects are bound, by digest, to the caption revisions they translate. | member | type | meaning | |---|---|---| | `kind` | `"wai-signing"` | | | `room` | `hex32` | the room | | `lang` | `tag` | the sign language | | `source` | `hex32` | the principal of the caption track it translates | | `form` | `"pose"`, `"video"` | an avatar pose stream, or a video of a signer | | `track` | `hex32` | the track's digest (live-rooms §6.2) | | `origin`, `model` | | as §5.1: `human` for a person signing (filmed, or motion-captured into the pose stream), `machine` for signing a model generated | | `video` | object or `null` | for `video`, `{"width", "height", "rate_num", "rate_den"}`, each an `int`: its picture size and frame rate; `null` for `pose` | | `segments` | array | each an object below | | `signer_pubkey`, `sig` | | §2 | | member of a segment | type | meaning | |---|---|---| | `object` | `hex32` | the content hash of an object of the track (jwp-receipts §2.1): an op batch of the pose world, or a video object | | `start`, `end` | `int` | room-clock microseconds the signing of the object spans | | `revisions` | array of `hex32` | the caption revisions it translates, by the digest of each `caption` event | Pose-equivalence (avatars §4) makes the motion every sink shows the motion in the bound objects, while the appearance is the sink's: a viewer may choose the signer's size, place and appearance, which Directive (EU) 2019/882 Annex I Section IV(b)(ii) calls user control of display, and the binding still holds. ### 6.2 Rules In order: 1. `room` is the verifier's: otherwise `wrong_room`. 2. `lang` reads as a tag; `model` is a `hex32` exactly when `origin` is `machine` or `machine-reviewed`; `video` is `null` exactly for `pose`, and its four numbers are above 0; there is at least one segment, each with `start` < `end` and at least one revision, each segment's `start` at or after the `end` of the segment before it; and no revision is named twice in the record: otherwise `signing_malformed`. 3. `source`'s captions are whole (§4.1): otherwise `stream_incomplete`. 4. Each named revision is a `caption` event of `source` that the verifier holds: otherwise `sign_revision_unknown`. 5. Each named revision is the last of its cue and final, its annotation not unknown: otherwise `sign_revision_not_final`. Signing translates the text a cue resolves to; a partial that a later revision changed is not what the captions say. ### 6.3 Figures - The **offset** of a segment is its `start` less *S* (§4.4) of the earliest cue, by its first revision's `t`, among those whose final revisions it names, when `source`'s sources record places that cue; it has none otherwise. It is a signed number of microseconds: the delay of the signing behind the speech it interprets. - For the form `video`, the **video floor** is `meets` when the picture is at least 320 by 240 in either orientation (the smaller side at least 240 and the larger at least 320) and the frame rate at least 20 a second (`rate_num` ≥ 20 × `rate_den`), and `below` otherwise: the resolution and frame rate EN 301 549 V4.1.1 clauses 6.5.2 and 6.5.3 require of real-time video communication, which its clause 4.2.4 NOTE 3 names for sign language that accompanies audiovisual content. For the form `pose` it is `null`: the sink renders the signer at the size it chooses. ### 6.4 Review A person who reviews the translation signs a **review**: | member | type | meaning | |---|---|---| | `kind` | `"wai-signing-review"` | | | `signing` | `hex32` | the record id of the signing record it reviews | | `scope` | `"translation"` | what was reviewed: the signing against the caption revisions it is bound to | | `verdict` | `"approved"`, `"approved-with-notes"`, `"rejected"` | | | `notes` | `hex32` or `null` | the digest of the reviewer's notes; `null` exactly for `approved` | | `signer_pubkey`, `sig` | | §2 | Rules: `notes` is `null` exactly when the verdict is `approved` (`record_malformed`), and `signing` is the record id of a signing record the verifier holds (`review_subject_unknown`). The review binds the objects by their digests, so, for a pose stream, every viewer whose sink is pose-equivalent sees the motion the reviewer approved. Its vocabulary is synthesis §9.1's review with the scope `translation`; that review binds one reconstruction of a synthesized object, and this one binds a signing record to the caption revisions it translates. ## 7. The completeness claim ### 7.1 The record A **completeness** claim states, for a room session, which access tracks it carried, and that each was delivered complete and in sync. | member | type | meaning | |---|---|---| | `kind` | `"wai-access-completeness"` | | | `room` | `hex32` | the room | | `from`, `to` | `int` | the session, from `from` up to, not including, `to`, in room-clock microseconds | | `pt0` | `int` | the media time of the room's `t0`, in milliseconds (timed-text §2) | | `tracks` | array | the access tracks it declares, each an object below | | `signer_pubkey`, `sig` | | §2 | | service | its members besides `service` | |---|---| | `"captions"` | `principal` (`hex32`), `lang` (`tag`), `latency_mean_max` and `latency_max` (each `int` or `null`, microseconds) | | `"audio-description"` | `record` (`hex32`, a description record's id), `lang` (`tag`) | | `"sign-language"` | `record` (`hex32`, a signing record's id), `lang` (`tag`), `review` (`hex32` or `null`, a review's id), `offset_max` (`int` or `null`, microseconds) | The tracks a claim declares are the availability of access services an electronic programme guide states (EN 301 549 V4.1.1 clauses 12.4 and 12.5), made checkable. A cue of *P* is **in the session** when its first revision's `t` is at or after `from` and before `to`. The session's media interval is from `pt0` + ⌊`from` / 1000⌋ to `pt0` + ⌊`to` / 1000⌋. ### 7.2 Checking the claim 1. `room` is the verifier's: otherwise `wrong_room`. 2. `from` < `to`; at least one track; each track's `lang` reads; no principal is named by two caption tracks, and no record by two tracks: otherwise `claim_malformed`. Then each track in order, the first failure naming the refusal: **Captions**, of principal *P*: 1. *P* has a cue in the session: otherwise `track_missing`. 2. *P*'s captions are resolved (§4.1): otherwise `track_incomplete`. A delivery gap is an event not delivered, and a cue that does not resolve was not finished. 3. Every revision of every cue of *P* in the session has the track's `lang`: otherwise `track_mismatch`. 4. When `latency_mean_max` or `latency_max` is not `null`: *P*'s sources record places every cue of *P* in the session, otherwise `sync_unmeasured`; and over those cues the mean latency (§4.4) is at most `latency_mean_max`, and each latency at most `latency_max`, where each is given: otherwise `out_of_sync`. **Audio description**, of record *D*: 1. *D* is a description record the verifier holds: otherwise `track_missing`. 2. Its `lang` is the track's, and each of its events lies in the session's media interval: otherwise `track_mismatch`. 3. For the form `track`, every event is voiced: otherwise `track_incomplete`. (Each voice is already in sync, §5.3.) **Sign language**, of record *G*: 1. *G* is a signing record the verifier holds: otherwise `track_missing`. 2. Its `lang` is the track's, and its `source` is the principal of a caption track of this claim: otherwise `track_mismatch`. 3. Every cue of `source` in the session has its final revision named by a segment of *G*: otherwise `track_incomplete`. 4. When `review` is not `null`: it is the id of a review the verifier holds (otherwise `review_missing`), which names *G* (otherwise `review_mismatch`) and whose verdict is not `rejected` (otherwise `review_rejected`). 5. When `offset_max` is not `null`: every segment of *G* has an offset (§6.3), otherwise `sync_unmeasured`; and each offset's absolute value is at most `offset_max`, otherwise `out_of_sync`. A claim that passes is **held**. Its **dialogue overlap** is, for each audio description track, §5.4's figure against the resolved cues of the claim's caption tracks, their media times as timed-text §4.5 gives them with the claim's `pt0`. ### 7.3 The tolerances | track | measured from | tolerance | stated by | |---|---|---|---| | captions, live | each cue's first display and its source (§4.4) | a mean of at most 4.5 s across live programming: a recommendation, not a limit per programme | the regulator's 2024 statement, §5.17 and §5.25; its 2013 statement (§3.65 to §3.67) recorded the earlier guidance of at most 3 s | | captions, shown by a player | a cue's time | within 100 ms of it, 20 ms as good practice | EN 301 549 V4.1.1 clause 7.1.2: the player's, timed-text §11 item 2 | | audio description | each voice and its event (§5.3) | 35 ms early to 45 ms late | DAPT §5.5, from EBU R 37 | | sign language | each segment and its source (§6.3) | none stated | no text read for this revision gives a figure; EN 301 549 V4.1.1 clause 4.2.4 NOTE 3 asks only that it be "well synchronized with the spoken content" | A claim states the caption and sign tolerances it is held to (`latency_mean_max`, `latency_max`, `offset_max`); the description tolerance is fixed by §5.3. The captions' mean is computed over the session, while the 2024 statement's is over a provider's live programming taken together; a claim that states 4 500 000 holds one session to it. ## 8. Verification ### 8.1 Inputs A verifier is given the room id; the records of the room's event stream, in stream order (live-events §6); and the records of this extension, as JSON texts, in the order given. ### 8.2 Steps 1. **Events.** Each event record is offered, in stream order, to a verifier of captions ([timed-text](timed-text.md) §4.3), which verifies it as live-events §6 does and the captions in it as timed-text §4.3 does. A refusal is the outcome, at `events` and the record's index, with that extension's reason. At the end of the stream the captions are finished (timed-text §4.3), and the gaps, which are not refusals, are noted. 2. **Reading.** Each record is read (§2), its signature verified and its record id computed, in the order given: a failure is the outcome, at `record` and the record's index. 3. **Rules.** Then each record is checked by its section's rules, kind by kind in this order, and the records of one kind in the order given: sources (§4.2), references (§4.3), descriptions (§5.2), signings (§6.2), reviews (§6.4), quality records (§4.7) and claims (§7.2). So a record that another names has been checked before it. The first failure is the outcome, at `record` and the failing record's index. 4. **Figures.** With no refusal, the outcome is **accept**, with: the figure (§4.7) of every principal whose captions are whole, in the order of each one's first `caption` event in the stream; for each description record, its number of events, the number voiced and each event's offset (§5.3); for each signing record, its number of segments and of revisions named, each segment's offset and its video floor (§6.3); and for each claim, its dialogue overlap (§7.2). ## 9. What the figures measure | figure | what it measures | in the texts read | |---|---|---| | latency: per cue, its mean, its range (`min`, `max`) and two ranks | first display on the room clock, less the capture of the first word the cue carries | the 2013 statement's "average latency of the subtitling (the delay between speech and live subtitling), and the range of latencies" (§1.3(b)); the 2024 statement's mean (§5.17, §5.25); 47 CFR 79.1(k)(2)(vi), "lag between the spoken word supplied by the program origination point and when captions are received at the same program origination point", which the room clock's `t`, stamped as the caption leaves its author, measures; 79.1(j)(2)(ii) and (j)(3)(ii); EBU Tech 3370 §2.3.2's authoring delay, from the subtitler hearing the audio to the tool's output, and its §2.3.3 `ebuttm:authoringDelay`, which a producer may state and this figure measures | | stability: revisions after first display, words changed, cues corrected | what the viewer saw that a later revision changed | the 2024 statement's research finding that subtitles corrected live on air distract (§5.23) | | accuracy: words, errors, and their kinds | word errors against a reference a person wrote or reviewed | 47 CFR 79.1(k)(2)(iv): accuracy is the total words less the errors, over the total words, which is (*N* − *E*) / *N* here, and (k)(2)(v)'s kinds of error, of which this figure counts incorrect and missing words; 79.1(j)(2)(i); the 2013 statement's "number and type of errors" (§1.3(c)); the 2024 statement sets no minimum accuracy (§5.29) | | dialogue overlap | description spoken over captioned dialogue | 47 CFR 79.3(a)(3); the 2024 statement §6.2 and §6.14 | | completeness | every declared track present, every cue finished, every event voiced and every cue signed | Directive (EU) 2019/882 Annex I Section IV(b)(ii), "fully transmitted … synchronised"; 47 CFR 79.1(j)(2)(iii), captioning "from the beginning to the end of the program", which this claim checks over the cues the session holds, not over the speech it does not hold | Not computed here: - the severity of an error, which the error-classification model the 2013 statement's measurement used (its footnote 21) grades minor, standard or serious: that grading needs a person. The alignment gives the errors and the reference words a grader starts from; - errors of capitalisation and of punctuation at a word's edge, which §3.1 removes, and which 47 CFR 79.1(j)(2)(i) counts; - the speed of the captions, and their placement (79.1(j)(2)(iv)); - the quality of a description, of a voice or of a signing, which a person judges, and whose judgement a review records. ## 10. Reasons | reason | raised at | |---|---| | `record_malformed` | §2, §4.2, §4.3, §6.4 | | `bad_key` | §2 | | `bad_signature` | §2 | | `wrong_room` | §2, §4.2, §4.3, §4.7, §6.2, §7.2 | | `record_conflict` | §4.2, §4.3 | | `source_unknown` | §4.2 | | `source_take_unknown` | §4.2 | | `text_not_in_subset` | §4.3, §5.2 | | `reference_unknown` | §4.3 | | `reference_incomplete` | §4.3 | | `stream_incomplete` | §4.2, §4.3, §4.7, §6.2 | | `figure_inputs_mismatch` | §4.7 | | `figure_mismatch` | §4.7 | | `description_malformed` | §5.2 | | `description_out_of_sync` | §5.2, §5.3 | | `parameter_malformed` | §5.5 | | `signing_malformed` | §6.2 | | `sign_revision_unknown` | §6.2 | | `sign_revision_not_final` | §6.2 | | `review_subject_unknown` | §6.4 | | `claim_malformed` | §7.2 | | `track_missing` | §7.2 | | `track_incomplete` | §7.2 | | `track_mismatch` | §7.2 | | `sync_unmeasured` | §7.2 | | `out_of_sync` | §7.2 | | `review_missing` | §7.2 | | `review_mismatch` | §7.2 | | `review_rejected` | §7.2 | An event record that [live-events](live-events.md) or [timed-text](timed-text.md) refuses keeps that extension's reason. ## 11. Conformance [`live-conformance/access/`](../live-conformance/access/) holds the corpus, written from fixed seeds by `wai_access_services_vectors gen`: `vectors.json`, and each description record's DAPT document as a file of its own, `NAME.dapt.ttml`. Each vector is a room's event stream and the records of this extension, with the outcome §8 gives them and, when accepted, every figure. It has: - **accepted sessions**: a lesson with a human captioner, a machine captioner whose partials are revised to final, a capture take placing the machine captions' words, a reference transcript, a quality record for each track, a description voiced on a separate track, a motion-captured signer bound to the human captions with an approved review, and a claim over all of them held to the 2024 statement's mean; a session whose machine captions a person reviews, with a description the sink voices by a pinned model, the video of a signer, and a claim without latency bounds; and a session with no sources, whose latency class is `none`; - **negative controls**, each a copy of an accepted session with one defect, and each refused with its reason: a latency figure misstated, a broken revision chain, a sign track bound to a revision a later one superseded and to a revision the stream does not hold, a description spoken late and one spoken early, a claim over a track the verifier does not hold, and one for every other reason of §10. A conforming verifier gives every vector the outcome, the reason, the place and every figure the corpus states, and writes every DAPT document byte for byte. `wai_access_services_vectors verify` and the test `tests/access_services.rs` check that every file regenerates byte for byte, that no other file is there, that `wai-rs` gives every vector its outcome, that every reason of §10 refuses a vector, and that §10 lists exactly the reference's codes. `live-conformance/access_services_verify.py` is the second implementation, written from this text: it shares no code with `wai-rs`; it verifies events and captions with the timed-text corpus's second implementation (`timed_text_verify.py`) and signatures with WAI's strict rule (`claim-conformance/_ed25519.py`); it computes every figure, writes every DAPT document and parses each with an XML parser; and it requires every mutated copy of the corpus to fail. ## 12. Security and privacy considerations - Every figure is recomputed from signed records. A figure states nothing a verifier takes on trust; a quality record's signer vouches only that it computed the figure, and a verifier that recomputes it needs no trust in the signer. - A source places words on the room clock as its signer states. On a take, the media's time is the device's signed commitment and cannot be moved; where a word lies in the media is the aligner's statement, attributable to it by its signature, and checkable by anyone holding the master. A source on the basis `room` places the words by the signer's word alone, and the latency's class says so (`stated`). - A take's opening commitment is the device's, but a principal that is not a capturing device can sign one too. This extension requires the take to be derived from a `record_start` addressed to the commitment's author; that the author was permitted to capture, and for whom, is what a take verifier and a room verifier check (capture-binding §9, live-rooms §9). - `t` is its author's statement of the room clock (live-events §11). A captioner who stamps its captions early lowers its latency, and the stamp is signed: the room's relays see when each event arrived, and a later extension's per-hop receipts witness it. - A reference transcript is its reviewer's. The accuracy's class names what the reference is; a verifier's policy decides whose transcripts it accepts. - Labels are their producers' statements, as timed-text §14 says of captions. A description or a signing labelled human that a model produced misleads its audience, and the signature makes that attributable. - A signing track's binding names caption revisions by digest. It shows what the signing translates; it does not show that the translation is correct, which a review states. - A sources record ties a cue to a position in a recording, and a signing record ties a track to a person who signs. Both persist with the session's records, as the events do.