wai: holdings — which objects of which groups a node holds now, for its neighbours #595
493 changed files with 44013 additions and 24 deletions
80
.github/workflows/wai-conformance.yml
vendored
80
.github/workflows/wai-conformance.yml
vendored
|
|
@ -257,6 +257,11 @@ on:
|
|||
# tests/bearer_receipts.rs requires this extension's §10 to list the codes the reference
|
||||
# reports, and bearer_verify.py reads that table.
|
||||
- 'wai/extensions/bearer-receipts.md'
|
||||
# tests/carousel.rs requires §9 to list the reference's codes and Appendix A to be its table
|
||||
# of K', and carousel_verify.py reads both.
|
||||
- 'wai/extensions/carousel.md'
|
||||
# tests/holdings.rs requires §7 to list the reference's codes, and holdings_verify.py reads it.
|
||||
- 'wai/extensions/holdings.md'
|
||||
# tests/exploration.rs requires this extension's §13 to list the codes the reference
|
||||
# reports and its §3.2 table to name the reference's descriptors, exploration_verify.py
|
||||
# reads §13, and the registry's wire identifiers test reads its sections.
|
||||
|
|
@ -316,6 +321,9 @@ on:
|
|||
# format-probes' tables; and the registry's wire identifiers test reads projection.md,
|
||||
# loc-carriage.md and delivery-telemetry.md (whose §6.6 cmsd_read_verify.py reads).
|
||||
- 'wai/extensions/format-probes.md'
|
||||
# tests/identify.rs requires media-identify §6 and §7 to list the reference's signatures,
|
||||
# and identify_verify.py reads those tables and the capabilities of its §5.1.
|
||||
- 'wai/extensions/media-identify.md'
|
||||
- 'wai/extensions/projection.md'
|
||||
- 'wai/extensions/loc-carriage.md'
|
||||
- 'wai/extensions/delivery-telemetry.md'
|
||||
|
|
@ -589,7 +597,7 @@ jobs:
|
|||
working-directory: wai/wai-rs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for f in neural_int video_int audio_int splat_int staged stage_measure telemetry http format_probe; do
|
||||
for f in neural_int video_int audio_int splat_int staged stage_measure telemetry http format_probe identify; do
|
||||
cargo check --lib --no-default-features --features "$f"
|
||||
done
|
||||
# The decode bench prints its machine line from bench_env; no other step builds it.
|
||||
|
|
@ -606,6 +614,8 @@ jobs:
|
|||
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features telemetry
|
||||
# And the header probes: a browser reads a payload's parameters the same way.
|
||||
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features format_probe
|
||||
# And identification: a browser says what a dropped file is the same way.
|
||||
cargo build --lib --target wasm32-unknown-unknown --no-default-features --features identify
|
||||
# carriage: every registered capability round-trips byte-exact. cli_wrap_intent and
|
||||
# cli_registry run the `wai` binary, which builds without the codecs feature: the
|
||||
# intent rule of `wai wrap`, its refusal of candidate capabilities (SPEC §5), its media
|
||||
|
|
@ -1182,7 +1192,7 @@ jobs:
|
|||
# No debuginfo, for the reason given at cargo-classes: this job ran out of the VM's disk
|
||||
# mid-link beside two others (#550).
|
||||
CARGO_PROFILE_DEV_DEBUG: '0'
|
||||
WAI_FEATURES: key_discovery,http,video_manifest,stage_measure,live_events,live_room,capture_take,live_compose,live_record,timed_text,access_services,live_metering,energy_steering,live_moderation,dred_exact,live_ads,live_provenance,synthesis,synthesis_mark,neural_int,exact_upscale,robustness,ai_usage,contribution,settlement,browser_sink,receipt_claims,takedown,influence,direction,co_performance,age_assurance,bearer_receipts,exploration,co_watch,telemetry_aggregate,exact_tonemap,splat_exact,live_watermark,derive_edge,sink_energy,key_release,stitched_presentation,prior_wire,scene_map,avatar_face,receipt_ledger,stream_audit,fleet,native_sink,platform_encode,remote_desktop,stream_termination,live_world,format_probe,live_messaging,conform_result,revenue_share,opportunity_report,channel_lineup,route_record
|
||||
WAI_FEATURES: key_discovery,http,video_manifest,stage_measure,live_events,live_room,capture_take,live_compose,live_record,timed_text,access_services,live_metering,energy_steering,live_moderation,dred_exact,live_ads,live_provenance,synthesis,synthesis_mark,neural_int,exact_upscale,robustness,ai_usage,contribution,settlement,browser_sink,receipt_claims,takedown,influence,direction,co_performance,age_assurance,bearer_receipts,exploration,co_watch,telemetry_aggregate,exact_tonemap,splat_exact,live_watermark,derive_edge,sink_energy,key_release,stitched_presentation,prior_wire,scene_map,avatar_face,receipt_ledger,stream_audit,fleet,native_sink,platform_encode,remote_desktop,stream_termination,live_world,format_probe,identify,live_messaging,conform_result,revenue_share,opportunity_report,channel_lineup,route_record,carousel,holdings
|
||||
WAI_PQ_FEATURES: synthesis,pq_signatures
|
||||
WAI_RELEASE_FEATURES: dred_exact,synthesis_mark,robustness,avatar_face
|
||||
steps:
|
||||
|
|
@ -1252,12 +1262,12 @@ jobs:
|
|||
--bin wai_http_vectors --bin wai_stage_measure --bin wai_browser_sink_vectors \
|
||||
--bin wai_claim_vectors --bin wai_jwp_vectors --bin wai_signer_vectors \
|
||||
--bin wai_sink_energy_vectors --bin wai_stitched_presentation \
|
||||
--bin wai_stream_termination_vectors --bin wai_format_probe --bin wai_conform_result_vectors \
|
||||
--bin wai_stream_termination_vectors --bin wai_format_probe --bin wai_identify_vectors --bin wai_conform_result_vectors \
|
||||
--bin wai_channel_lineup_vectors
|
||||
for b in wai_http_vectors wai_stage_measure wai_browser_sink_vectors \
|
||||
wai_claim_vectors wai_jwp_vectors wai_signer_vectors \
|
||||
wai_sink_energy_vectors wai_stitched_presentation \
|
||||
wai_stream_termination_vectors wai_format_probe wai_conform_result_vectors \
|
||||
wai_stream_termination_vectors wai_format_probe wai_identify_vectors wai_conform_result_vectors \
|
||||
wai_channel_lineup_vectors; do
|
||||
cp "${CARGO_TARGET_DIR:-target}/debug/$b" "$RUNNER_TEMP/wai-bins/union/"
|
||||
done
|
||||
|
|
@ -1273,12 +1283,12 @@ jobs:
|
|||
--test live_moderation --test live_ads --test ad_presentation --test live_provenance \
|
||||
--test synthesis --test exact_upscale --test cli_derive --test ai_usage --test contribution --test settlement \
|
||||
--test browser_sink --test takedown --test influence --test direction --test co_performance \
|
||||
--test age_assurance --test bearer_receipts --test exploration --test co_watch \
|
||||
--test age_assurance --test bearer_receipts --test carousel --test holdings --test exploration --test co_watch \
|
||||
--test telemetry_aggregate --test exact_tonemap --test splat_exact --test live_watermark \
|
||||
--test derive_edge --test det_cost_bin --test sink_energy --test key_release \
|
||||
--test stitched_presentation --test prior_wire --test scene_map --test receipt_ledger \
|
||||
--test stream_audit --test fleet --test native_sink --test platform_encode --test remote_desktop \
|
||||
--test stream_termination --test live_world --test format_probe --test live_messaging \
|
||||
--test stream_termination --test live_world --test format_probe --test identify --test live_messaging \
|
||||
--test conform_result --test revenue_share --test opportunity_report --test channel_lineup --test route_record
|
||||
cargo test --release --no-run --no-default-features --features "$WAI_RELEASE_FEATURES" \
|
||||
--test dred_exact --test dred_exact_measure --test synthesis_mark --test robustness \
|
||||
|
|
@ -2337,6 +2347,47 @@ jobs:
|
|||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --test bearer_receipts)
|
||||
python3 -I live-conformance/bearer_verify.py live-conformance/bearers
|
||||
python3 -I claim-conformance/claim_verify.py bearer live-conformance/bearers/vectors.json
|
||||
# wai.bearer.carousel (extensions/carousel.md): the reference module's unit tests (each code's
|
||||
# bounds and division into source blocks, K', the passes of advance and repeat, plans, and the
|
||||
# corpus reaching every stated outcome and failing a changed one) and tests/carousel.rs
|
||||
# (vectors.json regenerates byte for byte from its fixed seeds, wai-rs gives every case its
|
||||
# stated outcome, §9 lists exactly the reference's codes, Appendix A is its table of K',
|
||||
# jwp-receipts §3.1 lists the class codes at their steps, K' and Partition[] agree with an
|
||||
# independent RFC 6330 implementation for every block size, and each RaptorQ object a case
|
||||
# holds available decodes with that implementation from what a receiver within the case's
|
||||
# loss bound holds); then the corpus's second implementation, carousel_verify.py, written
|
||||
# from the extension's text and sharing no code with wai-rs, which verifies groups and
|
||||
# objects with jwp_verify.py and claims through steps 1-5 with claim_verify.py, writes the
|
||||
# context checks, the availability and the plans itself, reads §9 and Appendix A, requires
|
||||
# every mutated copy of the corpus to fail, and drops each of its own checks in turn; run
|
||||
# isolated (-I). Last, claim-conformance's own verifier reads every claim of the corpus
|
||||
# through jwp-receipts §3.1 steps 1 to 4 and requires the stated outcome.
|
||||
- name: carousel schedule · wai-rs and carousel_verify.py
|
||||
working-directory: wai
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --lib -- carousel:: claim_receipt::schedule::)
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --test carousel)
|
||||
python3 -I live-conformance/carousel_verify.py live-conformance/carousel
|
||||
python3 -I claim-conformance/claim_verify.py carousel live-conformance/carousel/vectors.json
|
||||
# wai.delivery.holdings (extensions/holdings.md): the reference module's unit tests (an entry's
|
||||
# one form, a record read back and every rule refusing, queries and a plan, and the corpus
|
||||
# reaching every stated outcome and failing a changed one) and tests/holdings.rs (vectors.json
|
||||
# regenerates byte for byte from its fixed seeds, wai-rs gives every case its stated outcome,
|
||||
# and §7 lists exactly the reference's codes); then the corpus's second implementation,
|
||||
# holdings_verify.py, written from the extension's text and sharing no code with wai-rs, which
|
||||
# verifies group receipts with jwp_verify.py and signatures with the shared _ed25519.py, writes
|
||||
# the record, its verdict, queries and plans itself, reads §7, requires every mutated copy of
|
||||
# the corpus to fail and drops each of its own checks in turn; run isolated (-I).
|
||||
- name: holdings · wai-rs and holdings_verify.py
|
||||
working-directory: wai
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --lib -- holdings::)
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --test holdings)
|
||||
python3 -I live-conformance/holdings_verify.py live-conformance/holdings
|
||||
# wai.media.exploration (extensions/exploration.md): the reference module's unit tests (the
|
||||
# descriptors of flat, single-row and checkerboard pictures, grid bins at both ends, the
|
||||
# sample distance's zero, the taste score, derived prompts, and a schedule that replays and
|
||||
|
|
@ -2729,6 +2780,21 @@ jobs:
|
|||
bash format-probe-conformance/fetch.sh "$fetched"
|
||||
(cd wai-rs && "$RUNNER_TEMP/wai-bins/union/wai_format_probe" verify ../format-probe-conformance --fetched "$fetched")
|
||||
python3 -I format-probe-conformance/format_probe_verify.py format-probe-conformance --fetched "$fetched"
|
||||
# Media identification (extensions/media-identify.md): wai-rs's unit tests (hostile bytes
|
||||
# through every walk, what a file reads as its bytes) and tests/identify.rs (the corpus
|
||||
# regenerates byte for byte with every result; the extension's tables; `wai identify`); then
|
||||
# identify_verify.py, written from the text and sharing no code with wai-rs but
|
||||
# format_probe_verify.py's probes, which gives every file its whole result, holds ext/ to
|
||||
# what an independent prober read of it, and requires every mutated copy to fail.
|
||||
- name: media identification · wai-rs and identify_verify.py
|
||||
working-directory: wai
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --lib identify::)
|
||||
(cd wai-rs && cargo test --no-default-features --features "$WAI_FEATURES" --test identify)
|
||||
(cd wai-rs && "$RUNNER_TEMP/wai-bins/union/wai_identify_vectors" verify ../identify-conformance)
|
||||
python3 -I identify-conformance/identify_verify.py identify-conformance
|
||||
# CMSD reading (extensions/delivery-telemetry.md §6.6): the second implementation, written
|
||||
# from that text and RFC 9651 with its own field parser, reads every field of
|
||||
# telemetry-conformance/cmsd-read/ and every field expected/cmsd.json states, and requires
|
||||
|
|
@ -2782,7 +2848,7 @@ jobs:
|
|||
capture_take live_compose live_record timed_text access_services live_metering energy_steering \
|
||||
live_moderation dred_exact live_ads live_provenance synthesis synthesis_mark neural_int \
|
||||
exact_upscale robustness ai_usage contribution settlement browser_sink browser_sink,receipt_claims \
|
||||
takedown influence direction co_performance age_assurance bearer_receipts exploration co_watch \
|
||||
takedown influence direction co_performance age_assurance bearer_receipts carousel holdings exploration co_watch \
|
||||
telemetry_aggregate exact_tonemap splat_exact live_watermark derive_edge pq_signatures \
|
||||
synthesis,pq_signatures sink_energy key_release stitched_presentation prior_wire \
|
||||
prior_wire,neural_int scene_map avatar_face receipt_ledger stream_audit fleet native_sink \
|
||||
|
|
|
|||
|
|
@ -6,6 +6,80 @@ wire format may change and such changes are recorded here.
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Who holds which objects near a receiver
|
||||
|
||||
- **Holdings** ([extensions/holdings.md](extensions/holdings.md)), the record `wai.delivery.holdings`:
|
||||
a node's signed record (`WHD1`) of which objects of which groups it holds now, for its neighbours.
|
||||
Per group, its link hash, its leaf count and either `whole` or a bitmap, in one canonical form,
|
||||
with the instant it states them at and the one it expires at, signed under SPEC §7 *Signatures*.
|
||||
Exact, so a neighbour is never sent after an object its holder lacks.
|
||||
- **What a verifier derives**: a record held to an instant and to the group receipts it has (one
|
||||
misstated leaf count refuses it whole), a holder's latest record standing and two of one instant
|
||||
that differ set aside, whether a record holds an object, and an asker's plan: each wanted
|
||||
object's holders in the asker's order, and the objects no neighbour holds, which come from the
|
||||
origin. An object a neighbour serves is verified under its receipts like any other.
|
||||
- **The corpus** [`live-conformance/holdings/`](live-conformance/holdings/): 21 records (a control
|
||||
for each rule and for the signature, a small-order key among them), 11 verdicts, 6 queries and
|
||||
a plan over three neighbours. `holdings_verify.py` is a second implementation.
|
||||
|
||||
### When each object passes on a bearer with no return path, and when a receiver holds it
|
||||
|
||||
- **The carousel schedule** ([extensions/carousel.md](extensions/carousel.md)), the record
|
||||
`wai.bearer.carousel`: a publisher's signed plan, a jwp-receipts claim of class `schedule`
|
||||
(§2.4.16) beside the group receipt and the instance's transmission claim, naming for each object
|
||||
its forward error correction, RaptorQ (RFC 6330 with its erratum 5548) or Compact No-Code
|
||||
(RFC 5445), with that code's object transmission information, the repair symbols each pass
|
||||
adds, whether a later pass repeats its symbols or sends new ones, and the times and count of its
|
||||
passes. Each code's parameters are held to its bounds and the RFCs' division of an object into
|
||||
source blocks; an entry to its group, its object's content hash and, where the bytes are held,
|
||||
their length.
|
||||
- **When an object is held.** For a loss bound in parts per million and an overhead of 0, 1 or 2,
|
||||
a verifier derives the pass after which a receiver whose losses stay within the bound holds
|
||||
each object, whatever symbols it loses: a RaptorQ block at K′ + h distinct symbols, the count
|
||||
RFC 6330 §5.8 states its decoding-failure bound at, and a symbol a repeating pass missed counted
|
||||
as missed again. It reports the octets each schedule sends and the bound on decoding failure,
|
||||
and checks a statement that an object or a group is held by an instant. Schedules that
|
||||
contradict each other, or whose instance is undeclared or two-way, are set aside.
|
||||
- **The passes a receiver listens to**, from what it holds of each block and when it tunes in: the
|
||||
passes that add symbols it is certain to keep, when it holds the object, and how long its radio
|
||||
must be on.
|
||||
- **The corpus** [`live-conformance/carousel/`](live-conformance/carousel/): 33 verdicts, 50
|
||||
claims (every bound of the codes met and passed) and 16 plans. `carousel_verify.py` is a second
|
||||
implementation; `claim_verify.py carousel` reads every claim through steps 1–4; and
|
||||
`tests/carousel.rs` checks the table of K′ and Partition[] against an independent RFC 6330
|
||||
implementation for every block size, and decodes each RaptorQ object a case holds available
|
||||
from what a receiver within the bound holds.
|
||||
- **Breaking:** `api:rust:wai::claim_receipt::ClaimClass::Schedule` and
|
||||
`api:rust:wai::claim_receipt::ClaimBody::Schedule`: the class jwp-receipts §2.4.16 registers, which an
|
||||
exhaustive match over either needs.
|
||||
- **Breaking:** the eleven reasons of the class, which an exhaustive match over `Reason` needs:
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleNotScheduled`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleObjectsMalformed`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleOtiInvalid`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleSymbolsInvalid`,
|
||||
`api:rust:wai::claim_receipt::Reason::SchedulePassesInvalid`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleSubjectMismatch`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleSignerMismatch`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleLeafOutOfRange`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleBeforeGroup`,
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleContentMismatch` and
|
||||
`api:rust:wai::claim_receipt::Reason::ScheduleLengthMismatch`.
|
||||
|
||||
### What any file is, from its bytes alone
|
||||
|
||||
- **`wai identify <file>`** (operation `media.inspect`, [extensions/media-identify.md](extensions/media-identify.md)): the
|
||||
formats a file's bytes fit, each with the offsets that say so and how far its structure was followed; a container's
|
||||
tracks with their codec, size and protection as it states them; the registered capabilities they map to; signatures
|
||||
found inside it; and, where no format is recognised, measures that still hold: how its values are spread and the lags at
|
||||
which it repeats. `verdict` is `identified`, `ambiguous`, `signature_only` or `unknown`, and there is no confidence
|
||||
figure: a candidate's strength says what was checked. Nothing is decoded and no protected track is opened. It reads ISO
|
||||
media, Matroska, transport streams, Ogg, FLAC, RIFF, MPEG audio, ADTS, PNG, JPEG, GIF, IVF, YUV4MPEG2, FLV, AV1 OBU and
|
||||
Annex B streams and HLS and DASH text, and knows many more by signature. Any size of file costs a bounded amount, and
|
||||
every walk has been given hostile bytes. It is in the agent door as `media_inspect`, its results marked
|
||||
`untrusted_content`, because a file's author chooses the strings in it.
|
||||
- The corpus [`identify-conformance/`](identify-conformance/) holds files from real muxers and encoders, with what an
|
||||
independent prober reads of them, and negative controls; `identify_verify.py` is a second implementation.
|
||||
|
||||
### A probe no longer panics on two adjacent start codes
|
||||
|
||||
- `wai.video.avs3` and `wai.audio.avs3`: a payload with a start code prefix directly after another (`00 00 01 00 00 01`)
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ operation of the toolchain, named by it with `_` for the dot, and the tools come
|
|||
|
||||
| Toolset | Tools |
|
||||
|---|---|
|
||||
| `core` (the default) | `envelope_inspect`, `registry_list`, `registry_show`, `receipt_verify`, `envelope_wrap`, `conformance_list`, `conformance_run`, `docs_search` |
|
||||
| `core` (the default) | `envelope_inspect`, `media_inspect`, `registry_list`, `registry_show`, `receipt_verify`, `envelope_wrap`, `conformance_list`, `conformance_run`, `docs_search` |
|
||||
| `dev` | `dev_start`, `dev_stop`, `room_grant`, `room_join`, `room_leave`, `session_show` |
|
||||
| `app` (a build with the feature `app`) | `app_guide`, `app_check`, `app_stream`, `app_export`: Create's, below |
|
||||
|
||||
|
|
|
|||
|
|
@ -182,10 +182,12 @@ and a role are fixed once registered.
|
|||
| `wai.audio.score` | payload | registered | audio | standalone | royalty-free | standard-defined | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.avatar.face` | payload | registered | interactive | standalone | royalty-free | generative-exact | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.avatar.pose` | payload | registered | interactive | standalone | royalty-free | standard-defined | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.bearer.carousel` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.bearer.receipts` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.capture.take` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.audit` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.fleet` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.holdings` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.ledger` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.opportunity` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
| `wai.delivery.revenue_share` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content |
|
||||
|
|
|
|||
31
wai/SPEC.md
31
wai/SPEC.md
|
|
@ -1767,6 +1767,33 @@ over the bearer is held and anchors nothing (`anchor_on_bearer`). The extension
|
|||
carriage onto FLUTE, DVB multicast ABR, DVB-I, ATSC 3.0, 3GPP MBMS and 5G broadcast, MoQ over
|
||||
multicast and the applications of hybrid broadcast receivers:
|
||||
[`extensions/bearer-receipts.md`](extensions/bearer-receipts.md).
|
||||
And for **when** each object passes on a bearer with no return path — `wai.bearer.carousel`: a
|
||||
publisher's signed schedule, a jwp-receipts claim of class `schedule` beside the group receipt
|
||||
and the instance's transmission claim, that names for each object its forward error correction
|
||||
(RaptorQ, RFC 6330, or Compact No-Code, RFC 5445) with that code's object transmission
|
||||
information, the repair symbols each pass adds, whether a later pass repeats its symbols or sends
|
||||
new ones, and the times and count of its passes. A verifier holds the parameters to each code's
|
||||
bounds and the entries to the group, its objects' content hashes and, where it holds them, their
|
||||
lengths; sets aside schedules that contradict each other, or whose instance is undeclared or
|
||||
two-way; and derives, for a loss bound stated in parts per million, the pass after which a
|
||||
receiver whose losses stay within it holds each object, whatever symbols it loses: a RaptorQ
|
||||
block at K′ + h distinct symbols, the count RFC 6330 states its decoding-failure bound at, and
|
||||
a symbol a repeating pass missed counted as missed again. That is the deadline a one-way bearer
|
||||
can promise, checked with no receiver; a statement that an object or a group is held by an
|
||||
instant holds or is refused (`availability_after_deadline`, `availability_not_within_schedule`,
|
||||
`object_not_scheduled`). A receiver derives the passes it must listen to, and when its radio may
|
||||
be off: [`extensions/carousel.md`](extensions/carousel.md).
|
||||
And for **who holds what** near a receiver — `wai.delivery.holdings`: a node's signed record
|
||||
(`WHD1`) of which objects of which groups it holds now, for its neighbours: per group, its link
|
||||
hash, its leaf count and either `whole` or a bitmap, in one canonical form, signed under SPEC §7
|
||||
*Signatures*, with an instant it states them at and one it expires at. Exact, so a neighbour is
|
||||
never sent after an object its holder lacks, and a bit an object where objects are held as groups
|
||||
are delivered. A verifier holds a record to its instant and to the group receipts it has
|
||||
(`holdings_leaf_count_mismatch` refuses it whole), keeps a holder's latest record and sets aside
|
||||
two of one instant that differ, answers whether a record holds an object, and derives which wanted
|
||||
objects each neighbour holds, in the asker's order, and which must come from the origin. An object
|
||||
a neighbour serves is verified under its receipts like any other; a record is never evidence of
|
||||
bytes: [`extensions/holdings.md`](extensions/holdings.md).
|
||||
And for **exploration** with the exact generator, and **taste** as a declared, owned objective —
|
||||
`wai.media.exploration`: six integer descriptors over a picture's `RGB8` samples (mean luma,
|
||||
chroma, detail, palette diversity and the luma centroid's two coordinates) place it without
|
||||
|
|
@ -2203,7 +2230,9 @@ above).
|
|||
`wai.video.avs3` and `wai.audio.avs3` (and for `wai.image.jxs`, `wai.video.jxs`,
|
||||
`wai.image.ieee1857_11` and `wai.meta.projection`): it identifies a payload as the format its
|
||||
capability names and reads the parameters its headers state, or refuses it with a code. A
|
||||
probe decodes no sample, so it does not change any class above.
|
||||
probe decodes no sample, so it does not change any class above. Where the capability is not known,
|
||||
[media-identify](extensions/media-identify.md) says what a file is from its bytes alone, with the
|
||||
offsets that say so, and runs these probes among its readings.
|
||||
|
||||
**One Opus packet.** A `wai.audio.opus.packet` payload is one Opus packet (RFC
|
||||
6716 §3), its TOC byte first, with no container: the per-packet form streaming
|
||||
|
|
|
|||
|
|
@ -1000,6 +1000,74 @@
|
|||
"sdk": "wai.media.generate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "media.inspect",
|
||||
"summary": "What a file is, from its bytes alone: the formats they fit with the bytes that say so, its tracks, its protection, and measures where no format is recognised",
|
||||
"cli": "wai identify",
|
||||
"aliases": [],
|
||||
"usage": [
|
||||
"wai identify <file> [--json] any file, not only an envelope (extensions/media-identify.md); unknown stays unknown"
|
||||
],
|
||||
"placement": "local",
|
||||
"effect": "reads",
|
||||
"dry_run": false,
|
||||
"confirm": "never",
|
||||
"route": null,
|
||||
"doors": {
|
||||
"cli": true,
|
||||
"mcp": true,
|
||||
"http": false
|
||||
},
|
||||
"toolset": "core",
|
||||
"stability": "stable",
|
||||
"input": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "Any file.",
|
||||
"x-cli": {
|
||||
"positional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"output": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"format",
|
||||
"size",
|
||||
"verdict",
|
||||
"candidates",
|
||||
"probes_accepted",
|
||||
"embedded",
|
||||
"embedded_capped",
|
||||
"measures"
|
||||
],
|
||||
"description": "The formats the bytes fit, each with the bytes that say so and how far they were followed; what a probe accepted; signatures found inside; and measures that hold whatever the bytes are"
|
||||
},
|
||||
"stream": null,
|
||||
"since": "0.3.0",
|
||||
"deprecated": null,
|
||||
"receipt": false,
|
||||
"needs": {
|
||||
"all": [
|
||||
"identify"
|
||||
]
|
||||
},
|
||||
"names": {
|
||||
"cli": "wai media inspect",
|
||||
"mcp": "media_inspect",
|
||||
"c": "wai_media_inspect",
|
||||
"rust": "wai::media::inspect",
|
||||
"sdk": "wai.media.inspect"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "media.upscale",
|
||||
"summary": "Upscale a PNG exactly, by an integer network under a pinned prior, with a signed record a verifier recomputes",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ claim are read, verified and their figures read by the OER reference verifier
|
|||
python3 claim_verify.py bearer <file> steps 1-4 over the `transmission` and
|
||||
`reconciliation` claims of the bearer corpus
|
||||
(live-conformance/bearers)
|
||||
python3 claim_verify.py carousel <file> steps 1-4 over the `transmission` and
|
||||
`schedule` claims of the carousel corpus
|
||||
(live-conformance/carousel)
|
||||
|
||||
Exit status 0 only if every vector reports its declared outcome, and the fixture and the
|
||||
bracket arithmetic this script computes equal the literals pinned in wai-rs.
|
||||
|
|
@ -179,7 +182,8 @@ ADMITS = {"energy": {"object"}, "lawful-basis": {"object", "receipt"}, "log-incl
|
|||
"soft-binding": {"object"}, "soft-binding-span": {"object"}, "binding-recovery": {"object"},
|
||||
"measurement": {"object", "receipt"}, "operating-point": {"object", "receipt"},
|
||||
"stage": {"object"}, "delivery-gap": {"object"}, "reconstruct-session": {"object"},
|
||||
"consumption": {"object"}, "transmission": {"receipt"}, "reconciliation": {"receipt"}}
|
||||
"consumption": {"object"}, "transmission": {"receipt"}, "reconciliation": {"receipt"},
|
||||
"schedule": {"receipt"}}
|
||||
FIGURE = ["figure_profile", "figure_index", "group", "joules_micro", "energy_provenance", "energy_uncertainty",
|
||||
"energy_counter_filtering", "energy_calibration_ref"]
|
||||
BODY = {
|
||||
|
|
@ -200,6 +204,8 @@ BODY = {
|
|||
# §2.4.14, §2.4.15: bearer-receipts §4.1, §5.1.
|
||||
"transmission": ["bearer", "bearer_id", "energy", "attests"],
|
||||
"reconciliation": ["bearer", "bearer_id", "leaf_count", "received", "received_root", "attests"],
|
||||
# §2.4.16: carousel §4.1.
|
||||
"schedule": ["bearer_id", "objects", "attests"],
|
||||
"reconstruct-session": ["session_nonce", "media", "host", "decoders", "units", "leaf_count", "output_root",
|
||||
"primary_units", "fallback_units", "switches", "switches_total", "deadline_us", "missed",
|
||||
"missed_units", "stage_sets", "framing", "counted_primary", "counted_fallback",
|
||||
|
|
@ -463,6 +469,22 @@ def check_body(cls, b):
|
|||
need(e["energy_calibration_ref"] is None or isinstance(e["energy_calibration_ref"], str), 2)
|
||||
need(isinstance(b["attests"], str), 2)
|
||||
return
|
||||
if cls == "schedule":
|
||||
# carousel §4.1: the instance an absolute URI; each entry's members, then each one's type
|
||||
# in the table's order, `oti`'s members those of the code `fec` names.
|
||||
need(is_uri(b["bearer_id"]), 2)
|
||||
need(isinstance(b["objects"], list), 2)
|
||||
for e in b["objects"]:
|
||||
need(isinstance(e, dict), 2)
|
||||
members(e, SCHEDULE_ENTRY, 2)
|
||||
need(is_int(e["leaf_index"]) and is_hex(e["content_hash"], 32), 2)
|
||||
need(e["fec"] in SCHEDULE_OTI and isinstance(e["oti"], dict), 2)
|
||||
members(e["oti"], SCHEDULE_OTI[e["fec"]], 2)
|
||||
need(all(is_int(v) for v in e["oti"].values()), 2)
|
||||
need(is_int(e["repair"]) and e["esi"] in ("repeat", "advance"), 2)
|
||||
need(all(is_int(e[k]) for k in ("first_ms", "period_ms", "duration_ms", "count")), 2)
|
||||
need(isinstance(b["attests"], str), 2)
|
||||
return
|
||||
if cls == "consumption":
|
||||
# ai-usage §6.1: the arrays' lengths and a term's are in the type column.
|
||||
need(isinstance(b["objects"], list) and 1 <= len(b["objects"]) <= 1024
|
||||
|
|
@ -1470,6 +1492,75 @@ def fabric_rules(f, joules):
|
|||
need(sum(s["joules_micro"] for s in sh) == joules, 4, "measurement_fabric_sum")
|
||||
|
||||
|
||||
# carousel §4.1, §5: a schedule entry's members, each code's parameters and their bounds.
|
||||
SCHEDULE_ENTRY = ["leaf_index", "content_hash", "fec", "oti", "repair", "esi", "first_ms", "period_ms",
|
||||
"duration_ms", "count"]
|
||||
SCHEDULE_OTI = {"raptorq": ["transfer_length", "symbol_size", "source_blocks", "sub_blocks", "alignment"],
|
||||
"no-code": ["transfer_length", "symbol_size", "max_block_length"]}
|
||||
K_PRIME_MAX = 56_403 # RFC 6330 §5.1.2
|
||||
RAPTORQ_MAX_F = 942_574_504_275 # RFC 6330 §3.3.2 as its erratum 5548 corrects it
|
||||
LAST_INSTANT_MS = 253_402_300_799_999 # 9999-12-31T23:59:59.999Z
|
||||
|
||||
|
||||
def ceil_div(a, b):
|
||||
return -(-a // b)
|
||||
|
||||
|
||||
def schedule_blocks(e):
|
||||
"""carousel §5.1, §5.2: an entry's source blocks as [(K, how many)], the larger size first
|
||||
and a size no block has left out; None when its code does not admit its parameters."""
|
||||
o = e["oti"]
|
||||
if e["fec"] == "raptorq":
|
||||
f, t, z, n, al = (o[k] for k in SCHEDULE_OTI["raptorq"])
|
||||
if not (1 <= f <= RAPTORQ_MAX_F and 1 <= al <= 255 and 1 <= t <= 65_535 and t % al == 0
|
||||
and 1 <= z <= 255 and 1 <= n <= t // al):
|
||||
return None
|
||||
kt = ceil_div(f, t)
|
||||
if z > kt or ceil_div(kt, z) > K_PRIME_MAX:
|
||||
return None
|
||||
# RFC 6330 §4.4.1.2: (KL, KS, ZL, ZS) = Partition[Kt, Z].
|
||||
kl, ks = ceil_div(kt, z), kt // z
|
||||
zl = kt - ks * z
|
||||
sizes = [(kl, zl), (ks, z - zl)]
|
||||
else:
|
||||
ln, es, bl = (o[k] for k in SCHEDULE_OTI["no-code"])
|
||||
if not (1 <= ln < 1 << 48 and 1 <= es <= 65_535 and 1 <= bl < 1 << 32):
|
||||
return None
|
||||
# RFC 5052 §9.1: Ts symbols in Nb blocks; I blocks of A_large, the rest of A_small.
|
||||
ts = ceil_div(ln, es)
|
||||
nb = ceil_div(ts, bl)
|
||||
a_large, a_small = ceil_div(ts, nb), ts // nb
|
||||
if nb > 1 << 16 or a_large > 1 << 16:
|
||||
return None
|
||||
i = ts - a_small * nb
|
||||
sizes = [(a_large, i), (a_small, nb - i)]
|
||||
return [(k, c) for k, c in sizes if c > 0]
|
||||
|
||||
|
||||
def schedule_bytes(e, blocks):
|
||||
"""carousel §5.4: count · Σ m · T for RaptorQ, count · L for Compact No-Code."""
|
||||
if e["fec"] == "raptorq":
|
||||
return e["count"] * sum(c * (k + e["repair"]) for k, c in blocks) * e["oti"]["symbol_size"]
|
||||
return e["count"] * e["oti"]["transfer_length"]
|
||||
|
||||
|
||||
def schedule_entry_rules(e):
|
||||
"""carousel §4.2 rule 3 for one entry, in its order. Returns its blocks."""
|
||||
blocks = schedule_blocks(e)
|
||||
need(blocks is not None, 4, "schedule_oti_invalid")
|
||||
if e["fec"] == "no-code":
|
||||
named = e["repair"] == 0 and e["esi"] == "repeat"
|
||||
else:
|
||||
m = blocks[0][0] + e["repair"]
|
||||
named = (m if e["esi"] == "repeat" else e["count"] * m) <= 1 << 24
|
||||
need(named, 4, "schedule_symbols_invalid")
|
||||
c, p, d = e["count"], e["period_ms"], e["duration_ms"]
|
||||
formed = (c >= 1 and d >= 1 and (c < 2 or p >= d) and e["first_ms"] + (c - 1) * p + d <= LAST_INSTANT_MS
|
||||
and schedule_bytes(e, blocks) <= MAX_INT)
|
||||
need(formed, 4, "schedule_passes_invalid")
|
||||
return blocks
|
||||
|
||||
|
||||
def class_rules(c):
|
||||
"""§3.1 step 4 — §2.4.5-§2.4.7 and §2.6."""
|
||||
cls, b = c["class"], c["body"]
|
||||
|
|
@ -1526,6 +1617,13 @@ def class_rules(c):
|
|||
need(n >= 1 and len(bits) == (n + 7) // 8, 4, bad)
|
||||
need(not any(bits[i // 8] & (0x80 >> (i % 8)) for i in range(n, 8 * len(bits))), 4, bad)
|
||||
need(any(bits), 4, bad)
|
||||
elif cls == "schedule":
|
||||
# carousel §4.2.
|
||||
need(b["attests"] == "scheduled", 4, "schedule_not_scheduled")
|
||||
idx = [e["leaf_index"] for e in b["objects"]]
|
||||
need(len(idx) > 0 and all(x < y for x, y in zip(idx, idx[1:])), 4, "schedule_objects_malformed")
|
||||
for e in b["objects"]:
|
||||
schedule_entry_rules(e)
|
||||
elif cls == "log-inclusion":
|
||||
need(b["log_receipt"] != "", 4, "log_inclusion_no_evidence")
|
||||
elif cls in ("soft-binding", "soft-binding-span", "binding-recovery"):
|
||||
|
|
@ -2218,6 +2316,39 @@ def bearer_claims(path):
|
|||
return 1 if fail or n == 0 else 0
|
||||
|
||||
|
||||
def carousel_claims(path):
|
||||
"""§3.1 steps 1-4 over the claims of the carousel corpus (live-conformance/carousel/vectors.json,
|
||||
carousel §10): a claim a verdict case states refused at one of those steps is refused there
|
||||
with the stated reason, and every other `transmission` or `schedule` claim passes them; and
|
||||
each claim case gets the outcome it states. Steps 5 and 6 are the carousel verifier's."""
|
||||
with open(path, encoding="utf-8") as f:
|
||||
doc = json.load(f)
|
||||
fail = n = 0
|
||||
checks = []
|
||||
for case in doc["verdicts"]:
|
||||
if case["outcome"]["outcome"] != "verdict":
|
||||
continue
|
||||
for wire, stated in zip(case["claims"], case["outcome"]["claims"]):
|
||||
m = re.fullmatch(r"reject step=(\d) reason=(\S+)", stated)
|
||||
checks.append((case["name"], wire, (int(m[1]), m[2]) if m and int(m[1]) <= 4 else None, True))
|
||||
for case in doc["claims"]:
|
||||
m = re.fullmatch(r"reject step=(\d) reason=(\S+)", case["outcome"])
|
||||
checks.append((case["name"], case["claim"], (int(m[1]), m[2]) if m else None, False))
|
||||
for name, wire, want, only_bearer in checks:
|
||||
try:
|
||||
c = verify_claim(wire)
|
||||
if only_bearer and c["class"] not in ("transmission", "schedule"):
|
||||
continue
|
||||
got = None
|
||||
except Reject as r:
|
||||
got = (r.step, r.reason)
|
||||
n += 1
|
||||
fail += got != want
|
||||
print(f"{'PASS' if got == want else 'FAIL'} {name} expected={want} got={got}")
|
||||
print(f"{n} carousel claims, {fail} failure(s)")
|
||||
return 1 if fail or n == 0 else 0
|
||||
|
||||
|
||||
def main(argv):
|
||||
if argv[1:2] == ["brackets"]:
|
||||
for name, br, _, _ in bracket_fixture():
|
||||
|
|
@ -2230,6 +2361,8 @@ def main(argv):
|
|||
return consumption_claims(argv[2])
|
||||
if argv[1:2] == ["bearer"] and len(argv) == 3:
|
||||
return bearer_claims(argv[2])
|
||||
if argv[1:2] == ["carousel"] and len(argv) == 3:
|
||||
return carousel_claims(argv[2])
|
||||
if argv[1:2] == ["fixture"]:
|
||||
c, canon, link = fixture()
|
||||
print("sig ", c["sig"])
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ and context checks; the verdict a verifier derives for one group from its receip
|
|||
claims (§6); the upper bound on a receiver's share of a shared transmission's energy (§7);
|
||||
verification with no return path (§8); and the mappings onto bearers (§9).
|
||||
|
||||
When each object passes on a bearer with no return path, and with which forward error
|
||||
correction, is [carousel](carousel.md)'s: a publisher's `schedule` claim beside the transmission
|
||||
claim here, from which a verifier derives when a receiver within a loss bound holds each object.
|
||||
|
||||
Out of scope: a transport or a bearer; a reception-reporting protocol — a reconciliation
|
||||
travels on whatever path a sink later has, as a receipt file (http-delivery §8.1) or inside a
|
||||
bearer's own reporting (§9); a pre-emption authority ([unidirectional-delivery](unidirectional-delivery.md)
|
||||
|
|
|
|||
460
wai/extensions/carousel.md
Normal file
460
wai/extensions/carousel.md
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
# WAI Extension: Carousel Schedule
|
||||
|
||||
> Status: Draft. Registers the record `wai.bearer.carousel`: a publisher's signed schedule of
|
||||
> when each object of a group passes on a bearer instance, and with which application-layer
|
||||
> forward error correction. On a bearer with no return path a receiver cannot ask for what it
|
||||
> missed: it holds an object only if the passes it heard carried enough encoding symbols. The
|
||||
> schedule says what was planned to be sent, in a jwp-receipts claim of class **schedule**
|
||||
> (jwp-receipts §2.4.16, §4 here), signed by the group's key beside the group receipt and the
|
||||
> transmission claim of [bearer-receipts](bearer-receipts.md) §4. From it a verifier derives,
|
||||
> for each object, when a receiver whose losses stay within a stated bound holds it (§6): the
|
||||
> deadline a one-way bearer can promise, with the decoding failure the FEC code bounds. A
|
||||
> receiver derives the passes it must listen to and when it may turn its radio off (§7). WAI
|
||||
> defines no transport and no code: the codes are RaptorQ (RFC 6330) and Compact No-Code
|
||||
> (RFC 5445), and the schedule declares their parameters. The reference implementation is the
|
||||
> `carousel` module of `wai-rs` (feature `carousel`), with the class's body in
|
||||
> `claim_receipt::schedule`; the corpus is
|
||||
> [`live-conformance/carousel/`](../live-conformance/carousel/), and its second implementation
|
||||
> is `live-conformance/carousel_verify.py`.
|
||||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||||
>
|
||||
> Texts read for this revision: RFC 6330 (RaptorQ) with its verified erratum 5548, RFC 5445
|
||||
> (Compact No-Code) and RFC 5052 §9.1 (block partitioning). A clause number of one of them in
|
||||
> this text is that RFC's.
|
||||
|
||||
## 1. Scope
|
||||
|
||||
In scope: the schedule claim (§4); how an object is divided into source blocks and symbols
|
||||
under each code (§5); when an object is held by a receiver whose losses stay within a bound,
|
||||
and the statements a verifier may check against it (§6); the passes a receiver listens to (§7).
|
||||
|
||||
Out of scope: encoding and decoding symbols, which are the codes' own (RFC 6330 §5, RFC 5445
|
||||
§3); a transport, and how symbols are packed into packets; a model of a channel. Every
|
||||
statement here holds for every pattern of loss within the stated bound (§2). Whether losses on a
|
||||
channel are independent, and how often a bound is exceeded, is a judgement about that
|
||||
channel, which a relying party makes and a schedule does not carry. Nothing here states
|
||||
presentation, and nothing here states that any receiver heard anything: that is a
|
||||
reconciliation's (bearer-receipts §5).
|
||||
|
||||
Relationship to the core spec: `wai.bearer.carousel` is a **record** ([SPEC](../SPEC.md) §5):
|
||||
the schedule claim is a signed record a WAI tool emits and verifies. It names no payload format
|
||||
and is not dispatchable.
|
||||
|
||||
## 2. Terms
|
||||
|
||||
- **Bearer instance**, **declaration**, **sink**, **relying party**: as bearer-receipts §2. A
|
||||
declaration is a transmission claim that stands under bearer-receipts §6.2 step 4.
|
||||
- **Source block**, **source symbol**, **repair symbol**, **encoding symbol**, **ESI** (encoding
|
||||
symbol ID): as RFC 6330 §2 and RFC 5052. An object is divided into source blocks; a block of
|
||||
*K* source symbols is sent as encoding symbols, each named by its ESI in that block.
|
||||
- **Pass**: one transmission of encoding symbols of every source block of one object on a
|
||||
bearer instance, from its start to its end. An object's passes are numbered from 1.
|
||||
- **Instant**: a count of milliseconds since 1970-01-01T00:00:00Z, leap seconds not counted, as
|
||||
[channel-lineup](channel-lineup.md) writes one.
|
||||
- **Loss bound** *L*: an integer from 0 to 999,999, in parts per million. A receiver is
|
||||
**within** *L* when, of the *m* encoding symbols of any one source block sent in any one
|
||||
pass, it fails to receive at most ⌊*L* · *m* / 1,000,000⌋, whichever they are. It is a bound
|
||||
on symbols: a relying party whose bound is stated on packets carrying several symbols each
|
||||
converts it.
|
||||
- **Overhead** *h*: 0, 1 or 2, the encoding symbols beyond *K′* a RaptorQ block is held at
|
||||
(§5.3).
|
||||
|
||||
## 3. Why a schedule beside the receipt
|
||||
|
||||
The group receipt signs its objects and names no bearer (bearer-receipts §3); the transmission
|
||||
claim names the bearer instance and attests that the publisher transmitted the group, which on
|
||||
a bearer with no return path says nothing about any receiver. Between the two is what decides
|
||||
whether a receiver that heard part of a transmission holds an object: how often each object
|
||||
passes, for how long, and with how many repair symbols. That is a plan, made before anything
|
||||
is sent, and it is a statement of the publisher's own:
|
||||
|
||||
- **A relying party can hold a deadline to it.** A publisher that offers an object to every
|
||||
receiver within a loss bound by a deadline makes a promise that the schedule either supports
|
||||
or does not (§6.4), checked with no receiver and no return path.
|
||||
- **A receiver can plan with it.** It learns which passes add symbols it needs, and listens to
|
||||
those only (§7). On a receiver powered by a battery the radio is a large part of its energy.
|
||||
- **It changes no byte of what it schedules.** The schedule names the group by its link hash
|
||||
and each object by its position and content hash, like a reconciliation. A group carried
|
||||
before this extension, or by a party that does not use it, verifies exactly as before.
|
||||
|
||||
The schedule attests a plan, not a transmission: that is the declaration's, and the schedule
|
||||
stands only beside one (§6.2). Neither attests that any receiver heard anything.
|
||||
|
||||
## 4. The schedule claim (`class: "schedule"`)
|
||||
|
||||
A publisher that transmits a group over a `multicast` or `unidirectional` bearer instance
|
||||
**MAY** declare its schedule there in a `schedule` claim, one per bearer instance. Its
|
||||
`subject_type` is `receipt` and its `subject` the group receipt's link hash (jwp-receipts §3
|
||||
step 3). It is signed by the key that signed the group. It attests that the publisher
|
||||
scheduled the passes it names, and nothing about what any sink received.
|
||||
|
||||
### 4.1 Body
|
||||
|
||||
| member | type | meaning |
|
||||
|---|---|---|
|
||||
| `bearer_id` | absolute URI | The bearer instance (bearer-receipts §2): the `bearer_id` of the transmission claim it schedules. |
|
||||
| `objects` | array | The objects scheduled, one entry each (below). |
|
||||
| `attests` | string | **MUST** be `"scheduled"`. Any string passes step 2; another value is refused at step 4. |
|
||||
|
||||
Each entry of `objects` has exactly these members:
|
||||
|
||||
| member | type | meaning |
|
||||
|---|---|---|
|
||||
| `leaf_index` | integer | The object's position in the group (jwp-receipts §2.2). |
|
||||
| `content_hash` | 32 B hex | The object's content hash (jwp-receipts §2.1): what a receiver that decodes the object checks it against. |
|
||||
| `fec` | string | The code: `raptorq` (RFC 6330, FEC Encoding ID 6) or `no-code` (RFC 5445, FEC Encoding ID 0). |
|
||||
| `oti` | object | The code's object transmission information, as §5 reads it. For `raptorq`, exactly `transfer_length` (F), `symbol_size` (T), `source_blocks` (Z), `sub_blocks` (N) and `alignment` (Al), RFC 6330 §3.3.2 and §3.3.3. For `no-code`, exactly `transfer_length`, `symbol_size` (the encoding symbol length) and `max_block_length` (the maximum source block length, in symbols), RFC 5445 §3.2.2.2. Each an integer. |
|
||||
| `repair` | integer | The repair symbols of each source block sent in each pass, beyond its source symbols (§5.4). |
|
||||
| `esi` | string | `repeat`: every pass sends the same ESIs. `advance`: each pass sends ESIs no earlier pass sent (§5.4). |
|
||||
| `first_ms` | integer | The instant the first pass starts. |
|
||||
| `period_ms` | integer | The time from one pass's start to the next's. |
|
||||
| `duration_ms` | integer | The time each pass lasts. |
|
||||
| `count` | integer | The number of passes. |
|
||||
|
||||
`fec` and `esi` are read at jwp-receipts §3.1 step 2 against their enumerations, the members of
|
||||
`oti` against the code `fec` names, `bearer_id` as an absolute URI, and every integer as an
|
||||
unsigned 64-bit one (`field_type`).
|
||||
|
||||
### 4.2 Rules
|
||||
|
||||
Checked at jwp-receipts §3.1 step 4, in this order:
|
||||
|
||||
1. `attests` **MUST** be `"scheduled"` (`schedule_not_scheduled`).
|
||||
2. `objects` has at least one entry, and its `leaf_index` values strictly increase
|
||||
(`schedule_objects_malformed`): one entry for each object scheduled, in group order.
|
||||
3. Each entry in order, the first of these to fail:
|
||||
1. its `oti` is one its code admits, §5.1 or §5.2 (`schedule_oti_invalid`);
|
||||
2. its symbols are ones its code can name, §5.4 (`schedule_symbols_invalid`);
|
||||
3. its passes are well formed (`schedule_passes_invalid`): `count` and `duration_ms` are at
|
||||
least 1; with `count` at least 2, `period_ms` is at least `duration_ms`, so one pass of
|
||||
an object ends before its next starts; the last pass ends,
|
||||
`first_ms` + (`count` − 1) · `period_ms` + `duration_ms`, at or before 253,402,300,799,999
|
||||
(9999-12-31T23:59:59.999Z); and the octets its passes send, `bytes_sent` (§5.4), are at
|
||||
most 2<sup>53</sup> − 1, the greatest integer a receipt states; each computed without
|
||||
overflow.
|
||||
|
||||
### 4.3 Against the group
|
||||
|
||||
Checked at jwp-receipts §3.1 step 6 against the group receipt the verifier holds, which has
|
||||
passed jwp-receipts §3 steps 1 and 2 and whose `group_start` is an RFC 3339 date-time
|
||||
(`context_invalid` otherwise; another receipt is `context_mismatch`), the object receipts it
|
||||
holds under that group, each of which has passed jwp-receipts §3 step 5, and the envelope bytes
|
||||
of those it holds. In order:
|
||||
|
||||
1. the group's link hash is the claim's `subject` (`schedule_subject_mismatch`);
|
||||
2. the claim's `signer_pubkey` is the group's (`schedule_signer_mismatch`): only the publisher
|
||||
schedules its own transmission;
|
||||
3. every `leaf_index` is less than the group's `leaf_count` (`schedule_leaf_out_of_range`);
|
||||
4. no entry's first pass starts before the group does (`schedule_before_group`): ⌊`first_ms`
|
||||
/ 1000⌋ is at least the seconds jwp-receipts §2.4.3 reads from `group_start`, its fraction
|
||||
dropped. Nothing can be sent before it exists;
|
||||
5. for each entry whose object receipt the verifier holds, the receipt's `content_hash` is the
|
||||
entry's (`schedule_content_mismatch`); and for each whose envelope bytes it holds, their
|
||||
length is `oti.transfer_length` (`schedule_length_mismatch`).
|
||||
|
||||
A schedule checked without the group stops at step 5 and is not counted by §6.
|
||||
|
||||
### 4.4 Authority
|
||||
|
||||
Confirmed under jwp-receipts §7, a schedule claim needs the authority `schedule`, which is not
|
||||
pinned-only and is not `delivery`. An origin gives it to the keys that publish its groups, as it
|
||||
gives `transmission`.
|
||||
|
||||
## 5. Blocks and symbols
|
||||
|
||||
### 5.1 RaptorQ
|
||||
|
||||
The `oti` of a `raptorq` entry, F, T, Z, N and Al, is one RFC 6330 admits when:
|
||||
|
||||
- F is from 1 to 942,574,504,275 (§3.3.2 as erratum 5548 corrects it: 56,403 symbols of
|
||||
65,535 octets in each of 255 blocks);
|
||||
- T is from 1 to 65,535 and a multiple of Al, and Al from 1 to 255 (§3.3.2, §3.3.3, §4.2);
|
||||
- Z is from 1 to 255, the range of its 8-bit field (§3.3.3: the parameters are positive);
|
||||
- N is from 1 to T / Al, so each sub-block has a sub-symbol of at least Al octets (§4.4.1.2);
|
||||
- with *Kt* = ⌈F / T⌉, Z is at most *Kt*, so no source block is empty, and ⌈*Kt* / Z⌉ is at
|
||||
most 56,403, *K′*<sub>max</sub> (§4.4.1.2, §5.1.2).
|
||||
|
||||
The object is then divided as RFC 6330 §4.4.1.2 divides it: (*KL*, *KS*, *ZL*, *ZS*) =
|
||||
Partition[*Kt*, Z], where Partition[*I*, *J*] = (⌈*I* / *J*⌉, ⌊*I* / *J*⌋, *I* − ⌊*I* / *J*⌋ · *J*,
|
||||
*J* − (*I* − ⌊*I* / *J*⌋ · *J*)). The first *ZL* source blocks have *KL* source symbols each and
|
||||
the remaining *ZS* have *KS*. Each symbol is T octets; the last source symbol of the object is
|
||||
padded to T.
|
||||
|
||||
### 5.2 Compact No-Code
|
||||
|
||||
The `oti` of a `no-code` entry, with L its `transfer_length`, E its `symbol_size` and B its
|
||||
`max_block_length`, is one RFC 5445 admits when L is from 1 to 2<sup>48</sup> − 1, E from 1 to
|
||||
65,535 and B from 1 to 2<sup>32</sup> − 1 (RFC 5445 §3.2.2.2; an object of no octets has
|
||||
nothing to schedule), and the division of RFC 5052 §9.1 fits RFC 5445's 16-bit source block
|
||||
number and encoding symbol ID (§3.2.1):
|
||||
|
||||
- the object has *Ts* = ⌈L / E⌉ source symbols, in *Nb* = ⌈*Ts* / B⌉ source blocks;
|
||||
- *A*<sub>large</sub> = ⌈*Ts* / *Nb*⌉, *A*<sub>small</sub> = ⌊*Ts* / *Nb*⌋ and *I* = *Ts* −
|
||||
*A*<sub>small</sub> · *Nb*: the first *I* blocks have *A*<sub>large</sub> source symbols and
|
||||
the others *A*<sub>small</sub>;
|
||||
- *Nb* is at most 65,536, and *A*<sub>large</sub> at most 65,536.
|
||||
|
||||
Every symbol is E octets but the object's last, which holds what remains of L.
|
||||
|
||||
### 5.3 What a receiver needs
|
||||
|
||||
A block of *K* source symbols is **held** when a receiver can recover it:
|
||||
|
||||
- **`no-code`**: when it has received every one of its *K* source symbols. There are no repair
|
||||
symbols; a symbol missed is missed until a pass sends it again.
|
||||
- **`raptorq`**: when it has received *K′* + *h* distinct encoding symbols of the block, where
|
||||
*K′* is the least value at least *K* in RFC 6330 Table 2 (§5.6; its column of *K′* is restated
|
||||
in Appendix A). RFC 6330 §5.8 bounds the decoder's failure to recover a block of *K′* source
|
||||
symbols from *K′*, *K′* + 1 and *K′* + 2 encoding symbols, chosen uniformly at random, at
|
||||
1 in 100, 1 in 10,000 and 1 in 1,000,000 on average. A block of *K* < *K′* symbols is padded
|
||||
to *K′* with symbols the decoder knows (§5.3.1), so it may need fewer than *K′* + *h*
|
||||
received; this text asks for the count §5.8 states its bound at, and no fewer.
|
||||
|
||||
An **object** is held when every one of its source blocks is.
|
||||
|
||||
### 5.4 The symbols a pass sends
|
||||
|
||||
Each pass sends, of each source block of *K* source symbols, *m* = *K* + `repair` encoding
|
||||
symbols:
|
||||
|
||||
- `repeat`: ESIs 0 to *m* − 1 in every pass;
|
||||
- `advance`: in pass *j*, ESIs (*j* − 1) · *m* to *j* · *m* − 1. The first pass holds the source
|
||||
symbols (ESIs 0 to *K* − 1, RFC 6330 §4.4.2) and every later pass only repair symbols, none
|
||||
sent before.
|
||||
|
||||
An entry's symbols are ones its code can name (§4.2 rule 3.2) when:
|
||||
|
||||
- **`no-code`**: `repair` is 0 and `esi` is `repeat`. The code has no repair symbols, and an ESI
|
||||
past a block's last source symbol names nothing;
|
||||
- **`raptorq`**: the greatest ESI sent fits the 24-bit field (RFC 6330 §3.2): with *KL* the
|
||||
larger block's *K*, *KL* + `repair` is at most 2<sup>24</sup> for `repeat`, and `count` ·
|
||||
(*KL* + `repair`) at most 2<sup>24</sup> for `advance`, computed without overflow.
|
||||
|
||||
The octets an entry's passes send, `bytes_sent`, are `count` · Σ *m* · T over its source blocks
|
||||
for `raptorq`, and `count` · L for `no-code`, whose last symbol is short.
|
||||
|
||||
## 6. When an object is held
|
||||
|
||||
### 6.1 Inputs
|
||||
|
||||
The group receipt; the object receipts the verifier holds under it, each with its envelope
|
||||
bytes where it holds them; the transmission and schedule claims it holds about the group, each
|
||||
signer's in the order of its chain; a loss bound *L*; and an overhead *h*.
|
||||
|
||||
### 6.2 Steps
|
||||
|
||||
The loss bound and overhead are checked first: *L* above 999,999 is `loss_bound_invalid`, and
|
||||
*h* other than 0, 1 or 2 is `overhead_invalid`; neither derives anything. Then:
|
||||
|
||||
1. **The group and its objects**: bearer-receipts §6.2 steps 1 and 2.
|
||||
2. **The claims**: each claim, in order, under jwp-receipts §3.1 steps 1 to 5, then step 6: a
|
||||
transmission claim by bearer-receipts §4.3 and a schedule by §4.3 here. A claim of another
|
||||
class is `context_mismatch` at step 6. A claim refused at any step takes no further part.
|
||||
3. **The declarations**: bearer-receipts §6.2 step 4.
|
||||
4. **The schedules**: the accepted schedule claims by `bearer_id`. Two with one `bearer_id` and
|
||||
different bodies set aside every schedule of that instance (`schedule_contradicted`): a
|
||||
publisher that changes its plan for one transmission has stated neither, and a relying
|
||||
party holds no promise that can change under it. Two with the same body are one. A schedule
|
||||
is then set aside at the first of these that holds: step 3 set aside the declarations of its
|
||||
`bearer_id` (`bearer_contradicted`); no declaration has its `bearer_id`
|
||||
(`bearer_not_declared`); its declaration's `bearer` is `bidirectional`
|
||||
(`schedule_on_bidirectional`), since a receiver with a path back asks for what it lacks and
|
||||
is not served by a plan. Otherwise it **stands**.
|
||||
5. **Availability**, for each entry of each standing schedule, by §6.3.
|
||||
|
||||
### 6.3 Availability
|
||||
|
||||
For an entry, pass *j* ends at the instant `first_ms` + (*j* − 1) · `period_ms` +
|
||||
`duration_ms`. For each source block of *K* source symbols, with *m* = *K* + `repair` and *x* =
|
||||
⌊*L* · *m* / 1,000,000⌋ the most a receiver within *L* misses of it in one pass, the first pass
|
||||
after which a receiver within *L* holds the block is, under §5.3:
|
||||
|
||||
- **`raptorq`, `advance`**: the least *j* from 1 to `count` with *j* · (*m* − *x*) ≥ *K′* + *h*.
|
||||
Every pass adds *m* − *x* symbols none before it sent.
|
||||
- **`raptorq`, `repeat`**: 1 if *m* − *x* ≥ *K′* + *h*, and none otherwise. A later pass sends
|
||||
the same ESIs, and the ones a receiver missed may be missed again: within *L*, nothing it adds
|
||||
is certain.
|
||||
- **`no-code`**: 1 if *x* is 0, and none otherwise, for the same reason.
|
||||
|
||||
The object is **available** at the end of the greatest of its blocks' passes, or is **not
|
||||
available within the schedule** if any block has none. A verifier reports for each entry its
|
||||
`available_ms` (or `null`), the pass, `bytes_sent` (§5.4), and, for `raptorq`, the RFC 6330
|
||||
§5.8 bound at *h* as `failure_one_in` (100, 10,000 or 1,000,000); for `no-code` it is `null`, a
|
||||
block held being recovered exactly.
|
||||
|
||||
### 6.4 Statements offered about an object or a group
|
||||
|
||||
A verifier **MAY** be asked to check a statement, on a bearer instance, for a loss bound *L* and
|
||||
an overhead *h*:
|
||||
|
||||
- **object *i* is held by *t***: holds when a standing schedule of the instance has an entry for
|
||||
object *i* that is available at an instant at or before *t*. It is refused when no standing
|
||||
schedule of the instance has one (`object_not_scheduled`), when it is not available within
|
||||
the schedule (`availability_not_within_schedule`), and when it is available after *t*
|
||||
(`availability_after_deadline`).
|
||||
- **the group is held by *t***: holds when the statement holds for every object of the group,
|
||||
0 to `leaf_count` − 1, and is refused with the first object's reason otherwise.
|
||||
|
||||
A statement that holds says what the publisher planned: a receiver within *L*, hearing every
|
||||
pass the schedule names, holds the object by *t*, but for a decoding failure no more likely than
|
||||
`failure_one_in` states. It does not say the passes were sent (the declaration's) or heard (a
|
||||
reconciliation's).
|
||||
|
||||
## 7. The passes a receiver listens to
|
||||
|
||||
A receiver that holds a standing schedule's entry, the instant *t* it is at, its loss bound *L*,
|
||||
the overhead *h* it decodes at, and, for each source block, the number *n* of distinct encoding
|
||||
symbols of it that it holds (for `no-code`, of its source symbols), derives the passes to listen
|
||||
to. The loss bound and overhead are checked as §6.2 checks them, and a holding that does not
|
||||
give one number for each source block of the object, in the order §5.1 and §5.2 divide it into
|
||||
them, or that holds more source symbols of a `no-code` block than it has, is `holding_invalid`.
|
||||
Only passes that start at or after *t* count: a pass under way at *t* has sent symbols the
|
||||
receiver did not hear. A pass adds, to a block not held, with *m* and *x* as §6.3:
|
||||
|
||||
- **`raptorq`, `advance`**: *m* − *x*, none of them sent before;
|
||||
- **`raptorq`, `repeat`** and **`no-code`**: max(0, *m* − *n* − *x*): of the *m* − *n* ESIs it
|
||||
lacks, *x* may be the ones it misses again.
|
||||
|
||||
The passes are taken in order of their start. While some block is not held, the next pass is
|
||||
listened to if it adds to a block not held, each such block's *n* growing by what it adds; the
|
||||
first pass that adds to none ends the plan. The plan is the passes listened to, each with its
|
||||
start and end; `complete_ms`, the instant the object is held: *t* when it already is, the last
|
||||
pass's end when the passes listened to suffice, and `null` otherwise; and `listen_ms`, the sum
|
||||
of their durations: the time the receiver's radio must be on. Outside those passes it **MAY** be
|
||||
off. Under `repeat` and `no-code` at most one pass adds anything certain within *L*, since after
|
||||
it a block lacks only symbols the receiver may miss again: a receiver that one pass does not
|
||||
complete is not certain to be held after any number, and keeps listening, if it does, on its own
|
||||
judgement of its channel.
|
||||
|
||||
## 8. Mappings
|
||||
|
||||
A bearer that carries files with FEC states the same parameters in its own description of each
|
||||
file, for its receivers: on FLUTE (RFC 6726) and the bearers built on it, the FEC Object
|
||||
Transmission Information of each file in the file delivery table. A schedule restates them,
|
||||
signed by the publisher, beside the group's receipts, so a relying party with no access to the
|
||||
bearer can check a promise (§6.4) and a receiver can plan before it tunes (§7). A sink that holds
|
||||
both compares them; a difference is the bearer's, and the sink reports it rather than choosing
|
||||
one. The bearer's own repetition (its carousel of descriptions and files) is the schedule's
|
||||
passes: a pass is one transmission of the object's symbols, whatever the bearer calls it.
|
||||
|
||||
## 9. Reasons
|
||||
|
||||
The codes this extension adds, each with where it is raised:
|
||||
|
||||
| reason | raised at |
|
||||
|---|---|
|
||||
| `schedule_not_scheduled` | §4.2 rule 1 |
|
||||
| `schedule_objects_malformed` | §4.2 rule 2 |
|
||||
| `schedule_oti_invalid` | §4.2 rule 3.1 |
|
||||
| `schedule_symbols_invalid` | §4.2 rule 3.2 |
|
||||
| `schedule_passes_invalid` | §4.2 rule 3.3 |
|
||||
| `schedule_subject_mismatch` | §4.3 step 1 |
|
||||
| `schedule_signer_mismatch` | §4.3 step 2 |
|
||||
| `schedule_leaf_out_of_range` | §4.3 step 3 |
|
||||
| `schedule_before_group` | §4.3 step 4 |
|
||||
| `schedule_content_mismatch` | §4.3 step 5 |
|
||||
| `schedule_length_mismatch` | §4.3 step 5 |
|
||||
| `schedule_contradicted` | §6.2 step 4 |
|
||||
| `schedule_on_bidirectional` | §6.2 step 4 |
|
||||
| `loss_bound_invalid` | §6.2, §7 |
|
||||
| `overhead_invalid` | §6.2, §7 |
|
||||
| `holding_invalid` | §7 |
|
||||
| `object_not_scheduled` | §6.4 |
|
||||
| `availability_not_within_schedule` | §6.4 |
|
||||
| `availability_after_deadline` | §6.4 |
|
||||
|
||||
A schedule also raises `bearer_contradicted` and `bearer_not_declared` (bearer-receipts §10) at
|
||||
§6.2 step 4; a group or object the codes of jwp-receipts §3.3; and a claim every code of
|
||||
jwp-receipts §3.1 steps 1 to 5 and those of step 6 named in §4.3 and bearer-receipts §4.3.
|
||||
|
||||
## 10. Conformance
|
||||
|
||||
[`live-conformance/carousel/`](../live-conformance/carousel/) holds the corpus, written from
|
||||
fixed seeds by `wai_carousel_vectors gen` as `vectors.json`. It has:
|
||||
|
||||
- **Availability**: RaptorQ objects of one and of several source blocks of two sizes, under
|
||||
`advance` and `repeat`, at loss bounds of zero and above, reaching a later pass, the last
|
||||
pass, and none; at each overhead; a Compact No-Code object available at the first pass with
|
||||
no loss and at none with any; a group of several objects; the same object simulcast on two
|
||||
instances with different schedules.
|
||||
- **Statements**: an object and a group held by a deadline, after it, not within the schedule,
|
||||
and not scheduled.
|
||||
- **Plans**: a receiver tuning in before the first pass, during one, between passes, holding
|
||||
part of an object, holding all of it, and one under `repeat` that one pass does not complete.
|
||||
- **Negative controls**, one for each reason of §9 that a claim or a verdict raises, and the
|
||||
bounds of §5.1, §5.2 and §5.4 on both sides.
|
||||
|
||||
A conforming verifier gives every case the outcome, the reason and everything else the corpus
|
||||
states. `wai_carousel_vectors verify` and the test `tests/carousel.rs` check that the file
|
||||
regenerates byte for byte, that `wai-rs` gives every case its outcome, that every reason of §9
|
||||
appears, that §9 lists exactly the reference's codes, and that Appendix A is the reference's
|
||||
table. Against an independent implementation of RFC 6330, the test also requires the reference's
|
||||
*K′* and Partition[] to agree for every block size, and decodes each RaptorQ object a case holds
|
||||
available at a pass: it encodes the object under the entry's parameters, drops in each pass the
|
||||
first ⌊*L* · *m* / 1,000,000⌋ symbols of each block (in the first pass, source symbols, so the
|
||||
decoder must decode), and requires the object back from what is left by that pass's end.
|
||||
`live-conformance/carousel_verify.py` is the second implementation, written from this text: it
|
||||
shares no code with `wai-rs`; it verifies groups and objects with the JWP corpus's second
|
||||
implementation (`jwp-conformance/jwp_verify.py`), and claims through steps 1–5 with the claim
|
||||
corpus's (`claim-conformance/claim_verify.py`), whose step 4 holds §4.2 with §5.1, §5.2 and
|
||||
§5.4; it reads Appendix A and §9 from this text, writes §4.3, §5.3, §6 and §7 itself, mutates
|
||||
copies of the corpus and requires every copy to fail, and drops each of its own checks in turn
|
||||
and requires the corpus to catch the weakened verifier.
|
||||
|
||||
## 11. Security and privacy considerations
|
||||
|
||||
- **A schedule is the publisher's word.** Nothing checks that the passes were sent as planned;
|
||||
a declaration attests transmission and no more, and a relying party that needs to know what
|
||||
receivers held counts reconciliations (bearer-receipts §6). A schedule that promises more
|
||||
than was sent is a false statement signed by the group's key, and is evidence of it.
|
||||
- **A loss bound is the relying party's.** Every statement holds for every pattern of loss
|
||||
within it (§2). A channel that exceeds it, in a fade or under interference, voids nothing in
|
||||
the schedule and nothing here predicts how often that happens.
|
||||
- **It discloses the plan, not the audience.** A schedule names objects, times and code
|
||||
parameters, which the bearer's own file descriptions already carry to every receiver. A
|
||||
receiver that plans with it (§7) sends nothing, and a unidirectional bearer's property that
|
||||
the source learns nothing of its receivers (unidirectional-delivery §2) is kept.
|
||||
- **Bounded work.** An entry's arithmetic is a few products per source block, and an object has
|
||||
at most 255 RaptorQ blocks of two sizes or Compact No-Code blocks of two sizes; a schedule's
|
||||
cost is linear in its entries, whatever the passes' count.
|
||||
|
||||
## Appendix A. The values of K′
|
||||
|
||||
The column *K′* of RFC 6330 Table 2 (§5.6), in increasing order, restated so that §5.3 can be
|
||||
applied from this text. The other columns of that table are the code's, and nothing here uses
|
||||
them.
|
||||
|
||||
<!-- k-prime:begin -->
|
||||
```
|
||||
10, 12, 18, 20, 26, 30, 32, 36, 42, 46, 48, 49, 55, 60, 62, 69,
|
||||
75, 84, 88, 91, 95, 97, 101, 114, 119, 125, 127, 138, 140, 149, 153, 160,
|
||||
166, 168, 179, 181, 185, 187, 200, 213, 217, 225, 236, 242, 248, 257, 263, 269,
|
||||
280, 295, 301, 305, 324, 337, 341, 347, 355, 362, 368, 372, 380, 385, 393, 405,
|
||||
418, 428, 434, 447, 453, 466, 478, 486, 491, 497, 511, 526, 532, 542, 549, 557,
|
||||
563, 573, 580, 588, 594, 600, 606, 619, 633, 640, 648, 666, 675, 685, 693, 703,
|
||||
718, 728, 736, 747, 759, 778, 792, 802, 811, 821, 835, 845, 860, 870, 891, 903,
|
||||
913, 926, 938, 950, 963, 977, 989, 1002, 1020, 1032, 1050, 1074, 1085, 1099, 1111, 1136,
|
||||
1152, 1169, 1183, 1205, 1220, 1236, 1255, 1269, 1285, 1306, 1347, 1361, 1389, 1404, 1420, 1436,
|
||||
1461, 1477, 1502, 1522, 1539, 1561, 1579, 1600, 1616, 1649, 1673, 1698, 1716, 1734, 1759, 1777,
|
||||
1800, 1824, 1844, 1863, 1887, 1906, 1926, 1954, 1979, 2005, 2040, 2070, 2103, 2125, 2152, 2195,
|
||||
2217, 2247, 2278, 2315, 2339, 2367, 2392, 2416, 2447, 2473, 2502, 2528, 2565, 2601, 2640, 2668,
|
||||
2701, 2737, 2772, 2802, 2831, 2875, 2906, 2938, 2979, 3015, 3056, 3101, 3151, 3186, 3224, 3265,
|
||||
3299, 3344, 3387, 3423, 3466, 3502, 3539, 3579, 3616, 3658, 3697, 3751, 3792, 3840, 3883, 3924,
|
||||
3970, 4015, 4069, 4112, 4165, 4207, 4252, 4318, 4365, 4418, 4468, 4513, 4567, 4626, 4681, 4731,
|
||||
4780, 4838, 4901, 4954, 5008, 5063, 5116, 5172, 5225, 5279, 5334, 5391, 5449, 5506, 5566, 5637,
|
||||
5694, 5763, 5823, 5896, 5975, 6039, 6102, 6169, 6233, 6296, 6363, 6427, 6518, 6589, 6655, 6730,
|
||||
6799, 6878, 6956, 7033, 7108, 7185, 7281, 7360, 7445, 7520, 7596, 7675, 7770, 7855, 7935, 8030,
|
||||
8111, 8194, 8290, 8377, 8474, 8559, 8654, 8744, 8837, 8928, 9019, 9111, 9206, 9303, 9400, 9497,
|
||||
9601, 9708, 9813, 9916, 10017, 10120, 10241, 10351, 10458, 10567, 10676, 10787, 10899, 11015, 11130, 11245,
|
||||
11358, 11475, 11590, 11711, 11829, 11956, 12087, 12208, 12333, 12460, 12593, 12726, 12857, 13002, 13143, 13284,
|
||||
13417, 13558, 13695, 13833, 13974, 14115, 14272, 14415, 14560, 14713, 14862, 15011, 15170, 15325, 15496, 15651,
|
||||
15808, 15977, 16161, 16336, 16505, 16674, 16851, 17024, 17195, 17376, 17559, 17742, 17929, 18116, 18309, 18503,
|
||||
18694, 18909, 19126, 19325, 19539, 19740, 19939, 20152, 20355, 20564, 20778, 20988, 21199, 21412, 21629, 21852,
|
||||
22073, 22301, 22536, 22779, 23010, 23252, 23491, 23730, 23971, 24215, 24476, 24721, 24976, 25230, 25493, 25756,
|
||||
26022, 26291, 26566, 26838, 27111, 27392, 27682, 27959, 28248, 28548, 28845, 29138, 29434, 29731, 30037, 30346,
|
||||
30654, 30974, 31285, 31605, 31948, 32272, 32601, 32932, 33282, 33623, 33961, 34302, 34654, 35031, 35395, 35750,
|
||||
36112, 36479, 36849, 37227, 37606, 37992, 38385, 38787, 39176, 39576, 39980, 40398, 40816, 41226, 41641, 42067,
|
||||
42490, 42916, 43388, 43840, 44279, 44729, 45183, 45638, 46104, 46574, 47047, 47523, 48007, 48489, 48976, 49470,
|
||||
49978, 50511, 51017, 51530, 52062, 52586, 53114, 53650, 54188, 54735, 55289, 55843, 56403
|
||||
```
|
||||
<!-- k-prime:end -->
|
||||
208
wai/extensions/holdings.md
Normal file
208
wai/extensions/holdings.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# WAI Extension: Holdings
|
||||
|
||||
> Status: Draft. Registers the record `wai.delivery.holdings`: a node's signed statement of which
|
||||
> objects of which groups it holds now, for its neighbours. A receiver that stores what a
|
||||
> broadcast carried, a neighbourhood cache or a peer can serve those objects over a short link,
|
||||
> and a neighbour that knows who holds what fetches from them before it spends the backhaul. A
|
||||
> holdings record (`WHD1`, §4) lists, for each group a node holds objects of, the group's link
|
||||
> hash, its object count and either `whole` or a bitmap of the objects held: exact, so a
|
||||
> neighbour never goes to a node for an object it does not hold, and compact where objects are
|
||||
> held as groups are delivered. A verifier checks a record's form, signature and freshness and
|
||||
> holds it to the group receipts it has (§5), answers whether a holder holds an object, and
|
||||
> derives which wanted objects each neighbour can serve and which must come from the origin
|
||||
> (§5.4). An object a neighbour serves is verified as every object is (jwp-receipts §3); a
|
||||
> record is never evidence of an object's bytes. WAI defines no transport. The reference
|
||||
> implementation is the `holdings` module of `wai-rs` (feature `holdings`); the corpus is
|
||||
> [`live-conformance/holdings/`](../live-conformance/holdings/), and its second implementation is
|
||||
> `live-conformance/holdings_verify.py`.
|
||||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||||
|
||||
## 1. Scope
|
||||
|
||||
In scope: the holdings record, its form, digest and signature (§4); what a verifier derives from
|
||||
records and the group receipts it has, at an instant (§5); and how a fetch from a neighbour is
|
||||
verified (§6).
|
||||
|
||||
Out of scope: how records travel (a neighbourhood's own announcements, a file beside a bearer's
|
||||
carousel, a request); how a node chooses what to keep; how a neighbour is reached or what a link
|
||||
costs, which the asker orders its neighbours by itself; and objects not delivered under a group
|
||||
receipt, which have no holdings here.
|
||||
|
||||
Relationship to the core spec: `wai.delivery.holdings` is a **record** ([SPEC](../SPEC.md) §5).
|
||||
It names no payload format and is not dispatchable.
|
||||
|
||||
## 2. Terms
|
||||
|
||||
- **Group**, **link hash**, **leaf count**: as [jwp-receipts](jwp-receipts.md) §2.4.1 and §3
|
||||
step 3. Object *i* of a group is its object at position *i*, from 0.
|
||||
- **Holder**: the node a record speaks for, named by its Ed25519 key. **Asker**: a node that wants
|
||||
objects and reads its neighbours' records.
|
||||
- **Instant**: a count of milliseconds since 1970-01-01T00:00:00Z, leap seconds not counted, as
|
||||
[channel-lineup](channel-lineup.md) writes one.
|
||||
- **Bitmap**: object *i* is bit `0x80 >> (i mod 8)` of byte ⌊*i* / 8⌋, as a reconciliation's
|
||||
`received` is ([bearer-receipts](bearer-receipts.md) §5.1).
|
||||
|
||||
## 3. Why groups and bitmaps
|
||||
|
||||
Everything WAI delivers is delivered under a group receipt that names its objects by position, and
|
||||
a node that holds objects holds them as they arrived: a whole group, or most of one, from a
|
||||
broadcast's carousel or a relay. A bitmap per group is exact and costs a bit an object; a group
|
||||
held whole costs 41 bytes. A probabilistic filter over content hashes costs over twenty bits an
|
||||
object at a one-in-a-million false-positive rate and sends an asker, now and then, after an
|
||||
object its neighbour does not hold: on a constrained link that is a wasted exchange each time.
|
||||
Naming objects by group and position also lets an asker check a record against the receipts it
|
||||
holds (§5.2), and fetch an object it knows the position of without knowing its bytes.
|
||||
|
||||
## 4. The record
|
||||
|
||||
### 4.1 Layout
|
||||
|
||||
Integers are unsigned and big-endian.
|
||||
|
||||
| size | field | meaning |
|
||||
|---|---|---|
|
||||
| 4 | `magic` | `WHD1` (ASCII) |
|
||||
| 1 | `version` | 1 in this revision |
|
||||
| 32 | `holder` | the holder's Ed25519 key |
|
||||
| 8 | `as_of_ms` | u64: the instant the record states the holdings at |
|
||||
| 8 | `expires_ms` | u64: the instant after which it states nothing |
|
||||
| 4 | `n` | u32: the entries' count |
|
||||
| … | `entries` | `n` entries, §4.2, in strictly increasing order of `group`'s bytes |
|
||||
| 64 | `sig` | §4.3 |
|
||||
|
||||
### 4.2 An entry
|
||||
|
||||
| size | field | meaning |
|
||||
|---|---|---|
|
||||
| 32 | `group` | the group receipt's link hash |
|
||||
| 8 | `leaf_count` | u64: the group's leaf count |
|
||||
| 1 | `form` | 0: the objects `held` names; 1: every object of the group |
|
||||
| ⌈`leaf_count` / 8⌉ | `held` | form 0 only: the bitmap of the objects held |
|
||||
|
||||
### 4.3 Digest and signature
|
||||
|
||||
The **digest** of a record is SHA-256 over its bytes from `magic` through its last entry. The
|
||||
**signature** is Ed25519 by `holder` over `wai:holdings` (ASCII) and 0x01, followed by the bytes
|
||||
the digest covers.
|
||||
|
||||
### 4.4 Reading and rules
|
||||
|
||||
A reader reads exactly the bytes it is given as one record, in the order of §4.1, and refuses it
|
||||
`holdings_malformed` when the bytes are more than 1,048,576, end before a field the layout
|
||||
requires or go on after `sig`, or when:
|
||||
|
||||
1. its magic is not `WHD1` or its version not 1;
|
||||
2. `as_of_ms` is not below `expires_ms`, or `expires_ms` is above 2^53 − 1;
|
||||
3. `n` is 0, or the entries' `group` values are not in strictly increasing order of their bytes;
|
||||
4. an entry's `leaf_count` is 0 or above 2^53 − 1, or its `form` is not 0 or 1;
|
||||
5. a form-0 entry's `held` sets a bit at or past `leaf_count`, sets none (a node holding nothing
|
||||
of a group lists no entry for it), or sets every bit (that holding is form 1).
|
||||
|
||||
So each holding has exactly one record form, and two records of one holder at one instant that
|
||||
state the same holdings are the same bytes. A record that reads is then refused
|
||||
`holdings_signature_invalid` when its signature does not verify under `holder` as SPEC §7
|
||||
*Signatures* verifies every signature: a key that is not the canonical encoding of a point of
|
||||
large order fails, as does a signature whose scalar is not below the group order.
|
||||
|
||||
## 5. What a verifier derives
|
||||
|
||||
### 5.1 At an instant
|
||||
|
||||
A record that reads and verifies is held to the verifier's instant *t*: it is refused
|
||||
`holdings_not_yet` when *t* is below `as_of_ms`, and `holdings_expired` when *t* is at or past
|
||||
`expires_ms`. A verifier does not stretch either bound; one whose clock it does not trust
|
||||
**SHOULD** say what instant it used.
|
||||
|
||||
### 5.2 Against the group receipts held
|
||||
|
||||
For each group receipt the verifier holds that passes jwp-receipts §3 steps 1 and 2, an entry
|
||||
whose `group` is its link hash **MUST** have its `leaf_count` (`holdings_leaf_count_mismatch`).
|
||||
A record with one such entry is refused whole: a holder that misstates a group misstates its
|
||||
holdings. An entry for a group the verifier holds no receipt of is kept: it cannot be checked
|
||||
yet, and an asker that fetches an object of it verifies that object under its receipt (§6).
|
||||
|
||||
### 5.3 One holder's records
|
||||
|
||||
Of the records of one `holder` that stand at *t*, the one with the greatest `as_of_ms` stands
|
||||
alone: a node's later record states what it holds now. Two with that same `as_of_ms` and
|
||||
different digests set both aside (`holdings_contradicted`), and records with the same digest are
|
||||
one.
|
||||
|
||||
### 5.4 Who holds what
|
||||
|
||||
A verifier **MAY** be asked whether a record that stands holds object *i* of group *G*: **held**
|
||||
when it has an entry for *G* with *i* below its `leaf_count` and, in form 0, bit *i* set; refused
|
||||
`object_not_held` when its entry has *i* below its `leaf_count` and bit *i* clear;
|
||||
`object_out_of_range` when *i* is at or past its `leaf_count`; and `group_not_advertised` when it
|
||||
has no entry for *G*.
|
||||
|
||||
An asker with its neighbours' records, in the order it prefers them (by link cost, say, which it
|
||||
judges itself), and a list of wanted objects, each a group's link hash and a position, derives for
|
||||
each wanted object the holders whose record stands and holds it, in that order; and the wanted
|
||||
objects no holder holds, which it fetches from the origin. An object held by none is not missing:
|
||||
it is the backhaul's.
|
||||
|
||||
## 6. Fetching from a neighbour
|
||||
|
||||
An object a neighbour serves is an object like any other: the asker verifies it under its object
|
||||
receipt and the group receipt (jwp-receipts §3), `delivered-verified` over the bytes it received,
|
||||
whoever sent them. A record says where to ask, never what the answer is: an object whose bytes do
|
||||
not verify is refused as jwp-receipts §3 refuses it, and a holder that advertised it and did not
|
||||
serve it, or served other bytes, gave the asker one wasted exchange and nothing else.
|
||||
|
||||
## 7. Reasons
|
||||
|
||||
The codes this extension adds, each with where it is raised:
|
||||
|
||||
| reason | raised at |
|
||||
|---|---|
|
||||
| `holdings_malformed` | §4.4 |
|
||||
| `holdings_signature_invalid` | §4.4 |
|
||||
| `holdings_not_yet` | §5.1 |
|
||||
| `holdings_expired` | §5.1 |
|
||||
| `holdings_leaf_count_mismatch` | §5.2 |
|
||||
| `holdings_contradicted` | §5.3 |
|
||||
| `object_not_held` | §5.4 |
|
||||
| `object_out_of_range` | §5.4 |
|
||||
| `group_not_advertised` | §5.4 |
|
||||
|
||||
## 8. Conformance
|
||||
|
||||
[`live-conformance/holdings/`](../live-conformance/holdings/) holds the corpus, written from fixed
|
||||
seeds by `wai_holdings_vectors gen` as `vectors.json`. It has:
|
||||
|
||||
- **Records**: whole and partial groups, a group of one object and of nine (a bitmap's last byte
|
||||
part used), several groups, each with its digest and verdict; and one negative control for each
|
||||
rule of §4.4 and for a signature that does not verify, a non-canonical key among them.
|
||||
- **Verdicts**: records at an instant before, within and at the end of their window; held against
|
||||
receipts, matching, mismatching and unknown; a holder's later record standing alone, and two at
|
||||
one instant contradicting each other.
|
||||
- **Queries and plans**: every answer of §5.4, and an asker's plan over three neighbours, with
|
||||
objects held by several, by one and by none.
|
||||
|
||||
A conforming verifier gives every case the outcome, the reason and everything else the corpus
|
||||
states. `wai_holdings_vectors verify` and the test `tests/holdings.rs` check that the file
|
||||
regenerates byte for byte, that `wai-rs` gives every case its outcome, that every reason of §7
|
||||
appears, and that §7 lists exactly the reference's codes. `live-conformance/holdings_verify.py` is
|
||||
the second implementation, written from this text: it shares no code with `wai-rs`; it verifies
|
||||
group receipts with the JWP corpus's second implementation (`jwp-conformance/jwp_verify.py`),
|
||||
writes §4, §5 and the plans itself, mutates copies of the corpus and requires every copy to fail,
|
||||
and drops each of its own checks in turn and requires the corpus to catch the weakened verifier.
|
||||
|
||||
## 9. Security and privacy considerations
|
||||
|
||||
- **A record discloses what its holder holds**, to everyone it reaches: which groups, and which of
|
||||
their objects. A node **SHOULD** advertise only groups that reached it over a `multicast` or
|
||||
`unidirectional` bearer ([bearer-receipts](bearer-receipts.md) §2), which every receiver in
|
||||
range could hold, and **SHOULD NOT** advertise what it fetched for itself alone. A node **MAY**
|
||||
sign each record with a fresh key, which unlinks its records from each other and from its
|
||||
other statements; a neighbour then cannot tell a node's later record from another node's, and
|
||||
§5.3 does not apply across them.
|
||||
- **A record is its holder's word**, and costs a wasted exchange when it is false (§6). An asker
|
||||
that is sent wrong bytes refuses them and **MAY** stop asking that holder; nothing here scores
|
||||
holders.
|
||||
- **Freshness**: a record past `expires_ms` states nothing. A holder **SHOULD** set an expiry no
|
||||
later than it expects to keep what it lists, since an eviction it does not announce sends
|
||||
askers after objects it no longer has.
|
||||
- **Bounded work**: a record is at most 1,048,576 bytes, read once, in order; a query is a lookup
|
||||
by group and a bit test.
|
||||
|
|
@ -531,7 +531,10 @@ states; and step 6, against the receipts it names that the client holds here:
|
|||
- a `transmission` claim, against the group of an item whose link hash is its subject
|
||||
([bearer-receipts](bearer-receipts.md) §4.3); and a `reconciliation` claim, against that
|
||||
group and the object receipts of the items listed under it here (bearer-receipts §5.3):
|
||||
one that names an object this index does not list is `context_not_held`.
|
||||
one that names an object this index does not list is `context_not_held`;
|
||||
- a `schedule` claim, against the group of an item whose link hash is its subject and the
|
||||
object receipts of the items listed under it here ([carousel](carousel.md) §4.3); an
|
||||
index carries no envelope bytes, so its lengths are not checked here.
|
||||
|
||||
A claim is accepted only when step 6 ran against what it needs. A `measurement` claim
|
||||
whose figure the client does not hold is `figure_not_held`, as §3.1 states; any other
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ In scope:
|
|||
`claim` receipt (§2.4.3) carries one of the others — `energy`,
|
||||
`lawful-basis`, `log-inclusion`, `measurement`, `operating-point`,
|
||||
`stage`, `delivery-gap`, `reconstruct-session`, `consumption`,
|
||||
`transmission`, `reconciliation` (§2.4.5–§2.4.15) — on one
|
||||
`transmission`, `reconciliation`, `schedule` (§2.4.5–§2.4.16) — on one
|
||||
envelope, one canonical form and one signature rule, so a statement other
|
||||
than delivery does not need a field set of its own.
|
||||
- **Objects not delivered** (§3, §4.4): an object a subscriber did not
|
||||
|
|
@ -417,6 +417,7 @@ key.
|
|||
| `consumption` | `claim` | `object` (the first object it names) | §2.4.13 | the agent that consumed the objects: its key, named by its `did:key` |
|
||||
| `transmission` | `claim` | `receipt` (the group transmitted) | §2.4.14 | the group's publisher: the key that signed the group |
|
||||
| `reconciliation` | `claim` | `receipt` (the group reconciled) | §2.4.15 | the sink that received the objects, never the publisher |
|
||||
| `schedule` | `claim` | `receipt` (the group scheduled) | §2.4.16 | the group's publisher: the key that signed the group |
|
||||
|
||||
Rules:
|
||||
|
||||
|
|
@ -1342,6 +1343,19 @@ step 2; its class rules are §5.2 there, at step 4; and its context is §5.3 the
|
|||
the group receipt, whose link hash is its `subject` and whose key does not sign it, and the
|
||||
object receipts at the positions it names.
|
||||
|
||||
#### 2.4.16. `class: "schedule"`
|
||||
|
||||
A publisher's plan of when each object of a group passes on a bearer instance with no return
|
||||
path, and with which forward error correction: for each object, its code (RaptorQ or Compact
|
||||
No-Code) and that code's object transmission information, the repair symbols each pass adds,
|
||||
and the times and count of its passes. It attests a plan, not a transmission or a delivery, and
|
||||
it sits beside the group receipt and the instance's transmission claim, whose bytes it leaves
|
||||
unchanged. [carousel](carousel.md) defines it: its body's members are §4.1 there
|
||||
(`bearer_id`, `objects` and `attests`), read at §3.1 step 2; its class rules are §4.2 there, with
|
||||
§5's bounds on each code's parameters, at step 4; and its context is §4.3 there, at step 6: the
|
||||
group receipt, whose link hash is its `subject` and whose key signs it, and the object receipts
|
||||
and envelopes at the positions it names.
|
||||
|
||||
### 2.5. Worked example
|
||||
|
||||
Manifest JSON (group receipt, formatted for readability — wire form
|
||||
|
|
@ -2088,7 +2102,7 @@ order listed.
|
|||
defines here (`record`, `binding_type`, a bracket's `opening`, a build's
|
||||
`profile`, a stage's `tier`, a stage object's `status` and `evidence`, a
|
||||
consumption claim's usage `vocabulary`, a transmission or reconciliation
|
||||
claim's `bearer`, a gap's `reason`, a session's
|
||||
claim's `bearer`, a schedule entry's `fec` and `esi`, a gap's `reason`, a session's
|
||||
`framing`, its host's `profile`, and the paths
|
||||
of its decoder entries and switches); a date-time that is not one of §2.4.3 (a gap's
|
||||
`observed_at`); a URI that is not absolute;
|
||||
|
|
@ -2123,7 +2137,9 @@ order listed.
|
|||
decoder entry's grammar and tier, the switches' reasons and order, the
|
||||
missed presentations, the order of its stage sets, its counted bytes and
|
||||
its classical rendition; in a `transmission` or `reconciliation` claim, its
|
||||
`attests`, a transmission's energy figure and a reconciliation's bitmap;
|
||||
`attests`, a transmission's energy figure and a reconciliation's bitmap; in a
|
||||
`schedule` claim, its `attests`, the order of its entries, and each entry's code
|
||||
parameters, symbols and passes;
|
||||
and, in a `consumption` claim, the order of its
|
||||
objects, usage entries and seen digests, its subject, its terms and its
|
||||
`signer_id`.
|
||||
|
|
@ -2178,7 +2194,9 @@ order listed.
|
|||
`reconciliation` claim against that group receipt and the object receipts
|
||||
at the positions it names, each verified by inclusion at least, as
|
||||
[bearer-receipts](bearer-receipts.md) §4.3 and §5.3 order them (§2.4.14,
|
||||
§2.4.15). A group receipt is verified here under §3 steps 1 and 2, and one
|
||||
§2.4.15); and a `schedule` claim against the group receipt its subject names and
|
||||
the object receipts and envelopes at the positions it names, as
|
||||
[carousel](carousel.md) §4.3 orders them (§2.4.16). A group receipt is verified here under §3 steps 1 and 2, and one
|
||||
that fails them is `context_invalid`. A
|
||||
declaration is verified here under steps 1–4, and one that fails
|
||||
them — or is not JSON — is `context_invalid`: its members,
|
||||
|
|
@ -2276,6 +2294,11 @@ and never folds it in: confirmation changes the outcome of no step here.
|
|||
| 4 | `transmission_not_transmitted` | a `transmission` claim's `attests` is not `transmitted` ([bearer-receipts](bearer-receipts.md) §4.2) |
|
||||
| 4 | `reconciliation_not_received` | a `reconciliation` claim's `attests` is not `received` (bearer-receipts §5.2) |
|
||||
| 4 | `reconciliation_bitmap_malformed` | its `leaf_count` is 0, its `received` is not ⌈`leaf_count` / 8⌉ bytes, sets a bit at or past `leaf_count`, or sets none |
|
||||
| 4 | `schedule_not_scheduled` | a `schedule` claim's `attests` is not `scheduled` ([carousel](carousel.md) §4.2) |
|
||||
| 4 | `schedule_objects_malformed` | it has no entry, or its entries' `leaf_index` values do not strictly increase |
|
||||
| 4 | `schedule_oti_invalid` | an entry's code parameters are not ones its code admits (carousel §5.1, §5.2) |
|
||||
| 4 | `schedule_symbols_invalid` | an entry's symbols are not ones its code can name (carousel §5.4) |
|
||||
| 4 | `schedule_passes_invalid` | an entry's passes are not well formed: none, of no duration, overlapping, ending past 9999, or sending more than 2<sup>53</sup> − 1 octets |
|
||||
| 6 | `energy_disagrees_with_delivery` | the figure differs from the `object` receipt's |
|
||||
| 6 | `energy_class_disagrees_with_delivery` | the object's leaf is labelled, and the `energy` claim's class or evidence differs from the label |
|
||||
| 6 | `figure_disagrees` | the receipt a `measurement` or `operating-point` claim names carries another figure at `figure_index` |
|
||||
|
|
@ -2322,6 +2345,12 @@ and never folds it in: confirmation changes the outcome of no step here.
|
|||
| 6 | `reconciliation_leaf_count_mismatch` | its `leaf_count` is not the group's |
|
||||
| 6 | `reconciliation_objects_not_held` | it names a position whose object receipt the verifier does not hold: recoverable, never accepted |
|
||||
| 6 | `reconciliation_root_mismatch` | its `received_root` is not the root over the leaves at the positions it names |
|
||||
| 6 | `schedule_subject_mismatch` | a `schedule` claim's `subject` is not the link hash of the group receipt it is checked against ([carousel](carousel.md) §4.3) |
|
||||
| 6 | `schedule_signer_mismatch` | it is not signed by the group's key |
|
||||
| 6 | `schedule_leaf_out_of_range` | an entry's `leaf_index` is not less than the group's `leaf_count` |
|
||||
| 6 | `schedule_before_group` | an entry's first pass starts before the group's `group_start` |
|
||||
| 6 | `schedule_content_mismatch` | an entry's `content_hash` is not that of the object receipt the verifier holds at its position |
|
||||
| 6 | `schedule_length_mismatch` | an entry's transfer length is not the length of the object's envelope the verifier holds |
|
||||
| 6 | `session_stage_disagrees` | a `stage` claim the session names is about a base, or lists an arrived layer object, the session did not receive; counts a unit the session never presented, or more units at a stage than the session presented at it or above; or no claim whose set holds a unit lists an applied layer that reaches a stage the session presented it at |
|
||||
|
||||
### 3.2. Recovery through a soft binding, and the ancestor rule
|
||||
|
|
@ -3544,7 +3573,7 @@ A confirmation needs every one of them:
|
|||
| item | authorities |
|
||||
|---|---|
|
||||
| a `group` receipt, and the `object` receipts under it | `delivery`; and `energy/<class>` for each acquisition class an `object` receipt under it labels, where its leaf binds a label (§2.2), and for the class a labelled group payload signs for the group's total (§2.3) |
|
||||
| a `claim` | its class's authority: `energy/` followed by its `energy_provenance` for `energy`, for example `energy/OnChipCounter`; `lawful-basis/declaration` or `lawful-basis/decision` by its `record`; its class name for every other class — `log-inclusion`, `soft-binding`, `soft-binding-span`, `binding-recovery`, `measurement`, `operating-point`, `stage`, `delivery-gap`, `reconstruct-session`, `consumption`, `transmission` and `reconciliation`. And `energy/` followed by its `energy_provenance` whenever its body carries that member, whatever its class |
|
||||
| a `claim` | its class's authority: `energy/` followed by its `energy_provenance` for `energy`, for example `energy/OnChipCounter`; `lawful-basis/declaration` or `lawful-basis/decision` by its `record`; its class name for every other class — `log-inclusion`, `soft-binding`, `soft-binding-span`, `binding-recovery`, `measurement`, `operating-point`, `stage`, `delivery-gap`, `reconstruct-session`, `consumption`, `transmission`, `reconciliation` and `schedule`. And `energy/` followed by its `energy_provenance` whenever its body carries that member, whatever its class |
|
||||
| the energy figure of a `reconstruct-session` claim whose `energy` is not `null` | `reconstruct-session`, and `energy/` followed by `energy.energy_provenance` |
|
||||
| the energy figure of a `transmission` claim whose `energy` is not `null` | `transmission`, and `energy/` followed by `energy.energy_provenance` |
|
||||
| a receipt of another WAI family that signs its own `signer_pubkey` and `signer_id` | `receipt`; and `energy/<class>` for each class its figures carry a label for |
|
||||
|
|
@ -3648,7 +3677,9 @@ its keys is that party is the origin's decision, and its log records it:
|
|||
a second item, as a session's is: it needs `transmission` and the energy authority of its
|
||||
class, and the declaration stands without it. A sink with no return path confirms under
|
||||
anchors provisioned before it was cut off, and a key log that reaches it over the bearer
|
||||
anchors nothing ([bearer-receipts](bearer-receipts.md) §8).
|
||||
anchors nothing ([bearer-receipts](bearer-receipts.md) §8). `schedule` lets a key state the
|
||||
passes of a group it signed over an instance (§2.4.16); an origin gives it to its publishing
|
||||
keys, as it gives `transmission`, and it is neither pinned-only nor `delivery`.
|
||||
- **Log inclusion.** A confirmation says under whose authority the log's
|
||||
evidence was attached. It does not check `log_receipt`: §2.4.7 is
|
||||
unchanged.
|
||||
|
|
|
|||
689
wai/extensions/media-identify.md
Normal file
689
wai/extensions/media-identify.md
Normal file
|
|
@ -0,0 +1,689 @@
|
|||
# WAI Extension: Media Identification
|
||||
|
||||
> Status: Draft. What a file is, from its bytes alone: the formats they fit, each with the bytes
|
||||
> that say so and how far they were followed; what a registered probe reads of it; signatures
|
||||
> found inside it; and measures that hold when no format is recognised. It decodes nothing, and
|
||||
> what it does not establish it leaves out. The reference implementation is the `identify` module
|
||||
> of `wai-rs` (feature `identify`), which `wai identify` runs (operation `media.inspect`); the
|
||||
> corpus is [`identify-conformance/`](../identify-conformance/), and its second implementation is
|
||||
> `identify-conformance/identify_verify.py`.
|
||||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||||
|
||||
## 1. Scope
|
||||
|
||||
§1.1 `wai inspect` reads what an envelope holds. Most files a toolchain meets are not envelopes,
|
||||
and the first question about one is what it is. Identification answers that for any file, and
|
||||
it is the first step of working out how a piece of media was made.
|
||||
|
||||
§1.2 A result states only what the bytes establish. A claim comes with the offsets of the bytes
|
||||
that make it, and a walk says where it stopped and why. A format is never guessed from a file
|
||||
name, and no confidence figure is given: a result has a strength (§4), which says what was
|
||||
checked. Where the bytes fit no format, the verdict is `unknown` and the measures (§8) are what
|
||||
is known.
|
||||
|
||||
§1.3 What a container states is not what its samples hold. A track row (§5.1) reports what the file
|
||||
says of a track, and no sample is read to confirm it. No payload is decrypted, and a protected track
|
||||
is reported as protected and no further.
|
||||
|
||||
§1.4 This extension adds no capability and changes no dispatch (SPEC §4). A capability in a result
|
||||
is the registered capability whose payload a format or track is: it says what `wai wrap` would
|
||||
carry it under, not that a decoder here accepts it.
|
||||
|
||||
## 2. The result
|
||||
|
||||
§2.1 A result is a JSON object:
|
||||
|
||||
| member | value |
|
||||
|---|---|
|
||||
| `format` | `wai.identify/1` |
|
||||
| `size` | the file's length in bytes |
|
||||
| `verdict` | `identified`, `ambiguous`, `signature_only` or `unknown` (§4.2) |
|
||||
| `candidates` | the formats the bytes fit, in the order of §4.3 |
|
||||
| `probes_accepted` | the capabilities whose probe accepted the bytes (§3.5) |
|
||||
| `embedded` | signatures found inside the file (§7), each `{at, id}` |
|
||||
| `embedded_capped` | whether more were found than `embedded` holds |
|
||||
| `measures` | §8 |
|
||||
|
||||
§2.2 A candidate is a JSON object:
|
||||
|
||||
| member | value |
|
||||
|---|---|
|
||||
| `id` | the format, as §5 and §6 name it, or the capability a probe alone accepted (§3.5) |
|
||||
| `kind` | `wai`, `container`, `audio`, `image`, `elementary`, `playlist`, `text`, `compression` or `capture` |
|
||||
| `strength` | `signature`, `structure` or `parameters` (§4.1) |
|
||||
| `evidence` | the first 8 bytes-that-say-so, each `{at, len, is}`: `len` bytes at offset `at`, which read as `is` |
|
||||
| `walked_to` | the offset a walk of the file's structures reached, or `null` where the format has no such walk |
|
||||
| `stopped` | `null`, or `{at, why}`: where a walk stopped before the file's end (§3.3) |
|
||||
| `facts` | what the format's headers state, by the section that defines it |
|
||||
| `tracks` | §5.1 |
|
||||
| `capabilities` | the capabilities it maps to (§1.4), sorted |
|
||||
|
||||
## 3. Common rules
|
||||
|
||||
§3.1 Integers are read as each section says. `u8`, `be16`, `be24`, `be32` and `be64` are big-endian,
|
||||
`le16` and `le32` little-endian. All arithmetic of a result is integer arithmetic, but for the
|
||||
sample
|
||||
rate of §5.4; two implementations give the same result for the same bytes on any machine.
|
||||
|
||||
§3.2 **What is read.** The **head** is the first 1 MiB of the file (1,048,576 bytes), or all of it
|
||||
if
|
||||
shorter. The walks of §5.5, §5.9, §5.10, §5.13, §5.17, §5.18, §5.19 and §5.20, the table of §6 and
|
||||
the
|
||||
probes read the head alone. The other walks read the file where their lengths lead. The **scan** is
|
||||
the first 16 MiB of the file (16,777,216 bytes), or all of it if shorter: §7 and §8 read it.
|
||||
|
||||
§3.3 **Walks.** A walk reads a format's structures in order and is bounded: at most 4,096 structures
|
||||
(§5.6: 256), and 64 tracks. A walk that stops before the file's end says why, in `stopped`:
|
||||
|
||||
| `why` | meaning |
|
||||
|---|---|
|
||||
| `overrun` | a structure declares, or needs, more than the file holds |
|
||||
| `invalid` | a structure's bytes are not what the format allows there |
|
||||
| `crc` | a checksum the format defines does not hold |
|
||||
| `limit` | the walk reached a bound of this extension, or the end of the head where it reads no further |
|
||||
| `unknown_size` | a structure of unknown size, whose end only reading it would find (§5.4) |
|
||||
|
||||
`walked_to` and a stop's `at` are never past the end of the file: an offset past it is taken as the
|
||||
file's length. A walk that reads the head alone and reaches its end while the file goes on has
|
||||
stopped
|
||||
there, `limit`.
|
||||
|
||||
§3.4 **Strings.** A string read from the file enters a result only through this filter: its first
|
||||
64 bytes, each of 0x20 to 0x7E but `\` as itself, and any other byte as `\xNN` in lowercase
|
||||
hexadecimal.
|
||||
A file is untrusted input: a reader **MUST** treat these strings as data, and a tool that gives a
|
||||
result to an agent **MUST** mark it as holding them.
|
||||
|
||||
§3.5 **Probes.** The probes of the table of [format-probes](format-probes.md) §2 that can name a
|
||||
format from the bytes (`wai.image.jxs`, `wai.video.jxs`, `wai.video.hevc`, `wai.video.mvhevc`,
|
||||
`wai.video.vvc`, `wai.video.lcevc`, `wai.audio.mpegh`, `wai.video.avs3`, `wai.audio.avs3`, in that
|
||||
order) each read the head. The other two read a payload declared to be theirs, have no signature,
|
||||
and say nothing of a file they are merely given, so they are not run. For each probe, in order:
|
||||
|
||||
- Where it accepts, the capability joins `probes_accepted`. The first candidate of kind `elementary`
|
||||
that maps to that capability (or, for `wai.video.mvhevc`, to `wai.video.hevc`) has its strength
|
||||
raised
|
||||
to `parameters`, the capability added to its `capabilities`, and the probe's result set in its
|
||||
`facts.probe` under the capability. With no such candidate, a candidate of kind `elementary` is
|
||||
added
|
||||
whose `id` is the capability, of strength `parameters`, with evidence `{at: 0, len: <head's
|
||||
length>,
|
||||
is: "probe"}`, that capability and the same `facts.probe`.
|
||||
- Where it refuses with `no_signature`, nothing is recorded.
|
||||
- Where it refuses otherwise, and a candidate of kind `elementary` already maps to that capability,
|
||||
its
|
||||
`facts.probe_refused` holds the code under the capability.
|
||||
|
||||
## 4. Strength and verdict
|
||||
|
||||
§4.1 **Strength** says how far a candidate's bytes were followed:
|
||||
|
||||
- `signature`: a format's fixed bytes are where the format puts them. Nothing else was checked, and
|
||||
short ones occur by chance: `evidence` shows how many bytes it was.
|
||||
- `structure`: what follows the signature is what the format's section requires of it.
|
||||
- `parameters`: a probe of format-probes read the format's headers and accepted them.
|
||||
|
||||
§4.2 **The verdict** comes from the candidates at the highest strength present: `unknown` if there
|
||||
are
|
||||
no candidates; `ambiguous` if more than one; `signature_only` if one, of strength `signature`; else
|
||||
`identified`. Two formats that both fit are both reported, and no ranking between them is invented.
|
||||
|
||||
§4.3 **Order.** Candidates are in order of strength, strongest first. Within a strength they keep
|
||||
the
|
||||
order of the sections that define them: §5.2 to §5.20, then §6's table in its order, then those a
|
||||
probe alone added, in the order of §3.5.
|
||||
|
||||
## 5. Walks
|
||||
|
||||
§5.1 **Tracks.** A row of `tracks` holds `track` (the identifier the container gives, or `null`),
|
||||
`media` (`video`, `audio`, `text` or `other`), `stated` (what the container calls it, through §3.4),
|
||||
`codec` (the name of the table below, or `null` where this extension has no name for it), and
|
||||
`width`,
|
||||
`height`, `channels`, `sample_rate` and `protected`, each `null` where not stated. `protected`, on a
|
||||
track the container says is protected, is `{scheme, original}`, each `null` where not stated.
|
||||
|
||||
A row's codec adds to its candidate's `capabilities` the capability of this table, where it has one:
|
||||
|
||||
| codec | capability |
|
||||
|---|---|
|
||||
| `avc` | `wai.video.avc` |
|
||||
| `hevc` | `wai.video.hevc` |
|
||||
| `vvc` | `wai.video.vvc` |
|
||||
| `av1` | `wai.video.av1` |
|
||||
| `vp8` | `wai.video.vp8` |
|
||||
| `vp9` | `wai.video.vp9` |
|
||||
| `evc` | `wai.video.evc` |
|
||||
| `lcevc` | `wai.video.lcevc` |
|
||||
| `jpeg-xs` | `wai.video.jxs` |
|
||||
| `jpeg2000` | `wai.video.j2k` |
|
||||
| `aac` | `wai.audio.aac` |
|
||||
| `opus` | `wai.audio.opus` |
|
||||
| `flac` | `wai.audio.flac` |
|
||||
| `ac4` | `wai.audio.ac4` |
|
||||
| `mpegh` | `wai.audio.mpegh` |
|
||||
| `iamf` | `wai.audio.iamf` |
|
||||
|
||||
The names `mp3`, `vorbis`, `theora`, `speex`, `ac3`, `eac3`, `webvtt` and `ttml` map to none.
|
||||
|
||||
§5.2 **WAI envelopes** (`wai`, kind `wai`). The head begins `WAI1` or `WAI2`: evidence `magic:WAI1`
|
||||
or
|
||||
`magic:WAI2`, 4 bytes at 0, and `facts.version`. `le32` at 4 is the manifest's length. If it is
|
||||
absent
|
||||
the walk stops at 0 (`overrun`). If it exceeds 1 MiB the walk stops at 8 (`limit`); if the file
|
||||
holds
|
||||
less the walk stops at 8 (`overrun`); if the manifest is not JSON, at 8 (`invalid`). Evidence
|
||||
`manifest`. For `WAI1`: `facts.capability` is `model_requirement.capability`, and `le32` after the
|
||||
manifest is the payload's length, with evidence `payload_length`; where it or the payload does not
|
||||
fit
|
||||
the walk stops there (`overrun`) with `walked_to` there too, else `walked_to` is past the payload.
|
||||
For
|
||||
`WAI2`: `le16` after the manifest is `facts.renditions`, evidence `rendition_count`, and `walked_to`
|
||||
is
|
||||
past it. A walk that reaches this is `structure`. `wai inspect` reads the rest.
|
||||
|
||||
§5.3 **ISO base media** (`isobmff`, kind `container`). The four bytes at 4 are one of `ftyp`,
|
||||
`styp`,
|
||||
`moov`, `mdat`, `moof`, `free`, `skip`, `wide`, `sidx` or `pnot`. Boxes are walked from 0 to the end
|
||||
of
|
||||
the file. A box: `be32` size and four bytes of type. Every type byte is 0x20 to 0x7E or 0xA9, else
|
||||
`invalid`. Size 0 runs to the end of the file; size 1 means `be64` at 8 is the size, which must be
|
||||
at
|
||||
least 16; any other size below 8 is `invalid`. Fewer bytes than a header needs is `overrun`. Each
|
||||
box whose header
|
||||
reads gives evidence `box:<type>`, 4 bytes at its offset plus 4, and its type joins `facts.boxes`
|
||||
(distinct, first 16); a box that does not end within the file stops the walk there (`overrun`).
|
||||
`moof`
|
||||
boxes are counted in `facts.fragments`, present when there are any.
|
||||
|
||||
`ftyp` that fits: fewer than 8 content bytes is `invalid`; else `facts.major_brand` is its first
|
||||
four,
|
||||
and `facts.compatible_brands` the first 16 of the four-byte brands from its ninth. A major or
|
||||
compatible brand `avif` or `avis` (among its first 4096 bytes) adds `wai.image.avif`.
|
||||
|
||||
`moov` that fits and is at most 64 MiB is read for `trak` boxes (above that, `facts.moov_not_read`
|
||||
is
|
||||
`true`). Inside a box, children are read as the top level is, stopping at the first that does not
|
||||
fit.
|
||||
A track needs `mdia`, and in it `hdlr` (the handler type is at content offset 8) and
|
||||
`minf`/`stbl`/`stsd`
|
||||
with a first entry (the entries start at content offset 8), else it has no row. `track` is from
|
||||
`tkhd`:
|
||||
`be32` at content offset 12 if its first byte is 0, else at 20. `media` from the handler: `vide`,
|
||||
`auxv`,
|
||||
`pict` is `video`; `soun` is `audio`; `text`, `subt`, `sbtl`, `clcp` is `text`; else `other`.
|
||||
`stated` is the
|
||||
entry's type. For `video`: `width` `be16` at entry content 24, `height` at 26, child boxes from 78.
|
||||
For
|
||||
`audio`: `channels` `be16` at 16, `sample_rate` `be32` at 24 shifted right 16, and, where `be16` at
|
||||
8 is
|
||||
0, child boxes from 28.
|
||||
|
||||
An entry of type `encv`, `enca`, `enct` or `encs` is protected: `protected.scheme` is from
|
||||
`sinf`/`schm`
|
||||
(content bytes 4 to 8) and `protected.original` from `sinf`/`frma` (its first four), and the codec
|
||||
is
|
||||
named from `original` where it is present. `mp4a` is named from its `esds`: `aac` for object type
|
||||
0x40,
|
||||
0x66, 0x67 or 0x68, `mp3` for 0x69 or 0x6B, else `null`. The object type is reached as MPEG-4
|
||||
Systems
|
||||
lays it out: after four version bytes, tag 3 and its size (one to four bytes of 7 bits, high bit set
|
||||
on
|
||||
all but the last); two bytes of identifier and one of flags, then 2 bytes if flag 0x80, a length
|
||||
byte
|
||||
and that many if 0x40, and 2 bytes if 0x20; tag 4 and its size; the next byte. Other entry types:
|
||||
|
||||
| entry | codec | entry | codec |
|
||||
|---|---|---|---|
|
||||
| `avc1` `avc2` `avc3` `avc4` `dvav` `dva1` | `avc` | `hvc1` `hev1` `dvh1` `dvhe` | `hevc` |
|
||||
| `vvc1` `vvi1` | `vvc` | `av01` `dav1` | `av1` |
|
||||
| `vp08` | `vp8` | `vp09` | `vp9` |
|
||||
| `evc1` | `evc` | `lvc1` | `lcevc` |
|
||||
| `jxsm` | `jpeg-xs` | `mjp2` | `jpeg2000` |
|
||||
| `Opus` | `opus` | `fLaC` | `flac` |
|
||||
| `ac-3` | `ac3` | `ec-3` | `eac3` |
|
||||
| `ac-4` | `ac4` | `mhm1` `mhm2` `mha1` `mha2` | `mpegh` |
|
||||
| `iamf` | `iamf` | `wvtt` | `webvtt` |
|
||||
| `stpp` | `ttml` | | |
|
||||
|
||||
`structure`: at least two box headers read.
|
||||
|
||||
§5.4 **Matroska and WebM** (`matroska`, kind `container`). The head begins `1A 45 DF A3`: evidence
|
||||
`ebml`, 4 bytes at 0. An element is an identifier of 1 to 4 bytes (its length in the leading zeros
|
||||
of
|
||||
its first byte, the marker kept) and a size of 1 to 8 bytes (the same, the marker removed; all ones
|
||||
is unknown). The header must read in the head, with a known size: else the walk stops at 0
|
||||
(`invalid` for an
|
||||
element that does not parse, `overrun` for one that does not fit). `facts.doc_type` is its 0x4282,
|
||||
through
|
||||
§3.4, and `facts.doc_type_version` its 0x4287. The next element must be the Segment, 0x18538067:
|
||||
evidence
|
||||
`segment`; else the walk stops there, `overrun` if fewer than 5 bytes remain and `invalid`
|
||||
otherwise. A
|
||||
Segment with a known size that passes the file's end sets `facts.segment_overruns_by`, and the walk
|
||||
ends at
|
||||
the file's end.
|
||||
|
||||
Its children are walked to its end. A child that does not parse stops the walk (`overrun` if fewer
|
||||
than 12
|
||||
bytes remain, else `invalid`); an unknown size, `unknown_size`; a child that passes the file's end,
|
||||
`overrun`.
|
||||
Clusters (0x1F43B675) are counted in `facts.clusters`, present when there are any, one that passes
|
||||
the file's
|
||||
end included. Tracks (0x1654AE6B) of at most 1 MiB gives evidence `tracks` and a row for each 0xAE:
|
||||
`track` 0xD7;
|
||||
`media` from 0x83: 1 `video`, 2 `audio`, 17 `text`, else `other`; `stated` and `codec` from 0x86;
|
||||
`width` and
|
||||
`height` 0xB0 and 0xBA in 0xE0; `channels` 0x9F and `sample_rate` 0xB5 in 0xE1, an IEEE float of 4
|
||||
or 8 bytes
|
||||
taken toward zero; `protected` where 0x6D80 holds 0x6240 holding 0x5035. Unsigned values are
|
||||
big-endian, of 1 to
|
||||
8 bytes. The codec is `aac` for an identifier beginning `A_AAC`, else:
|
||||
|
||||
| identifier | codec | identifier | codec |
|
||||
|---|---|---|---|
|
||||
| `V_MPEG4/ISO/AVC` | `avc` | `V_MPEGH/ISO/HEVC` | `hevc` |
|
||||
| `V_MPEGI/ISO/VVC` | `vvc` | `V_AV1` | `av1` |
|
||||
| `V_VP8` | `vp8` | `V_VP9` | `vp9` |
|
||||
| `A_OPUS` | `opus` | `A_FLAC` | `flac` |
|
||||
| `A_VORBIS` | `vorbis` | `A_AC3` | `ac3` |
|
||||
| `A_EAC3` | `eac3` | `A_MPEG/L3` | `mp3` |
|
||||
| `S_TEXT/WEBVTT` | `webvtt` | | |
|
||||
|
||||
`structure`: at least one child read.
|
||||
|
||||
§5.5 **MPEG transport streams** (`mpeg-ts`, kind `container`). For each layout in order, packet size
|
||||
and sync
|
||||
offset (188, 0), (192, 4), (204, 0), and for each start from 0 below the packet size: 0x47 at the
|
||||
sync offset of
|
||||
five packets from there. The first that holds is the layout: evidence `sync`, 1 byte at its first
|
||||
sync,
|
||||
`facts.packet_size`. Packets are read while 188 bytes from a sync offset lie in the head. A packet
|
||||
whose sync byte
|
||||
is not 0x47 stops the walk (`invalid`), at the offset of that byte. PID is 13 bits from byte 1;
|
||||
`facts.scrambled_packets`
|
||||
counts those whose byte 3 has either of its top two bits set. With an adaptation field (byte 3 bit
|
||||
0x20), the payload
|
||||
is past its length byte and its length. With a payload (bit 0x10), PID 0 and each PID the program
|
||||
table names are
|
||||
gathered: a packet with the start bit (byte 1 bit 0x40) begins a section after its pointer field,
|
||||
and a section's
|
||||
length is 3 plus the 12 bits from its second byte. A section is used only if at least 12 bytes long
|
||||
and its MPEG-2
|
||||
checksum (polynomial 0x04C11DB7, from all ones, not reflected, over the section and its checksum) is
|
||||
0.
|
||||
|
||||
The first section of table 0 on PID 0 is the program table: evidence `pat`, each entry of 4 bytes
|
||||
from offset 8 to 4 from
|
||||
its end (the first 64), with program number from the first two bytes and PID from the next 13 bits,
|
||||
program 0 not being
|
||||
counted. `facts.programs` is the number of programs. The first section of table 2 on each such PID:
|
||||
evidence `pmt`.
|
||||
Program information of the 12 bits from offset 10 starts at 12; streams follow, each a type byte, 13
|
||||
bits of PID at the
|
||||
next two bytes and 12 bits of descriptor length at the next two, then that many bytes of
|
||||
descriptors. A row for each,
|
||||
after the last packet: `track` the PID, `stated` `stream_type:0x<two lowercase hexadecimal digits>`,
|
||||
`protected` where a
|
||||
descriptor with tag 9 is among the program's or the stream's (then `{scheme: null, original:
|
||||
null}`). Codec and media:
|
||||
|
||||
| type | codec | media | type | codec | media |
|
||||
|---|---|---|---|---|---|
|
||||
| 0x1B | `avc` | video | 0x24 | `hevc` | video |
|
||||
| 0x33 | `vvc` | video | 0x0F 0x11 | `aac` | audio |
|
||||
| 0x03 0x04 | `mp3` | audio | 0x81 | `ac3` | audio |
|
||||
| 0x87 | `eac3` | audio | 0x01 0x02 0x10 | `null` | video |
|
||||
|
||||
Any other type has codec `null` and media `other`. `facts.packets_read` counts the packets. If no
|
||||
sync byte stopped it,
|
||||
and the walk's end is before the file's: `limit` if the head is shorter than the file, else
|
||||
`overrun`. Always `structure`.
|
||||
|
||||
§5.6 **Ogg** (`ogg`, kind `container`). The head begins `OggS` and a zero byte. Pages from 0: a
|
||||
27-byte header beginning the
|
||||
same, a table of as many bytes as its byte 26, and a body of as many bytes as that table sums to. A
|
||||
page that does not fit is
|
||||
`overrun`; a header that does not begin so, `invalid`; a checksum (`le32` at 22) that does not hold
|
||||
over the page with its checksum
|
||||
bytes zero, under the polynomial 0x04C11DB7 from zero, not reflected, `crc`. At most 256 pages. Each
|
||||
gives evidence `page`, 1 page
|
||||
long, at its offset. Byte 5 bit 0x02 starts a stream: a row with `track` `le32` at 14, read from the
|
||||
body: `OpusHead` (`stated`
|
||||
`OpusHead`, `opus`, audio, `channels` byte 9, `sample_rate` `le32` at 12); `\x01vorbis` (`stated`
|
||||
`vorbis`, `vorbis`, audio, `channels` byte
|
||||
11, `sample_rate` `le32` at 12); `\x7fFLAC` (`stated` `FLAC`, `flac`, audio); `\x80theora` (`stated`
|
||||
`theora`, `theora`, video);
|
||||
`Speex` and three spaces (`stated` `Speex`, `speex`, audio); else `stated` is its first 8 bytes,
|
||||
media `other`. `facts.pages_read`.
|
||||
`structure`: two pages, or one that ends at the file's end.
|
||||
|
||||
§5.7 **ID3 and FLAC** (`flac`, kind `audio`). An **ID3v2 tag** at 0, for this and §5.9: `ID3`, ten
|
||||
header bytes, bytes 3 and 4 not 0xFF,
|
||||
and bytes 6 to 9 each below 0x80. Its length is 10 plus those four as 7 bits each, plus 10 where
|
||||
byte 5 has bit 0x10. A head that begins
|
||||
`ID3` with a header that is not one means neither walk applies. `fLaC` at 0, or after the tag:
|
||||
evidence `fLaC`, and `wai.audio.flac`.
|
||||
Blocks follow: a byte (bit 0x80: last; low 7 bits: type), then `be24` length. The first must be type
|
||||
0 of length 34, else `invalid`:
|
||||
evidence `streaminfo`, 34 bytes at its body, and `facts.sample_rate` (20 bits from body byte 10),
|
||||
`facts.channels` (3 bits, plus 1),
|
||||
`facts.bits_per_sample` (5 bits, plus 1) and `facts.samples` (36 bits). A block past the file's end
|
||||
is `overrun`. After the last block, 2 bytes
|
||||
0xFF and 0xF8 or 0xF9 are a frame's sync: evidence `frame_sync`, and `structure`. Other bytes there
|
||||
stop the walk (`invalid`), and no bytes
|
||||
(`overrun`). `walked_to` is the offset after the last block read.
|
||||
|
||||
§5.8 **RIFF** (kind per form). The head begins `RIFF`, with at least 12 bytes. The form at 8: `WAVE`
|
||||
is `wave`, audio; `AVI ` is `avi`, container;
|
||||
`WEBP` is `webp`, image; any other is `riff`, container. Evidence `form:<form>`, 4 bytes at 8, and
|
||||
`facts.form`. The declared end is `le32` at 4 plus 8,
|
||||
in `facts.declared_size` where it is not the file's size; the walk ends at the lesser of it and the
|
||||
file's size. Chunks from 12: four bytes of
|
||||
type and `le32` length, padded to an even length. A header past the end stops the walk (`overrun`)
|
||||
unless the walk is exactly at the end or beyond it;
|
||||
each header read gives evidence `chunk:<type>`, and its type joins `facts.chunks` (distinct, first
|
||||
16). A chunk's content past the end stops the walk there
|
||||
(`overrun`).
|
||||
|
||||
For `wave`: `fmt ` gives, from at most its first 64 bytes, `facts.format_tag` `le16` at 0 (where
|
||||
that is 0xFFFE, `le16` at 24 instead),
|
||||
`facts.channels` `le16` at 2, `facts.sample_rate` `le32` at 4 and `facts.bits_per_sample` `le16` at
|
||||
14, each `null` where absent; `data` gives `facts.data_bytes`.
|
||||
For `webp`: `VP8 ` with bytes 3 to 5 `9D 01 2A` gives `facts.coding` `lossy`, `facts.width` and
|
||||
`facts.height` from `le16` at 6 and 8 in their low 14 bits;
|
||||
`VP8L` with first byte 0x2F and 5 bytes gives `lossless`, and, in `le32` at 1, `width` its low 14
|
||||
bits plus 1 and `height` the next 14 plus 1; `VP8X` with 10 bytes gives
|
||||
`extended`, and `width` and `height` the 24-bit little-endian values at 4 and 7, each plus 1.
|
||||
`walked_to` is the walk's end, at most the end above. `structure`: one chunk read.
|
||||
|
||||
§5.9 **MPEG audio** (`mpeg-audio`, kind `audio`). From 0 or after an ID3v2 tag (§5.7). A frame:
|
||||
0xFF; 3 high bits of the next byte set; version 2 bits (0: 2.5, 2: 2,
|
||||
3: 1), layer 2 bits (1: III, 2: II, 3: I), the bitrate index in byte 2's high 4 bits (not 0 or 15),
|
||||
rate index in the next 2 (not 3), padding the next bit; byte 3's high 2 bits are
|
||||
the channel mode. The version value 1 and layer value 0 are not a frame. Rate is 44100, 48000 or
|
||||
32000 by index, halved for version 2 and quartered for 2.5. The bitrate in kbit/s, by
|
||||
index from 1:
|
||||
|
||||
| | indexes 1 to 14 |
|
||||
|---|---|
|
||||
| version 1, layer I | 32 64 96 128 160 192 224 256 288 320 352 384 416 448 |
|
||||
| version 1, layer II | 32 48 56 64 80 96 112 128 160 192 224 256 320 384 |
|
||||
| version 1, layer III | 32 40 48 56 64 80 96 112 128 160 192 224 256 320 |
|
||||
| version 2 or 2.5, layer I | 32 48 56 64 80 96 112 128 144 160 176 192 224 256 |
|
||||
| version 2 or 2.5, layers II and III | 8 16 24 32 40 48 56 64 80 96 112 128 144 160 |
|
||||
|
||||
Frame length: layer I, (12000 × kbit/s ÷ rate + padding) × 4; layer III of version 2 or 2.5, 72000 ×
|
||||
kbit/s ÷ rate + padding; otherwise 144000 × kbit/s ÷ rate + padding; division
|
||||
is integer division. Nothing is a candidate unless the first bytes are a frame and at least one
|
||||
whole frame is walked. Evidence `id3v2`, 3 bytes at 0, where a tag is there; `frame_header`, 4 bytes
|
||||
at the first
|
||||
frame. `facts.version` (`1`, `2` or `2.5`), `facts.layer`, `facts.bitrate_kbps`, `facts.sample_rate`
|
||||
and `facts.channels` (1 for channel mode 3, else 2), of the first frame. Frames walk through the
|
||||
head. A frame must
|
||||
keep the first's version, layer and rate, else the walk stops there (`overrun` if fewer than 4 bytes
|
||||
remain, else `invalid`). Exactly 128 bytes left beginning `TAG`: evidence `id3v1`, 3 bytes, and the
|
||||
walk ends after them. A frame past the
|
||||
file's end is `overrun`, past the head's alone `limit`. `facts.frames_read`. `structure`: three
|
||||
frames.
|
||||
|
||||
§5.10 **ADTS** (`adts`, kind `audio`). A frame: 0xFF; byte 1 with `byte & 0xF6 == 0xF0`; byte 2's
|
||||
high 2 bits plus 1 the audio object type, its next 4 the rate index (below 13: 96000, 88200, 64000,
|
||||
48000, 44100, 32000, 24000, 22050,
|
||||
16000, 12000, 11025, 8000, 7350), and channel configuration from its low bit and byte 3's high 2;
|
||||
frame length 13 bits from byte 3's low 2, at least 7. Nothing is a candidate unless the head begins
|
||||
with a frame and at least one whole
|
||||
frame is walked. Evidence `frame_header`, 7 bytes at 0, `wai.audio.aac`, and
|
||||
`facts.audio_object_type`, `facts.sample_rate`, `facts.channel_configuration` of the first. Frames
|
||||
walk as §5.9's do, keeping type and rate (`overrun` under 7 bytes), and with
|
||||
the same limits. `facts.frames_read`. `structure`: three frames.
|
||||
|
||||
§5.11 **PNG** (`png`, kind `image`). The head begins `89 50 4E 47 0D 0A 1A 0A`: evidence
|
||||
`signature`, and `wai.image.png`. Chunks from 8: `be32` length, four ASCII letters of type (else
|
||||
`invalid`), data, 4-byte checksum. The first must be `IHDR` of length 13, else
|
||||
`invalid`; its checksum (polynomial 0xEDB88320 reflected, from all ones, inverted at the end; over
|
||||
type and data) must hold, else `crc`. Then evidence `ihdr`, 25 bytes, `facts.width`, `facts.height`,
|
||||
`facts.bit_depth`, `facts.colour_type`, `facts.interlace`, and `structure`.
|
||||
`acTL` sets `facts.animated`; `cICP` with 4 bytes readable sets `facts.cicp` to them as a list. A
|
||||
chunk past the file's end, or the end of the file before `IEND`, is `overrun`. `IEND` gives evidence
|
||||
`iend`, 12 bytes at its offset, and the walk ends after it. `facts.chunks_read`.
|
||||
Only `IHDR`'s checksum is computed.
|
||||
|
||||
§5.12 **JPEG** (`jpeg`, kind `image`). The head begins `FF D8 FF`: evidence `soi`, 2 bytes at 0, and
|
||||
`wai.image.jpeg`. Markers from 2: 0xFF and a code, a code of 0xFF being a fill byte that advances
|
||||
one. Fewer than 2 bytes is `overrun`; a first byte not 0xFF is `invalid`. A code of 0xDA gives
|
||||
evidence `sos`, 2 bytes, and ends the walk there. Others: `be16` length, at least 2 (else
|
||||
`invalid`), `overrun` if it does not read or the segment passes the file's end. 0xE0 to 0xEF name an
|
||||
application segment from its first 32 bytes: `JFIF\0` `jfif`, `Exif\0\0` `exif`, `ICC_PROFILE\0`
|
||||
`icc`, `MPF\0` `mpf`,
|
||||
the XMP namespace (`http://ns.adobe.com/xap/1.0/\0`) `xmp`, `Adobe` `adobe`, else `other`; <!-- allow-name: the APP14 segment's identifier and the XMP namespace, byte signatures a JPEG carries -->
|
||||
`facts.applications` holds them, distinct, first 8. Codes 0xC0 to 0xC3, 0xC5 to 0xC7, 0xC9 to 0xCB
|
||||
and 0xCD to 0xCF are a frame header, needing 6 bytes (else `overrun`): evidence `sof`, the segment's
|
||||
bytes, `facts.process` (0xC0
|
||||
`baseline`, 0xC1 `extended`, 0xC2 `progressive`, 0xC3 `lossless`, 0xC9 to 0xCB `arithmetic`, else
|
||||
`hierarchical`), `facts.precision`, `facts.height`, `facts.width`, `facts.components`, and
|
||||
`structure`.
|
||||
|
||||
§5.13 **GIF** (`gif`, kind `image`). The head begins `GIF87a` or `GIF89a`: evidence
|
||||
`signature:<it>`, 6 bytes. Fewer than 13 bytes stops the walk at 6. `facts.width` and `facts.height`
|
||||
from `le16` at 6 and 8; a global table of 3 × 2^(n+1) bytes follows byte 10 flag 0x80 (n its low 3
|
||||
bits). Blocks from there: 0x3B the
|
||||
trailer (evidence `trailer`, 1 byte, and the walk ends after it); 0x21 an extension, sub-blocks from
|
||||
2 on; 0x2C an image, 10 bytes (a local table likewise from its byte 9), one more byte, then
|
||||
sub-blocks, evidence `image`, 10 bytes, for the first; any other byte is `invalid`. Sub-blocks: a
|
||||
count byte and that many, ending after a count
|
||||
of 0. Bytes not in the head stop the walk: `limit` where the head is shorter than the file, else
|
||||
`overrun`. `facts.frames_read`. `structure`: one image, or the trailer.
|
||||
|
||||
§5.14 **IVF** (`ivf`, kind `container`). The head begins `DKIF`: evidence `dkif`, 4 bytes. Under 12
|
||||
bytes stops at 0 (`overrun`); a version (`le16` at 4) not 0 or a header length (`le16` at 6) under
|
||||
32 stops at 0 (`invalid`). A row: media `video`, `stated` the four bytes at 8, codec `av1` for
|
||||
`AV01`, `vp9` for `VP90`, `vp8` for
|
||||
`VP80`, `width` and `height` `le16` at 12 and 14. Frames from the header length: `le32` size, 8
|
||||
bytes of time, then that many. `overrun` where one does not fit. `facts.frames_read`. `structure`:
|
||||
one frame.
|
||||
|
||||
§5.15 **YUV4MPEG2** (`y4m`, kind `elementary`). The head begins `YUV4MPEG2 `: evidence `signature`,
|
||||
10 bytes. A line feed must end the header within 256 bytes: else stop at 0 (`overrun` if the file
|
||||
ends sooner, else `invalid`). Space-separated tokens: `W` width, `H` height, `C` colour (default
|
||||
`420`), `F` frame rate; `facts.width`, `facts.height`, `facts.colour`
|
||||
and `facts.frame_rate`. A colour is a base, and optionally `p` and a decimal depth, 2 bytes a sample
|
||||
above 8. Bases: `420`, `420jpeg`, `420mpeg2`, `420paldv` (w·h + 2·⌈w/2⌉·⌈h/2⌉ samples a frame);
|
||||
`422` (w·h + 2·⌈w/2⌉·h); `444` (3·w·h); `mono` (w·h). Any other stops the walk (`invalid`), as do
|
||||
absent dimensions. Frames: a line beginning `FRAME`, within 256 bytes, then a frame's bytes.
|
||||
`facts.frames_read`.
|
||||
`structure`: one frame.
|
||||
|
||||
§5.16 **FLV** (`flv`, kind `container`). The head begins `FLV` and 0x01: evidence `signature`, 4
|
||||
bytes. `be32` at 5, the data offset, must be at least 9, else stop at 0 (`overrun` under 9 bytes,
|
||||
else `invalid`). Tags from that offset plus 4: 11 header bytes (type in byte 0's low 5 bits, one of
|
||||
8, 9 or 18, else `invalid`; `be24` length at 1), then the body, then a `be32` that must be 11 plus
|
||||
the length (else `invalid`; absent, `overrun`).
|
||||
The first audio tag (type 8, with a body) gives a row: media `audio`, `stated` `sound_format:<n>`
|
||||
for its first body byte's high 4 bits, codec `mp3` for 2 and `aac` for 10. The first video tag (type
|
||||
9, with a body) gives a row: where its first body byte has bit 0x80, `stated` is the next four
|
||||
bytes, with codec `avc` for `avc1`, `hevc` for `hvc1`, `av1` for `av01`, `vp9` for `vp09`; otherwise
|
||||
`stated` is `codec_id:<n>` for that byte's low 4 bits, with `avc` for 7. The video row comes first.
|
||||
`facts.tags_read`. `structure`: one tag.
|
||||
|
||||
§5.17 **AV1 OBU streams** (`av1-obu`, kind `elementary`). An OBU: a header byte with bits 0x81 clear
|
||||
and 0x02 set; type its bits 3 to 6, one of 1 to 8 or 15; an extension byte where bit 0x04 is set; a
|
||||
LEB128 size of at most 8 bytes; that many bytes. The first must be type 1 or 2. They are walked
|
||||
through the head. A header that does not parse stops the walk at it (`limit` if the head is shorter
|
||||
than the file and fewer than 8 bytes remain, else `invalid`); one past the file's end, `overrun`;
|
||||
one past the head's alone, `limit`. The first type 1 gives evidence `sequence_header`, and is read
|
||||
as AV1 §5.5: `facts.profile`, `facts.level` (the first operating point's `seq_level_idx`),
|
||||
`facts.width` and `facts.height` (`max_frame_width_minus_1` and `max_frame_height_minus_1`, plus 1).
|
||||
`facts.units_read`, and `wai.video.av1`. A candidate only if a sequence header reads and at least
|
||||
two OBUs were walked, and then `structure`: two bytes that merely fit are no claim of AV1.
|
||||
|
||||
§5.18 **HLS** (`hls`, kind `playlist`). After an optional UTF-8 byte order mark, `#EXTM3U` and a
|
||||
line end or the end of the head: evidence `extm3u`, 7 bytes at it. Lines (ended by a line feed, less
|
||||
a final carriage return) are read: any beginning `#EXT-X-` is a tag; `#EXT-X-STREAM-INF:` counts
|
||||
`facts.variants`, and the first with `CODECS="` gives `facts.codecs`; `#EXTINF:` counts
|
||||
`facts.segments`; `#EXT-X-MAP:` counts `facts.maps`; `#EXT-X-ENDLIST` sets `facts.ended`;
|
||||
`#EXT-X-VERSION:` gives `facts.version` where it is a decimal integer; and the first `#EXT-X-KEY:`
|
||||
or `#EXT-X-SESSION-KEY:` whose `METHOD=` (to the next comma) is not `NONE` gives `facts.key_method`.
|
||||
`facts.playlist` is `multivariant` with variants, else `media` with segments, else `other`. Every
|
||||
one of these facts is present; `structure`: at least one tag.
|
||||
|
||||
§5.19 **DASH** (`dash`, kind `playlist`). After an optional byte order mark and any white space,
|
||||
`<?...?>` and `<!--...-->`, then `<MPD` and white space or `>`: evidence `mpd`, 4 bytes at it.
|
||||
`urn:mpeg:dash:schema:mpd:2011` anywhere in the head gives evidence `namespace` and `structure`.
|
||||
`facts.type` is the value of ` type="` in the root tag up to its first `>`. `facts.periods`,
|
||||
`facts.adaptation_sets`, `facts.representations` and `facts.content_protection` count `<Period`,
|
||||
`<AdaptationSet`, `<Representation` and `<ContentProtection`; `facts.codecs` holds the distinct
|
||||
values of `codecs="`, the first 16. It is no XML parser: these are substrings.
|
||||
|
||||
§5.20 **Annex B streams** (kind `elementary`). The head holds at least two zero bytes, then 0x01,
|
||||
and nothing before them. Units are split as format-probes §2 (its Annex B byte streams) does, the
|
||||
first 4,096 (trailing zero bytes removed). If the byte after the first prefix is 0xBA or 0xB3, §6
|
||||
has the format, and no reading applies. Each reading below needs every unit's header to hold and its
|
||||
parameter sets in order, each before the first coded picture of that reading; a stream two readings
|
||||
fit is reported as both, and no reading is preferred. A reading is `structure`, with
|
||||
`facts.units_read`. Where the head is shorter than the file, or 4,096 units were read, it stops at
|
||||
the last unit (`limit`). `walked_to` is `null`.
|
||||
|
||||
- `avc-annexb`: one header byte, bit 0x80 clear, type its low 5 bits and 1 to 21; types 5, 7 and 8
|
||||
have nonzero bits 0x60. Order 7, 8; coded pictures are types 1 to 5. Its first type 7 is read as
|
||||
ITU-T H.264 §7.3.2.1.1, with emulation prevention removed: a `profile_idc` other than 66, 77, 88,
|
||||
100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134 or 135 or a failed reading rules the
|
||||
reading out. Evidence `nal:sps`; facts `profile_idc`, `constraint_flags`, `level_idc`,
|
||||
`chroma_format_idc`, `width` and `height` less cropping; `wai.video.avc`.
|
||||
- `hevc-annexb`: two header bytes, bit 0x80 of the first clear, the low 3 bits of the second not
|
||||
zero, type the first's bits 1 to 6 and one of 0 to 9, 16 to 21 or 32 to 40. Order 32, 33, 34;
|
||||
coded pictures are types 0 to 9 and 16 to 21. Evidence `nal:sps`; `wai.video.hevc`.
|
||||
- `vvc-annexb`: two header bytes, bits 0xC0 of the first clear, the low 3 bits of the second not
|
||||
zero, type the second's high 5 bits and at most 25. Order 15, 16; coded pictures are types 0 to
|
||||
10. Evidence `nal:sps`; `wai.video.vvc`.
|
||||
|
||||
## 6. Formats known by their signature
|
||||
|
||||
§6.1 A format of this table is a candidate where every one of its fixed bytes is at its offset in
|
||||
the head: `strength` `signature`, evidence `signature` for each, and the capabilities it names.
|
||||
Bytes are in hexadecimal, each group at the offset before its colon. A format with two rows is
|
||||
either.
|
||||
|
||||
| id | kind | bytes | capability |
|
||||
|---|---|---|---|
|
||||
| `aiff` | audio | `0:464f524d 8:41494646` | |
|
||||
| `aiff` | audio | `0:464f524d 8:41494643` | |
|
||||
| `caf` | audio | `0:636166660001` | |
|
||||
| `wavpack` | audio | `0:7776706b` | |
|
||||
| `amr` | audio | `0:2321414d520a` | |
|
||||
| `amr-wb` | audio | `0:2321414d522d57420a` | |
|
||||
| `jpegxl` | image | `0:ff0a` | `wai.image.jxl` |
|
||||
| `jpegxl` | image | `0:0000000c4a584c200d0a870a` | `wai.image.jxl` |
|
||||
| `jp2` | image | `0:0000000c6a5020200d0a870a` | `wai.image.jp2` |
|
||||
| `j2k` | image | `0:ff4fff51` | `wai.image.jp2` |
|
||||
| `tiff` | image | `0:49492a00` | |
|
||||
| `tiff` | image | `0:4d4d002a` | |
|
||||
| `bigtiff` | image | `0:49492b00` | |
|
||||
| `bigtiff` | image | `0:4d4d002b` | |
|
||||
| `openexr` | image | `0:762f3101` | |
|
||||
| `dpx` | image | `0:53445058` | |
|
||||
| `dpx` | image | `0:58504453` | |
|
||||
| `radiance` | image | `0:233f52414449414e43450a` | |
|
||||
| `radiance` | image | `0:233f524742450a` | |
|
||||
| `mxf` | container | `0:060e2b34020501010d0102` | |
|
||||
| `asf` | container | `0:3026b2758e66cf11a6d900aa0062ce6c` | |
|
||||
| `mpeg-ps` | container | `0:000001ba` | |
|
||||
| `mpeg-video` | elementary | `0:000001b3` | |
|
||||
| `webvtt` | text | `0:574542565454` | |
|
||||
| `gzip` | compression | `0:1f8b08` | |
|
||||
| `zstd` | compression | `0:28b52ffd` | |
|
||||
| `xz` | compression | `0:fd377a585a00` | |
|
||||
| `bzip2` | compression | `0:425a68` | |
|
||||
| `zip` | compression | `0:504b0304` | |
|
||||
| `tar` | compression | `257:7573746172` | |
|
||||
| `pcap` | capture | `0:d4c3b2a1` | |
|
||||
| `pcap` | capture | `0:a1b2c3d4` | |
|
||||
| `pcap` | capture | `0:4d3cb2a1` | |
|
||||
| `pcap` | capture | `0:a1b23c4d` | |
|
||||
| `pcapng` | capture | `0:0a0d0d0a` | |
|
||||
|
||||
## 7. Signatures inside a file
|
||||
|
||||
§7.1 A reverse engineer's first question about a blob is what it holds. The scan is searched for
|
||||
each of these fixed bytes, none shorter than 8 bytes, so that one in 16 MiB of noise is improbable;
|
||||
each hit past the file's beginning gives `{at, id}`, `at` being where its structure begins, `back`
|
||||
bytes before them. A hit whose structure would begin at or before the file's beginning is not
|
||||
reported. Hits are in the order their bytes are found, and within one offset in this table's order.
|
||||
At most 32 are reported, and `embedded_capped` says whether another was found. A hit says only that
|
||||
the bytes are there.
|
||||
|
||||
| id | bytes | back |
|
||||
|---|---|---|
|
||||
| `png` | `89504e470d0a1a0a` | 0 |
|
||||
| `jp2` | `0000000c6a5020200d0a870a` | 0 |
|
||||
| `jpegxl` | `0000000c4a584c200d0a870a` | 0 |
|
||||
| `mxf` | `060e2b34020501010d0102` | 0 |
|
||||
| `asf` | `3026b2758e66cf11a6d900aa0062ce6c` | 0 |
|
||||
| `ogg` | `4f67675300020000000000000000` | 0 |
|
||||
| `flac` | `664c614300000022` | 0 |
|
||||
| `flac` | `664c614380000022` | 0 |
|
||||
| `isobmff` | `6674797069736f6d` | 4 |
|
||||
| `isobmff` | `6674797069736f32` | 4 |
|
||||
| `isobmff` | `6674797069736f35` | 4 |
|
||||
| `isobmff` | `6674797069736f36` | 4 |
|
||||
| `isobmff` | `667479706d703431` | 4 |
|
||||
| `isobmff` | `667479706d703432` | 4 |
|
||||
| `isobmff` | `667479704d344120` | 4 |
|
||||
| `isobmff` | `667479704d345620` | 4 |
|
||||
| `isobmff` | `6674797071742020` | 4 |
|
||||
| `isobmff` | `6674797061766966` | 4 |
|
||||
| `isobmff` | `6674797068656963` | 4 |
|
||||
| `isobmff` | `66747970636d6663` | 4 |
|
||||
| `isobmff` | `6674797064617368` | 4 |
|
||||
| `isobmff` | `667479706d736468` | 4 |
|
||||
|
||||
## 8. Measures
|
||||
|
||||
§8.1 `measures` holds, over the scan: `scanned`, its length; `distinct_bytes`, how many of the 256
|
||||
values occur; `zero_bytes`, how many are zero; `chi_square`, the floor of `256 · Σc² / n` less `n`,
|
||||
where `c` are the counts of the values and `n` the length, and 0 for an empty scan; `text` (§8.2);
|
||||
`windows` and `uniform_windows` (§8.3); and `periods` (§8.4).
|
||||
|
||||
§8.2 `text` is true where the scan is not empty, is valid UTF-8 (a sequence the scan's end cuts
|
||||
short allowed), and holds no byte below 0x20 but 0x09, 0x0A, 0x0C and 0x0D, and no 0x7F.
|
||||
|
||||
§8.3 A **window** is each whole 4096 bytes of the scan from its start. One is **uniform** when the
|
||||
sum of its counts squared is at most 70,496: its chi-square against a uniform spread is then at most
|
||||
310, about the 99th percentile for 255 degrees of freedom. A compressed payload and an encrypted one
|
||||
both tend to be uniform; this measure cannot tell them apart, and says only that the bytes show no
|
||||
structure in their values.
|
||||
|
||||
§8.4 **Repeats.** Over the first 256 KiB of the scan (`n` bytes), for each lag `l` from 1 to `L +
|
||||
1`, where `L` is the lesser of 4096 and `⌊n/4⌋`: `m = n − l`; `matches` counts the `i` below `m`
|
||||
with byte `i` equal to byte `i + l`; `expected` is the floor of `m · Σc² / n²`, at least 1, over
|
||||
this sample's counts; the excess `e` is `matches − expected`; and `z` is, where `e` is positive, the
|
||||
floor of the square root of the floor of `e² / expected`, otherwise 0. With `L` below 2 there are no
|
||||
periods.
|
||||
|
||||
The floor is 8 times the median of `|e|` over those lags, the element at index `⌊k/2⌋` of the `k` of
|
||||
them sorted: data that repeats exactly is not independent from lag to lag, and its noise is many
|
||||
times what `z` assumes. A lag from 2 to `L`, in increasing order, is reported as `{lag, z}` where
|
||||
all of these hold, at most 8: `z` is at least 8; `e` is at least the floor; `e` exceeds twice the
|
||||
greatest of its neighbours' excess and 0; if `2l ≤ L + 1`, `z` at `2l` is at least 8; and it is no
|
||||
multiple of a lag already reported. A repeat is a record size, a sample width or a table's stride,
|
||||
found with no knowledge of the format.
|
||||
|
||||
## 9. Security
|
||||
|
||||
§9.1 Identification reads files nobody vouches for. A conforming implementation **MUST NOT** panic,
|
||||
read outside the file, allocate more than the file's bounded windows and a bounded number of
|
||||
structures, or follow a URI in a playlist. The bounds of §3 are what make the cost of a hostile file
|
||||
bounded.
|
||||
|
||||
§9.2 A result can hold strings a file's author chose (§3.4). The operation's agent door marks its
|
||||
results `untrusted_content`.
|
||||
|
||||
§9.3 An implementation **MUST NOT** decrypt, or try to recover the keys of, a protected track. It
|
||||
reports the scheme the file states and stops.
|
||||
|
||||
## 10. Conformance
|
||||
|
||||
§10.1 The corpus [`identify-conformance/`](../identify-conformance/) holds files written by real
|
||||
muxers and encoders from synthetic inputs (`ext/`), generated cases and negative controls (`gen/`),
|
||||
and `vectors.json`, each file's digest and whole result. `prober.json` is what an independent media
|
||||
prober reads of `ext/`, the reference checked against it wherever both state a value. A conforming
|
||||
implementation gives each file its result exactly.
|
||||
|
|
@ -405,6 +405,14 @@ def step6(c, cid, passed, fetcher, exact_algs):
|
|||
if r.reason == "reconciliation_objects_not_held":
|
||||
raise Refused("context_not_held", 6)
|
||||
raise
|
||||
elif cls == "schedule":
|
||||
# carousel §4.3: against the group of an item its subject names and the object
|
||||
# receipts of the items listed under it here, with no envelope bytes.
|
||||
import carousel_verify as KV
|
||||
v = next((v for v in passed if JV.link(v["g"]).hex() == c["subject"]), None)
|
||||
if v is None:
|
||||
raise Refused("context_not_held", 6)
|
||||
KV.check_schedule(c, v["g"], [(w["o"], None) for w in passed if JV.link(w["g"]).hex() == c["subject"]])
|
||||
except CV.Reject as r:
|
||||
raise Refused(r.reason, r.step)
|
||||
|
||||
|
|
|
|||
21
wai/identify-conformance/README.md
Normal file
21
wai/identify-conformance/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# identify-conformance — media identification
|
||||
|
||||
The corpus of [`extensions/media-identify.md`](../extensions/media-identify.md) §10.
|
||||
|
||||
**Feature equivalence.** A conforming implementation, given a vector's file, gives the whole result
|
||||
`vectors.json` states for it: every candidate with its evidence, where its walk stopped and why, its
|
||||
facts, tracks and capabilities; what was embedded; and the measures. A vector with `repeat` is its
|
||||
file that many times over, for what happens past the head and past the scan (§3.2).
|
||||
|
||||
- `ext/` — files written by real muxers and encoders, from test patterns and sine waves. The
|
||||
protected file uses an all-zero test key.
|
||||
- `gen/` — what `wai_identify_vectors gen` writes: generated cases and negative controls, each saying
|
||||
in `vectors.json` what it tests, many of them a change to a file of `ext/`.
|
||||
- `vectors.json` — every vector's file, SHA-256, origin and whole result.
|
||||
- `prober.json` — what an independent media prober reads of `ext/`: each file's format and streams.
|
||||
`identify_verify.py` holds the result to it wherever both state a value, so the reference and the
|
||||
second implementation agreeing is not all that is checked.
|
||||
- `identify_verify.py` — the second implementation, written from the extension.
|
||||
|
||||
**Provenance.** `ext/` and `prober.json` are written by `tools/make_external.sh`, with open tools; it
|
||||
is not run in CI, because it needs them. `tools/oracle.py` writes `prober.json`.
|
||||
BIN
wai/identify-conformance/ext/a.aac
Normal file
BIN
wai/identify-conformance/ext/a.aac
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.aiff
Normal file
BIN
wai/identify-conformance/ext/a.aiff
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.caf
Normal file
BIN
wai/identify-conformance/ext/a.caf
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.flac
Normal file
BIN
wai/identify-conformance/ext/a.flac
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.mp3
Normal file
BIN
wai/identify-conformance/ext/a.mp3
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.wav
Normal file
BIN
wai/identify-conformance/ext/a.wav
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.wav.gz
Normal file
BIN
wai/identify-conformance/ext/a.wav.gz
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/a.wav.zst
Normal file
BIN
wai/identify-conformance/ext/a.wav.zst
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/av1.ivf
Normal file
BIN
wai/identify-conformance/ext/av1.ivf
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/av1.mp4
Normal file
BIN
wai/identify-conformance/ext/av1.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/av1.obu
Normal file
BIN
wai/identify-conformance/ext/av1.obu
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc-aac.flv
Normal file
BIN
wai/identify-conformance/ext/avc-aac.flv
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc-aac.mp4
Normal file
BIN
wai/identify-conformance/ext/avc-aac.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc-aac.ts
Normal file
BIN
wai/identify-conformance/ext/avc-aac.ts
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc-protected.mp4
Normal file
BIN
wai/identify-conformance/ext/avc-protected.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc.h264
Normal file
BIN
wai/identify-conformance/ext/avc.h264
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/avc.m2ts
Normal file
BIN
wai/identify-conformance/ext/avc.m2ts
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/container.jxl
Normal file
BIN
wai/identify-conformance/ext/container.jxl
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/flac.mp4
Normal file
BIN
wai/identify-conformance/ext/flac.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/hevc-flac.mkv
Normal file
BIN
wai/identify-conformance/ext/hevc-flac.mkv
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/hevc-fragmented.mp4
Normal file
BIN
wai/identify-conformance/ext/hevc-fragmented.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/hevc-hvc1.mp4
Normal file
BIN
wai/identify-conformance/ext/hevc-hvc1.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/hevc.h265
Normal file
BIN
wai/identify-conformance/ext/hevc.h265
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/hevc.ts
Normal file
BIN
wai/identify-conformance/ext/hevc.ts
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/id3.mp3
Normal file
BIN
wai/identify-conformance/ext/id3.mp3
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/lossless.webp
Normal file
BIN
wai/identify-conformance/ext/lossless.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 B |
BIN
wai/identify-conformance/ext/lossy.webp
Normal file
BIN
wai/identify-conformance/ext/lossy.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 214 B |
26
wai/identify-conformance/ext/manifest.mpd
Normal file
26
wai/identify-conformance/ext/manifest.mpd
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
||||
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
||||
type="static"
|
||||
mediaPresentationDuration="PT0.3S"
|
||||
maxSegmentDuration="PT5.0S"
|
||||
minBufferTime="PT0.6S">
|
||||
<ProgramInformation>
|
||||
</ProgramInformation>
|
||||
<ServiceDescription id="0">
|
||||
</ServiceDescription>
|
||||
<Period id="0" start="PT0.0S">
|
||||
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="10/1" maxWidth="32" maxHeight="32" par="1:1">
|
||||
<Representation id="0" mimeType="video/mp4" codecs="avc1.42c00a" bandwidth="60373" width="32" height="32" sar="1:1">
|
||||
<SegmentTemplate timescale="10240" initialization="i.m4s" media="s.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="3072" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
8
wai/identify-conformance/ext/media.m3u8
Normal file
8
wai/identify-conformance/ext/media.m3u8
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
#EXT-X-TARGETDURATION:0
|
||||
#EXT-X-MEDIA-SEQUENCE:0
|
||||
#EXT-X-PLAYLIST-TYPE:VOD
|
||||
#EXTINF:0.300000,
|
||||
s0.ts
|
||||
#EXT-X-ENDLIST
|
||||
BIN
wai/identify-conformance/ext/mpeg2.m2v
Normal file
BIN
wai/identify-conformance/ext/mpeg2.m2v
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/mpeg2.mpg
Normal file
BIN
wai/identify-conformance/ext/mpeg2.mpg
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/opus.m4a
Normal file
BIN
wai/identify-conformance/ext/opus.m4a
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/opus.ogg
Normal file
BIN
wai/identify-conformance/ext/opus.ogg
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/p.avif
Normal file
BIN
wai/identify-conformance/ext/p.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
wai/identify-conformance/ext/p.gif
Normal file
BIN
wai/identify-conformance/ext/p.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1,010 B |
BIN
wai/identify-conformance/ext/p.j2k
Normal file
BIN
wai/identify-conformance/ext/p.j2k
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/p.jp2
Normal file
BIN
wai/identify-conformance/ext/p.jp2
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/p.jpg
Normal file
BIN
wai/identify-conformance/ext/p.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 720 B |
BIN
wai/identify-conformance/ext/p.jxl
Normal file
BIN
wai/identify-conformance/ext/p.jxl
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/p.png
Normal file
BIN
wai/identify-conformance/ext/p.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 344 B |
BIN
wai/identify-conformance/ext/p.tif
Normal file
BIN
wai/identify-conformance/ext/p.tif
Normal file
Binary file not shown.
4
wai/identify-conformance/ext/t.vtt
Normal file
4
wai/identify-conformance/ext/t.vtt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:01.000
|
||||
A line.
|
||||
5
wai/identify-conformance/ext/v.y4m
Normal file
5
wai/identify-conformance/ext/v.y4m
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
YUV4MPEG2 W16 H16 F10:1 Ip A1:1 C420jpeg XYSCSS=420JPEG
|
||||
FRAME
|
||||
JJJJJJJJ))))jjェェJJJJJJJJ))))jjェェJJ&&66JJ&&66JJ&&66JJ&&66JJ鋳鋳&&鋳JJ鋳鋳&鋳6QQ 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 鋳@鋳鋳鋳6嘆Q 鋳@鋳鋳鋳6嘆Q 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 0鋳@鋳&鋳6QQ 00鋳鋳&&鋳ンンンン<EFBE9D>ハヲン覧梨恍貨覧梨恍貨<E6818D>MOボJZw5d<35>55瓜w5Li55_Zw5d<35>55瓜w`AOボJヲヲヲヲnn゙ヲ演榎|妖ヲ演榎|妖ヲ於ウー困ェ<E59BB0>チлチチd<EFBE81>チ」榁チ挺愧лチチd<EFBE81>イー困ェFRAME
|
||||
JJJJJJJJ))))jjェェJJJJJJJJ))))jjェェJJ&&66JJ&&66JJ&&66JJ&&66JJ鋳鋳&&鋳JJ鋳鋳&鋳6QQ 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 鋳@鋳鋳鋳6嘆Q 鋳@鋳鋳鋳6嘆Q 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 0鋳@鋳&鋳6QQ 00鋳鋳&&鋳ンンンン<EFBE9D>ハヲン覧梨恍貨覧梨恍貨<E6818D>MOボJZw5d<35>55瓜w5Li55_Zw5d<35>55瓜w`AOボJヲヲヲヲnn゙ヲ演榎|妖ヲ演榎|妖ヲ於ウー困ェ<E59BB0>チлチチd<EFBE81>チ」榁チ挺愧лチチd<EFBE81>イー困ェFRAME
|
||||
JJJJJJJJ))))jjェェJJJJJJJJ))))jjェェJJ&&66JJ&&66JJ&&66JJ&&66JJ鋳鋳&&鋳JJ鋳鋳&鋳6QQ 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 鋳@鋳鋳鋳6嘆Q 鋳@鋳鋳鋳6嘆Q 鋳@@鋳鋳66QQ 鋳@@鋳鋳66QQ 0鋳@鋳&鋳6QQ 00鋳鋳&&鋳ンンンン<EFBE9D>ハヲン覧梨恍貨覧梨恍貨<E6818D>MOボJZw5d<35>55瓜w5Li55_Zw5d<35>55瓜w`AOボJヲヲヲヲnn゙ヲ演榎|妖ヲ演榎|妖ヲ於ウー困ェ<E59BB0>チлチチd<EFBE81>チ」榁チ挺愧лチチd<EFBE81>イー困ェ
|
||||
BIN
wai/identify-conformance/ext/vorbis.ogg
Normal file
BIN
wai/identify-conformance/ext/vorbis.ogg
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/vp9-opus.webm
Normal file
BIN
wai/identify-conformance/ext/vp9-opus.webm
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/vp9.ivf
Normal file
BIN
wai/identify-conformance/ext/vp9.ivf
Normal file
Binary file not shown.
BIN
wai/identify-conformance/ext/vp9.mp4
Normal file
BIN
wai/identify-conformance/ext/vp9.mp4
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-bad-len.bin
Normal file
BIN
wai/identify-conformance/gen/adts-bad-len.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-bad-rate.bin
Normal file
BIN
wai/identify-conformance/gen/adts-bad-rate.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-frame.bin
Normal file
BIN
wai/identify-conformance/gen/adts-frame.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-inside.bin
Normal file
BIN
wai/identify-conformance/gen/adts-inside.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-junk.bin
Normal file
BIN
wai/identify-conformance/gen/adts-junk.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-many.bin
Normal file
BIN
wai/identify-conformance/gen/adts-many.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-other-rate.bin
Normal file
BIN
wai/identify-conformance/gen/adts-other-rate.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-past.bin
Normal file
BIN
wai/identify-conformance/gen/adts-past.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-rate13.bin
Normal file
BIN
wai/identify-conformance/gen/adts-rate13.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-six.bin
Normal file
BIN
wai/identify-conformance/gen/adts-six.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/adts-tail.bin
Normal file
BIN
wai/identify-conformance/gen/adts-tail.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-02.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-02.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-all-zero.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-all-zero.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-empty-unit.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-empty-unit.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-first-empty.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-first-empty.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-one-byte.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-one-byte.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/annexb-one-zero.bin
Normal file
BIN
wai/identify-conformance/gen/annexb-one-zero.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-bad-seq.bin
Normal file
BIN
wai/identify-conformance/gen/av1-bad-seq.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-cut-size.bin
Normal file
2
wai/identify-conformance/gen/av1-cut-size.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<EFBFBD>
|
||||
BIN
wai/identify-conformance/gen/av1-ext.bin
Normal file
BIN
wai/identify-conformance/gen/av1-ext.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-first-other.bin
Normal file
2
wai/identify-conformance/gen/av1-first-other.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
2
|
||||
<1D>葮
|
||||
BIN
wai/identify-conformance/gen/av1-forbidden.bin
Normal file
BIN
wai/identify-conformance/gen/av1-forbidden.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-full.bin
Normal file
BIN
wai/identify-conformance/gen/av1-full.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-leb8.bin
Normal file
2
wai/identify-conformance/gen/av1-leb8.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<1D><><EFBFBD>22<><32><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
BIN
wai/identify-conformance/gen/av1-level7.bin
Normal file
BIN
wai/identify-conformance/gen/av1-level7.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-long-size.bin
Normal file
2
wai/identify-conformance/gen/av1-long-size.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<1D>葮22<><32><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
BIN
wai/identify-conformance/gen/av1-long-uvlc.bin
Normal file
BIN
wai/identify-conformance/gen/av1-long-uvlc.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-many.bin
Normal file
BIN
wai/identify-conformance/gen/av1-many.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-no-seq.bin
Normal file
BIN
wai/identify-conformance/gen/av1-no-seq.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-no-size.bin
Normal file
BIN
wai/identify-conformance/gen/av1-no-size.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-only-seq.bin
Normal file
2
wai/identify-conformance/gen/av1-only-seq.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<1D>葮
|
||||
BIN
wai/identify-conformance/gen/av1-past.bin
Normal file
BIN
wai/identify-conformance/gen/av1-past.bin
Normal file
Binary file not shown.
2
wai/identify-conformance/gen/av1-reduced.bin
Normal file
2
wai/identify-conformance/gen/av1-reduced.bin
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
<1D>葮2
|
||||
BIN
wai/identify-conformance/gen/av1-reserved.bin
Normal file
BIN
wai/identify-conformance/gen/av1-reserved.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/av1-unit.bin
Normal file
BIN
wai/identify-conformance/gen/av1-unit.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/avc-444-crop.bin
Normal file
BIN
wai/identify-conformance/gen/avc-444-crop.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/avc-444-separate.bin
Normal file
BIN
wai/identify-conformance/gen/avc-444-separate.bin
Normal file
Binary file not shown.
BIN
wai/identify-conformance/gen/avc-adjacent.bin
Normal file
BIN
wai/identify-conformance/gen/avc-adjacent.bin
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue