Skip to main content

WAI Extension: LOC Carriage

Mirrored from the canonical text at commit 260cbd09 ().

Status: Draft, tracking its drafts as moq-streaming-format does. Maps a WAI envelope and its receipt onto one object of a track of packaging loc: the Low Overhead Media Container of draft-ietf-moq-loc-04 (20 July 2026), the packaging draft-ietf-moq-msf-01 §4.1 names, over draft-ietf-moq-transport-22. A WAI envelope whose capability’s payload is one chunk of a codec the WebCodecs Codec Registry registers travels as one LOC object: its payload is the LOC Payload, unchanged; its manifest is an Immutable Property, WAI_MANIFEST; its receipt commitment is the WAI_RECEIPT property of moq-streaming-format §5. A subscriber that reads LOC and nothing else hands the payload to its decoder as a chunk; a subscriber that reads WAI rebuilds the envelope byte for byte and checks its receipt against it. No new transport. The reference implementation is the loc module of joule-moq (joule-code/joule-code-rs/crates/joule-moq); the corpus is live-conformance/loc/, and its second implementation is live-conformance/loc_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

A section number of LOC, MSF or MOQT here is one of those revisions. Where a later revision renumbers or renames what this text cites, the citation is what changes (moq-streaming-format §6).

1. Scope

§1.1 Packaging wai (moq-streaming-format §3) carries whole envelopes, and a subscriber that does not read WAI cannot use its objects. Packaging loc carries a codec’s chunks, which any LOC subscriber can decode. This extension lets one track be both: its objects are LOC objects, and a WAI subscriber reads each as the envelope it came from.

§1.2 It maps the capabilities of §3 and no others. A capability whose payload is not one chunk of a registered WebCodecs codec travels as packaging wai.

§1.3 It changes no envelope and no receipt: the envelope a subscriber rebuilds is the one the publisher wrote, byte for byte, and the receipt commits to it exactly as jwp-receipts §2.1 says.

2. LOC, as this extension reads it

§2.1 A LOC object’s MOQ Object Properties hold the LOC Public Properties, and its payload holds the LOC Private Properties followed by the LOC Payload, the “internal data” of a WebCodecs EncodedAudioChunk or EncodedVideoChunk (LOC §2, §2.2). LOC defines Private Properties only with Secure Objects (LOC §3.1.3): an object this extension writes carries none, and a sealed object (confidentiality §4) is opened first and then read as here.

§2.2 The LOC properties (LOC §2.3, §6.1), each at most once on an object:

typenamevaluerule
0x10Timestampvarint
0x08Timescalevarintnot 0
0x09Video Frame Markingbytes1 to 4 bytes (RFC 9626)
0x0CAudio Levelvarintat most 255 (RFC 6464 §3)
0x0DVideo Configbytesnot empty
0x0FAudio Configbytesnot empty

§2.3 MSF §4.1 places one chunk in each object and a group of pictures in one group; MSF §5.2 names the track’s codec by the WebCodecs codec string.

3. Mapped capabilities

§3.1 The capabilities this extension maps, each with the WebCodecs codec whose chunk its payload is:

capabilityWebCodecs codecthe payload, one chunk
wai.audio.opus.packetopus, format opusone Opus packet (RFC 6716 §3)
wai.video.avcavc1 or avc3, Annex B (no description)one access unit as an Annex B byte stream
wai.video.hevchev1 or hvc1, Annex B (no description)one access unit as an Annex B byte stream, of the base layer only
wai.audio.aacmp4a.40, format adtsone ADTS frame

Not mapped: wai.video.vp8 and wai.video.vp9 (their payload is an IVF stream, a chunk is one frame), wai.video.av1 (its payload carries a framing header), wai.audio.flac (a FLAC stream with its STREAMINFO, where a chunk is one frame), wai.video.vvc (the WebCodecs registry does not register VVC), wai.video.mvhevc (a WebCodecs HEVC access unit holds exactly one base-layer picture), and every capability with no WebCodecs codec.

§3.2 One chunk. A writer and a reader hold the payload to the form of its capability, and refuse another with loc_payload_form:

4. Writing an object

§4.1 A publisher writes an envelope (SPEC §2) of a mapped capability as one object:

WAI_MANIFEST has type 0x3A61, from the range MOQT §8.4 leaves to an application’s own use (odd, so its value is bytes). Like WAI_RECEIPT’s 0x3A41, a subscriber reads it only on a track that §4.2 declares. A manifest longer than 65 535 bytes, the largest value MOQT §8.3 allows, is not written (loc_manifest_invalid), and neither is an envelope that is not a WAI1 envelope.

§4.2 The track’s catalog entry declares packaging "loc" and codec (MSF §5.2), and the fields of moq-streaming-format §4: wai_capabilities, holding exactly the mapped capability, and wai_receipts where its objects carry WAI_RECEIPT. A subscriber that reads WAI recognises such a track by wai_capabilities on a loc track.

§4.3 The LOC Timestamp and Timescale are the publisher’s, and nothing in the envelope fixes them; the receipt commits to the envelope, so it does not cover them. A subscriber that needs a signed time for an object reads it from a claim (jwp-receipts §2.4.3).

5. Reading an object

§5.1 A subscriber reading an object of such a track, whose catalog names the capability C, checks, in this order:

  1. at most one Immutable Properties, which parses and holds none of its own: loc_property_invalid;
  2. each LOC property of §2.2 at most once, across both property lists, with its rule: loc_property_invalid;
  3. WAI_MANIFEST and WAI_RECEIPT only inside the Immutable Properties, each at most once: loc_property_invalid; no WAI_MANIFEST: loc_manifest_absent;
  4. the manifest is a JSON object whose wai is of major version 1, whose model_requirement.capability is a string, and whose security.encryption is absent, null or "none": loc_manifest_invalid; that capability is mapped (§3.1): loc_capability_unmapped; and it is C: loc_capability_mismatch;
  5. the payload is one chunk (§3.2): loc_payload_form;
  6. the envelope is WAI1, the manifest’s length as a u32 little-endian value, the manifest, the payload’s length the same way, and the payload (SPEC §2);
  7. where WAI_RECEIPT is present: its value reads and holds to moq-streaming-format §5’s rules on the property alone (property_invalid), and its content_hash is the envelope’s (jwp-receipts §2.1): loc_receipt_mismatch.

§5.2 The envelope a reader rebuilds is then read as any envelope is (SPEC §2 to §4), and its receipt verified as jwp-receipts §3 says, against the group receipt the property names. This extension checks only what §5.1 lists.

6. Refusals

codemeaning
loc_property_invalidthe properties do not parse, a LOC property breaks its rule or is given twice, or WAI_MANIFEST or WAI_RECEIPT is outside the Immutable Properties or given twice
loc_manifest_absentno WAI_MANIFEST
loc_manifest_invalidthe manifest is not a plaintext WAI1 manifest of major version 1 naming a capability, or (writing) the envelope is not a WAI1 envelope or its manifest is longer than 65 535 bytes
loc_capability_unmappedthe manifest’s capability is not one of §3.1
loc_capability_mismatchthe manifest’s capability is not the track’s
loc_payload_formthe payload is not one chunk of its codec (§3.2)
loc_receipt_mismatchWAI_RECEIPT’s content_hash is not the rebuilt envelope’s
property_invalidWAI_RECEIPT itself is refused (moq-streaming-format §5)

7. What this extension does not do

§7.1 It does not change LOC’s payload, define a LOC property beyond WAI_MANIFEST, or restate MOQT’s encoding of properties. It does not map a WAI2 envelope: a multi-rendition envelope is several payloads, and each rendition a publisher sends over LOC is its own track.

8. Conformance

§8.1 live-conformance/loc/vectors.json holds objects of every mapped capability, each with the envelope it carries, its MOQ Object Properties as MOQT -22 encodes them, its payload, its LOC properties and its receipt property; envelopes the writer refuses; and objects the reader refuses, each with its code, every code of §6 given. Its HEVC objects carry access units of format-probe-conformance/ext/hevc/base_64x64.265; its Opus object a 20 ms packet of silence; its AVC and AAC objects fixed bytes in the form of §3.2, which are not decodable.

§8.2 joule-moq’s tests/loc_vectors.rs regenerates the corpus byte for byte and requires every outcome; loc_verify.py, written from this text and sharing no code with joule-moq, decodes every object’s properties, rebuilds every envelope, computes every content hash with BLAKE3 itself, requires every outcome, and requires every mutated copy of the corpus to fail.