open-standards/wai/extensions/capability-constraints.md
Transaction Science f7d55b28a5 wai: the refresh rate a page measures, stated exactly; wording
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22 13:53:23 -04:00

195 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: Capability Constraints
> Status: Draft. Makes capability dispatch (WAI SPEC.md §4) a **precondition
> check** rather than a binary claim, by ingesting the constraint-set grammar and
> register pattern the broadcast-IP world already ratified. No transport, no
> registry service, no negotiation protocol — it evaluates values WAI already
> carries. A sink also declares what its display presents — resolution, refresh
> rate, bit depth, dynamic range and physical size — and no rendition above that is
> sent to it (§4); the receipt of each segment records how its rendition matched.
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
## 1. The problem: support is not binary
WAI dispatch treats a capability as present or absent: a sink either claims
`cap` or does not. Real sinks are not like that. A decoder supports a codec *to
a level* — up to a resolution, a frame rate, a bit depth, a profile. "This
capability, but only to 4K60 10-bit, this profile" is the normal case and is
currently unexpressible.
The consequence is that partial support fails at **decode time** rather than at
**selection time**. A sink advertises the capability, receives an object it
cannot actually handle, and fails after committing to the bytes. Worse, WAI's
rendition selection has nothing to evaluate, so it defers to a deployer policy
the specification never states.
## 2. Constraint sets
A **constraint set** is an ORed set of constraints over named parameters, each
constraint being an enumeration, a minimum, a maximum, or a combination. A sink
satisfies a capability when it advertises `cap` **and** the object's declared
parameters satisfy at least one of the registered constraint sets for `cap`.
This grammar is deliberately not invented here. It is the shape AMWA's Receiver
and Sender Capabilities specifications (BCP-004-01 and BCP-004-02, both v1.0.0)
use, with parameters drawn from a public register rather than a bespoke schema
per implementation. WAI ingests the **grammar and the register pattern**, not the
transport or the discovery model those specifications sit in.
- A capability registration **MAY** publish one or more constraint sets. A
capability with none is unconstrained, which is the current behaviour and
remains valid.
- A sink **MUST** evaluate constraint sets **before** dispatch, not during it.
The point is to move the failure earlier; a check performed at decode has
bought nothing.
- A sink **MUST NOT** advertise a capability whose constraint sets it cannot
evaluate. Advertising a capability while ignoring its constraints is the
binary behaviour this extension exists to replace.
- Where an object's declared parameters satisfy no set, the sink **MUST** treat
the capability as unsupported for that object — not as a decode error, and not
as a reason to attempt it anyway.
## 3. Parameters and the register
Parameters are named in a register, so two implementations constrain the same
property under the same name. WAI adopts the pattern; it does not adopt the
existing register wholesale, for a reason worth stating plainly:
**The ratified registers are broadcast-essence-shaped.** They cover media type,
frame rate, colour sampling, bit depth, codec profile — which serves WAI's
conventional media capabilities and nothing else. There are no entries for the
neural, quantum, world or haptic families. Those register entries **WAI must
author itself**, and until it does, those capabilities remain unconstrained under
§2, which is the honest state rather than a pretended one.
- A parameter name **MUST** resolve in a register. An implementation-local name
is not a constraint anyone else can evaluate.
- WAI-authored parameters **MUST** be namespaced so they cannot collide with
entries authored elsewhere.
### 3.1 The register this revision holds
WAI's parameters are named under the prefix `wai:`. Each value is an integer or a
string, so every evaluation is exact; a rate that would be a rational elsewhere is an
integer in a stated unit here.
| parameter | value | what it is |
|---|---|---|
| `wai:codec` | string | the codec string a decoder is configured with |
| `wai:frame_width` | integer | the coded width, in samples |
| `wai:frame_height` | integer | the coded height, in samples |
| `wai:frame_rate_milli` | integer | frames per 1000 seconds |
| `wai:bit_depth` | integer | bits per sample |
| `wai:chroma` | string | the chroma sampling: `4:2:0`, `4:2:2`, `4:4:4` or `4:0:0` |
| `wai:sample_rate` | integer | audio samples per second, per channel |
| `wai:channel_count` | integer | audio channels |
| `wai:bitrate` | integer | bits per second |
| `wai:transfer` | integer | the video's transfer characteristics, as a `TransferCharacteristics` code point of the coding-independent code points (ISO/IEC 23001-8, as `draft-pantos-hls-rfc8216bis-22` §4.4.6.2 cites it): 1, 6, 13, 14 and 15 are standard dynamic range, 16 is PQ and 18 is HLG |
The families §3 names (neural, quantum, world, haptic) have no entries yet, and stay
unconstrained until they do. The form a page evaluates constraint sets in, and their
evaluation, are [browser-sink](browser-sink.md) §5; its reference implementation is
the `browser_sink` module of `wai-rs`.
## 4. Display capability
Bits a panel cannot present are spent and never seen: pixels above its resolution, pictures
above its refresh rate, bits per sample above its depth, a dynamic range above its own. A
sink states what its panel presents in a **display declaration**, and a rendition above it is
not sent. The declaration is a statement the sink makes once to whatever chooses its
renditions, and makes again when its panel changes; nothing is exchanged beyond it.
### 4.1 The declaration
| member | value | what it is |
|---|---|---|
| `long` | integer, at least 1 | the panel's longer side, in its own pixels |
| `short` | integer, from 1 to `long` | its shorter side |
| `refresh_milli` | integer, at least 1 | the most pictures it presents per 1000 seconds; for a panel whose rate varies, its highest |
| `bit_depth` | integer, 1 to 16 | the bits per colour component it presents |
| `hdr` | boolean | whether it presents the range of the high-dynamic-range transfers, PQ and HLG, above standard dynamic range's |
| `long_mm`, `short_mm` | integer, 0 or more | its physical size along its longer and shorter sides, in millimetres; 0 where the sink does not know it |
The sides are stated without orientation, so a sink that turns its panel declares nothing
new. A native host reads every member from its platform's description of the display the
stream is presented on. A page reads them through the interfaces it has:
- `long` and `short`: CSSOM View's `screen.width` and `screen.height`, which are in CSS
pixels, each times `devicePixelRatio`, rounded to the nearest integer;
- `bit_depth`: ⌊`screen.colorDepth` / 3⌋, which is 8 where the user agent does not say
(CSSOM View has `colorDepth` return 24 when it does not know the depth or withholds it);
- `hdr`: true when the media query `(video-dynamic-range: high)` or `(dynamic-range: high)`
matches (Media Queries Level 5 §6.5, §8.2);
- `refresh_milli`: no interface states it, so a page measures it: 1000 times the whole
number of pictures per second nearest to 10⁶ divided by the median of at least 60 intervals,
in microseconds, between consecutive animation frames (of an even count, the upper of the
two middle ones);
- `long_mm` and `short_mm`: 0, since a page has no interface for them.
Texts read for this section: CSSOM View Module, W3C Working Draft 16 September 2025, §2.1
and §4.3 (`Screen`, `devicePixelRatio`); Media Queries Level 5, W3C Working Draft
19 February 2026, §6.5 and §8.2; `draft-pantos-hls-rfc8216bis-22` §4.4.6.2 (`RESOLUTION`,
`FRAME-RATE`, `VIDEO-RANGE`).
### 4.2 The match
A rendition is read through the parameters of §3.1: `w` = `wai:frame_width`, `h` =
`wai:frame_height`, `r` = `wai:frame_rate_milli`, `d` = `wai:bit_depth` and `x` =
`wai:transfer`, with `x` = 1 where the rendition declares none (an HLS variant stream without
`VIDEO-RANGE` is standard dynamic range, rfc8216bis-22 §4.4.6.2). Its **exceedances** against
a declaration are the bits:
| bit | name | set when |
|---|---|---|
| 0 | `resolution` | max(`w`, `h`) > `long`, or min(`w`, `h`) > `short` |
| 1 | `refresh` | `r` > `refresh_milli` |
| 2 | `bit_depth` | `d` > `bit_depth` |
| 3 | `dynamic_range` | `x` is 16 or 18, and `hdr` is false |
A rendition is **within** the declaration when its exceedances are 0. Of the renditions a
sender offers a sink (its **ladder**), the **admissible** ones are those within the
declaration; where none is, the least rendition alone is admissible. The **least** is the
first in the order of (`w` × `h`, `r`, `d`, 1 where `x` is 16 or 18 and 0 otherwise),
ascending, and of two equal there, the one the ladder lists first. A panel smaller than every
rendition is still served, by the least of them.
The **match** of a rendition is its exceedances, with bit 4, `none_within`, set when no
rendition of the ladder is within the declaration. It is what a receipt records
([stream-termination](stream-termination.md) §3.4).
- A sender that holds a sink's declaration **MUST NOT** send it a rendition that is not
admissible. A sink that chooses its own renditions — an HLS or DASH player's selection, a
MoQ subscriber's choice of track — **MUST NOT** request one that is not admissible.
- A sink **SHOULD** declare its panel to whatever chooses its renditions, before the first
is chosen, by a means this revision does not fix, and **MUST** declare it again when the
panel changes (a window moved to another display, its dynamic range switched).
- The physical size bounds nothing in this revision: how much of a resolution a viewer
resolves depends on how far away the viewer is, which a sink does not know. It is
declared and recorded.
- A receipt of a segment records the match of its rendition against the declaration in
effect when it was received, and a verifier refuses a segment of a rendition that was
not admissible (stream-termination §6.1, `rendition_above_panel`).
The check is made before dispatch, as §2's is, and like §2's an unmet declaration is not a
decode error: the rendition is not chosen.
## 5. What this is not
- It is **not** a negotiation protocol. There is no pre-flight exchange, no
controller, and no registry service. The display declaration of §4 is a statement
the sink makes, which a sender evaluates; nothing answers it. Capability-plus-budget binding before
execution is already owned by JCP, and duplicating it here would put two
mechanisms in the tree for one job.
- It is **not** a transport concern. Constraint evaluation reads values the
object already declares.
- It is **not** an energy mechanism. A constraint set says what a sink can
decode, and a display declaration what it can present; neither says what it
should spend. What a rendition above the panel would have cost is not claimed
as saved by this text.
## 6. Framing
The grammar is thin — close to a JSON-Schema subset — and that is fine. The value
is not expressiveness; it is that a ratified, register-backed way of saying
"supported, to here" already exists, and WAI's dispatch, which has only
"supported", gains a range by adopting it.