From bdf52d511b43bc1b97e62f0d8e403489bdf4430b Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 14:21:27 -0400 Subject: [PATCH 01/21] wai: a refinement's own delivery timeout in the staged delivery simulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A MoQ track sets one DELIVERY_TIMEOUT for every object on it, and a packager gives each refinement layer and deadline its own track (packaging §8.6), so one refinement timeout per plan cannot model a package's delivery. - stage_measure::sim: a model object may carry `timeout_us`, a refinement's own timeout in place of the plan's for its layer; a base carrying one is not a well-formed model. `timeout_us` is written only where an object has one, so every existing model reads and writes back unchanged. - §10.4's bound under fifo takes T as the longest timeout a refinement takes, its own or the plan's (`stall_bound_for`); `stall_bound` keeps its meaning for a model with none, and `check_stall` uses the model's. - staged-measurement §10.4 and Appendix B state both; the corpus adds the `per-track` model and three cases (a fifo plan with no plan timeout is bounded by the objects' own); sim_verify.py follows, independently. - Tests: a hand-computed case, and the bound over targeted draws whose refinements carry timeouts of their own. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/staged-measurement.md | 18 +- wai/staged-measure-conformance/README.md | 3 + wai/staged-measure-conformance/sim/cases.json | 18 + .../sim/models/per-track.json | 399 ++++++ .../sim/results.json | 1262 +++++++++++++++++ wai/staged-measure-conformance/sim_verify.py | 26 +- wai/wai-rs/src/bin/wai_stage_measure.rs | 33 +- wai/wai-rs/src/stage_measure/sim.rs | 65 +- wai/wai-rs/src/stage_measure/sim/tests.rs | 67 +- 9 files changed, 1859 insertions(+), 32 deletions(-) create mode 100644 wai/staged-measure-conformance/sim/models/per-track.json diff --git a/wai/extensions/staged-measurement.md b/wai/extensions/staged-measurement.md index e113eb8d9..bfad27ae5 100644 --- a/wai/extensions/staged-measurement.md +++ b/wai/extensions/staged-measurement.md @@ -307,13 +307,15 @@ under the plan minus the same time under the base-only model. The stall bound is `S = S_link + S_decode`: - `S_link`: under `strict-priority`, the serialisation time of one chunk; under `fifo` - with a refinement timeout `T`, `T` plus one chunk; otherwise there is no bound. + with a refinement timeout, `T` plus one chunk, where `T` is the longest timeout any + refinement takes (its own, where the model gives it one, or the plan's); otherwise + (a refinement that takes none) there is no bound. - `S_decode`: under `base-first` decode, one refinement's decode time; otherwise there is no bound. **Preconditions.** The bound holds for every model and every plan with no base timeout (`delivery_timeout_us.base` is `null`), a bounded link schedule (`strict-priority`, -or `fifo` with a refinement timeout) and `base-first` decode, whatever else the plan +or `fifo` where every refinement takes a timeout) and `base-first` decode, whatever else the plan states (carriage, window, live captures, layer cap, workers, decode times, playout deadlines). A plan outside them has no bound: a base its refinements delay past its own timeout is not presented at all, and the two unbounded parts above have counter-plans. @@ -494,9 +496,12 @@ them (`wai-sim-model/1`): ``` `units` is `[from, count]` over the title's units and `bytes` the object's counted -bytes (§3). A model is well formed when every object has at least one byte and one -unit, no two objects cover one unit at one layer, and every unit an object of layer -`k ≥ 1` covers is covered at layer `k − 1` by an object of the same base. A measured +bytes (§3). A refinement **MAY** carry `timeout_us`, a delivery timeout of its own in +place of the plan's for its layer: what a MoQ track's `DELIVERY_TIMEOUT` sets for every +object on it ([packaging](packaging.md) §8, its deadlines). A model is well formed when +every object has at least one byte and one unit, no base carries `timeout_us`, no two +objects cover one unit at one layer, and every unit an object of layer `k ≥ 1` covers is +covered at layer `k − 1` by an object of the same base. A measured title's model is its objects with their counted bytes, and its bases' capture times. A **plan** (every member required, none other allowed): @@ -524,7 +529,8 @@ microseconds, and `h = ⌊rtt_us / 2⌋`; a time or a byte sum past 64 bits is r with no window), and at `max(h, capture_us)` live; a live plan over an object with no capture time is refused. - *Deadline.* An object's availability plus its layer's timeout (`base` for layer 0, - `refinement` otherwise); none when that timeout is `null`. + `refinement` otherwise, or a refinement's own `timeout_us`); none when that timeout is + `null`. - *Cap.* An object above `layer_cap` is capped: never sent, never offered. - *Datagrams.* Under `datagram`, an object of more than `chunk_bytes` that is not capped is refused (`datagram-too-large`). diff --git a/wai/staged-measure-conformance/README.md b/wai/staged-measure-conformance/README.md index ee75844aa..474b75ca2 100644 --- a/wai/staged-measure-conformance/README.md +++ b/wai/staged-measure-conformance/README.md @@ -62,6 +62,9 @@ identity (Appendix D). Across the corpus: the record-wide deduplication totals ( with no refinement timeout, whose stall passes the bound of the same `fifo` link with a short timeout (`fifo-timeout-short`), and `fifo` decode on one worker, whose stall passes the bound of the same plan with `base-first` decode (`strict-window`). + The model `per-track` gives each refinement a delivery timeout of its own, one per + layer, as a MoQ track's `DELIVERY_TIMEOUT` does (Appendix B): with them a `fifo` link + with no plan timeout is bounded, by the longest of them. **Provenance.** Generated here. Each base is a committed vector under [`../wai-rs/tests/vectors/`](../wai-rs/tests/vectors/), generated too (that directory's diff --git a/wai/staged-measure-conformance/sim/cases.json b/wai/staged-measure-conformance/sim/cases.json index 08e336a8d..8deb0b169 100644 --- a/wai/staged-measure-conformance/sim/cases.json +++ b/wai/staged-measure-conformance/sim/cases.json @@ -86,6 +86,24 @@ "plan": "plans/reorder-decode.json", "claim": "within-bound" }, + { + "name": "per-track/strict-slow-timeout", + "model": "models/per-track.json", + "plan": "plans/strict-slow-timeout.json", + "claim": "within-bound" + }, + { + "name": "per-track/fifo-timeout-subgroup", + "model": "models/per-track.json", + "plan": "plans/fifo-timeout-subgroup.json", + "claim": "within-bound" + }, + { + "name": "per-track/fifo-no-timeout", + "model": "models/per-track.json", + "plan": "plans/fifo-no-timeout.json", + "claim": "within-bound" + }, { "name": "audio/strict-subgroup", "model": "fixture:audio", diff --git a/wai/staged-measure-conformance/sim/models/per-track.json b/wai/staged-measure-conformance/sim/models/per-track.json new file mode 100644 index 000000000..5698e2378 --- /dev/null +++ b/wai/staged-measure-conformance/sim/models/per-track.json @@ -0,0 +1,399 @@ +{ + "format": "wai-sim-model/1", + "unit_us": 40000, + "objects": [ + { + "id": "g0-base", + "base": 0, + "layer": 0, + "units": [ + 0, + 5 + ], + "bytes": 5000, + "capture_us": 200000 + }, + { + "id": "g0-l1-u0", + "base": 0, + "layer": 1, + "units": [ + 0, + 1 + ], + "bytes": 1500, + "capture_us": 200000, + "timeout_us": 60000 + }, + { + "id": "g0-l1-u1", + "base": 0, + "layer": 1, + "units": [ + 1, + 1 + ], + "bytes": 1537, + "capture_us": 200000, + "timeout_us": 60000 + }, + { + "id": "g0-l1-u2", + "base": 0, + "layer": 1, + "units": [ + 2, + 1 + ], + "bytes": 1574, + "capture_us": 200000, + "timeout_us": 60000 + }, + { + "id": "g0-l1-u3", + "base": 0, + "layer": 1, + "units": [ + 3, + 1 + ], + "bytes": 1611, + "capture_us": 200000, + "timeout_us": 60000 + }, + { + "id": "g0-l1-u4", + "base": 0, + "layer": 1, + "units": [ + 4, + 1 + ], + "bytes": 1648, + "capture_us": 200000, + "timeout_us": 60000 + }, + { + "id": "g0-l2-u0", + "base": 0, + "layer": 2, + "units": [ + 0, + 1 + ], + "bytes": 900, + "capture_us": 200000, + "timeout_us": 20000 + }, + { + "id": "g0-l2-u1", + "base": 0, + "layer": 2, + "units": [ + 1, + 1 + ], + "bytes": 937, + "capture_us": 200000, + "timeout_us": 20000 + }, + { + "id": "g0-l2-u2", + "base": 0, + "layer": 2, + "units": [ + 2, + 1 + ], + "bytes": 974, + "capture_us": 200000, + "timeout_us": 20000 + }, + { + "id": "g0-l2-u3", + "base": 0, + "layer": 2, + "units": [ + 3, + 1 + ], + "bytes": 1011, + "capture_us": 200000, + "timeout_us": 20000 + }, + { + "id": "g0-l2-u4", + "base": 0, + "layer": 2, + "units": [ + 4, + 1 + ], + "bytes": 1048, + "capture_us": 200000, + "timeout_us": 20000 + }, + { + "id": "g1-base", + "base": 1, + "layer": 0, + "units": [ + 5, + 5 + ], + "bytes": 5700, + "capture_us": 400000 + }, + { + "id": "g1-l1-u0", + "base": 1, + "layer": 1, + "units": [ + 5, + 1 + ], + "bytes": 1511, + "capture_us": 400000, + "timeout_us": 60000 + }, + { + "id": "g1-l1-u1", + "base": 1, + "layer": 1, + "units": [ + 6, + 1 + ], + "bytes": 1548, + "capture_us": 400000, + "timeout_us": 60000 + }, + { + "id": "g1-l1-u2", + "base": 1, + "layer": 1, + "units": [ + 7, + 1 + ], + "bytes": 1585, + "capture_us": 400000, + "timeout_us": 60000 + }, + { + "id": "g1-l1-u3", + "base": 1, + "layer": 1, + "units": [ + 8, + 1 + ], + "bytes": 1622, + "capture_us": 400000, + "timeout_us": 60000 + }, + { + "id": "g1-l1-u4", + "base": 1, + "layer": 1, + "units": [ + 9, + 1 + ], + "bytes": 1659, + "capture_us": 400000, + "timeout_us": 60000 + }, + { + "id": "g1-l2-u0", + "base": 1, + "layer": 2, + "units": [ + 5, + 1 + ], + "bytes": 911, + "capture_us": 400000, + "timeout_us": 20000 + }, + { + "id": "g1-l2-u1", + "base": 1, + "layer": 2, + "units": [ + 6, + 1 + ], + "bytes": 948, + "capture_us": 400000, + "timeout_us": 20000 + }, + { + "id": "g1-l2-u2", + "base": 1, + "layer": 2, + "units": [ + 7, + 1 + ], + "bytes": 985, + "capture_us": 400000, + "timeout_us": 20000 + }, + { + "id": "g1-l2-u3", + "base": 1, + "layer": 2, + "units": [ + 8, + 1 + ], + "bytes": 1022, + "capture_us": 400000, + "timeout_us": 20000 + }, + { + "id": "g1-l2-u4", + "base": 1, + "layer": 2, + "units": [ + 9, + 1 + ], + "bytes": 1059, + "capture_us": 400000, + "timeout_us": 20000 + }, + { + "id": "g2-base", + "base": 2, + "layer": 0, + "units": [ + 10, + 5 + ], + "bytes": 6400, + "capture_us": 600000 + }, + { + "id": "g2-l1-u0", + "base": 2, + "layer": 1, + "units": [ + 10, + 1 + ], + "bytes": 1522, + "capture_us": 600000, + "timeout_us": 60000 + }, + { + "id": "g2-l1-u1", + "base": 2, + "layer": 1, + "units": [ + 11, + 1 + ], + "bytes": 1559, + "capture_us": 600000, + "timeout_us": 60000 + }, + { + "id": "g2-l1-u2", + "base": 2, + "layer": 1, + "units": [ + 12, + 1 + ], + "bytes": 1596, + "capture_us": 600000, + "timeout_us": 60000 + }, + { + "id": "g2-l1-u3", + "base": 2, + "layer": 1, + "units": [ + 13, + 1 + ], + "bytes": 1633, + "capture_us": 600000, + "timeout_us": 60000 + }, + { + "id": "g2-l1-u4", + "base": 2, + "layer": 1, + "units": [ + 14, + 1 + ], + "bytes": 1670, + "capture_us": 600000, + "timeout_us": 60000 + }, + { + "id": "g2-l2-u0", + "base": 2, + "layer": 2, + "units": [ + 10, + 1 + ], + "bytes": 922, + "capture_us": 600000, + "timeout_us": 20000 + }, + { + "id": "g2-l2-u1", + "base": 2, + "layer": 2, + "units": [ + 11, + 1 + ], + "bytes": 959, + "capture_us": 600000, + "timeout_us": 20000 + }, + { + "id": "g2-l2-u2", + "base": 2, + "layer": 2, + "units": [ + 12, + 1 + ], + "bytes": 996, + "capture_us": 600000, + "timeout_us": 20000 + }, + { + "id": "g2-l2-u3", + "base": 2, + "layer": 2, + "units": [ + 13, + 1 + ], + "bytes": 1033, + "capture_us": 600000, + "timeout_us": 20000 + }, + { + "id": "g2-l2-u4", + "base": 2, + "layer": 2, + "units": [ + 14, + 1 + ], + "bytes": 1070, + "capture_us": 600000, + "timeout_us": 20000 + } + ] +} diff --git a/wai/staged-measure-conformance/sim/results.json b/wai/staged-measure-conformance/sim/results.json index 486d84bc8..9816943db 100644 --- a/wai/staged-measure-conformance/sim/results.json +++ b/wai/staged-measure-conformance/sim/results.json @@ -5597,6 +5597,1268 @@ ] } }, + { + "name": "per-track/strict-slow-timeout", + "stall_bound_us": 5000, + "max_stall_us": 0, + "result": { + "objects": [ + { + "id": "g0-base", + "sent_first_us": 20000, + "arrived_us": 52500, + "state": "delivered" + }, + { + "id": "g0-l1-u0", + "sent_first_us": 62750, + "arrived_us": 86500, + "state": "delivered" + }, + { + "id": "g0-l1-u1", + "sent_first_us": 66500, + "arrived_us": 90343, + "state": "delivered" + }, + { + "id": "g0-l1-u2", + "sent_first_us": 70343, + "arrived_us": 94278, + "state": "delivered" + }, + { + "id": "g0-l1-u3", + "sent_first_us": 74278, + "arrived_us": 98306, + "state": "delivered" + }, + { + "id": "g0-l1-u4", + "sent_first_us": 78306, + "arrived_us": null, + "state": "abandoned" + }, + { + "id": "g0-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-base", + "sent_first_us": 32500, + "arrived_us": 66750, + "state": "delivered" + }, + { + "id": "g1-l1-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l1-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l1-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l1-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l1-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-base", + "sent_first_us": 46750, + "arrived_us": 82750, + "state": "delivered" + }, + { + "id": "g2-l1-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + } + ], + "presentations": [ + [ + 0, + 1, + 56500 + ], + [ + 0, + 2, + 88750 + ], + [ + 1, + 1, + 56500 + ], + [ + 1, + 2, + 92343 + ], + [ + 2, + 1, + 56500 + ], + [ + 2, + 2, + 96278 + ], + [ + 3, + 1, + 56500 + ], + [ + 3, + 2, + 100306 + ], + [ + 4, + 1, + 56500 + ], + [ + 5, + 1, + 70750 + ], + [ + 6, + 1, + 70750 + ], + [ + 7, + 1, + 70750 + ], + [ + 8, + 1, + 70750 + ], + [ + 9, + 1, + 70750 + ], + [ + 10, + 1, + 86750 + ], + [ + 11, + 1, + 86750 + ], + [ + 12, + 1, + 86750 + ], + [ + 13, + 1, + 86750 + ], + [ + 14, + 1, + 86750 + ] + ], + "bfpf_sched": 6200, + "tfpf_us": 56500, + "tffs_us": [ + 56500, + 88750, + null + ], + "offered": 55650, + "delivered": 23322, + "dropped": 31128, + "abandoned": 1200, + "never_sent_capped": 0, + "missed": 0, + "stall_us": [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 2, + 0 + ], + [ + 3, + 0 + ], + [ + 4, + 0 + ], + [ + 5, + 0 + ], + [ + 6, + 0 + ], + [ + 7, + 0 + ], + [ + 8, + 0 + ], + [ + 9, + 0 + ], + [ + 10, + 0 + ], + [ + 11, + 0 + ], + [ + 12, + 0 + ], + [ + 13, + 0 + ], + [ + 14, + 0 + ] + ] + } + }, + { + "name": "per-track/fifo-timeout-subgroup", + "stall_bound_us": 65000, + "max_stall_us": 34287, + "result": { + "objects": [ + { + "id": "g0-base", + "sent_first_us": 20000, + "arrived_us": 52500, + "state": "delivered" + }, + { + "id": "g0-l1-u0", + "sent_first_us": 32500, + "arrived_us": 56250, + "state": "delivered" + }, + { + "id": "g0-l1-u1", + "sent_first_us": 36250, + "arrived_us": 60093, + "state": "delivered" + }, + { + "id": "g0-l1-u2", + "sent_first_us": 40093, + "arrived_us": 64028, + "state": "delivered" + }, + { + "id": "g0-l1-u3", + "sent_first_us": 44028, + "arrived_us": 68056, + "state": "delivered" + }, + { + "id": "g0-l1-u4", + "sent_first_us": 48056, + "arrived_us": 72176, + "state": "delivered" + }, + { + "id": "g0-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g0-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-base", + "sent_first_us": 52176, + "arrived_us": 86426, + "state": "delivered" + }, + { + "id": "g1-l1-u0", + "sent_first_us": 66426, + "arrived_us": 90204, + "state": "delivered" + }, + { + "id": "g1-l1-u1", + "sent_first_us": 70204, + "arrived_us": 94074, + "state": "delivered" + }, + { + "id": "g1-l1-u2", + "sent_first_us": 74074, + "arrived_us": 98037, + "state": "delivered" + }, + { + "id": "g1-l1-u3", + "sent_first_us": 78037, + "arrived_us": null, + "state": "abandoned" + }, + { + "id": "g1-l1-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g1-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-base", + "sent_first_us": 81037, + "arrived_us": 117037, + "state": "delivered" + }, + { + "id": "g2-l1-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l1-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + } + ], + "presentations": [ + [ + 0, + 1, + 56500 + ], + [ + 0, + 2, + 58500 + ], + [ + 1, + 1, + 56500 + ], + [ + 1, + 2, + 62093 + ], + [ + 2, + 1, + 56500 + ], + [ + 2, + 2, + 66028 + ], + [ + 3, + 1, + 56500 + ], + [ + 3, + 2, + 70056 + ], + [ + 4, + 1, + 56500 + ], + [ + 4, + 2, + 74176 + ], + [ + 5, + 1, + 90426 + ], + [ + 5, + 2, + 92426 + ], + [ + 6, + 1, + 90426 + ], + [ + 6, + 2, + 96074 + ], + [ + 7, + 1, + 90426 + ], + [ + 7, + 2, + 100037 + ], + [ + 8, + 1, + 90426 + ], + [ + 9, + 1, + 90426 + ], + [ + 10, + 1, + 121037 + ], + [ + 11, + 1, + 121037 + ], + [ + 12, + 1, + 121037 + ], + [ + 13, + 1, + 121037 + ], + [ + 14, + 1, + 121037 + ] + ], + "bfpf_sched": 6500, + "tfpf_us": 56500, + "tffs_us": [ + 56500, + 58500, + null + ], + "offered": 55650, + "delivered": 29614, + "dropped": 24836, + "abandoned": 1200, + "never_sent_capped": 0, + "missed": 0, + "stall_us": [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 2, + 0 + ], + [ + 3, + 0 + ], + [ + 4, + 0 + ], + [ + 5, + 19676 + ], + [ + 6, + 19676 + ], + [ + 7, + 19676 + ], + [ + 8, + 19676 + ], + [ + 9, + 19676 + ], + [ + 10, + 34287 + ], + [ + 11, + 34287 + ], + [ + 12, + 34287 + ], + [ + 13, + 34287 + ], + [ + 14, + 34287 + ] + ] + } + }, + { + "name": "per-track/fifo-no-timeout", + "stall_bound_us": 62480, + "max_stall_us": 8000, + "result": { + "objects": [ + { + "id": "g0-base", + "sent_first_us": 20000, + "arrived_us": 42000, + "state": "delivered" + }, + { + "id": "g0-l1-u0", + "sent_first_us": 22000, + "arrived_us": 42600, + "state": "delivered" + }, + { + "id": "g0-l1-u1", + "sent_first_us": 22600, + "arrived_us": 43215, + "state": "delivered" + }, + { + "id": "g0-l1-u2", + "sent_first_us": 23215, + "arrived_us": 43845, + "state": "delivered" + }, + { + "id": "g0-l1-u3", + "sent_first_us": 23845, + "arrived_us": 44490, + "state": "delivered" + }, + { + "id": "g0-l1-u4", + "sent_first_us": 24490, + "arrived_us": 45150, + "state": "delivered" + }, + { + "id": "g0-l2-u0", + "sent_first_us": 25150, + "arrived_us": 45510, + "state": "delivered" + }, + { + "id": "g0-l2-u1", + "sent_first_us": 25510, + "arrived_us": 45885, + "state": "delivered" + }, + { + "id": "g0-l2-u2", + "sent_first_us": 25885, + "arrived_us": 46275, + "state": "delivered" + }, + { + "id": "g0-l2-u3", + "sent_first_us": 26275, + "arrived_us": 46680, + "state": "delivered" + }, + { + "id": "g0-l2-u4", + "sent_first_us": 26680, + "arrived_us": 47100, + "state": "delivered" + }, + { + "id": "g1-base", + "sent_first_us": 27100, + "arrived_us": 49380, + "state": "delivered" + }, + { + "id": "g1-l1-u0", + "sent_first_us": 29380, + "arrived_us": 49985, + "state": "delivered" + }, + { + "id": "g1-l1-u1", + "sent_first_us": 29985, + "arrived_us": 50605, + "state": "delivered" + }, + { + "id": "g1-l1-u2", + "sent_first_us": 30605, + "arrived_us": 51239, + "state": "delivered" + }, + { + "id": "g1-l1-u3", + "sent_first_us": 31239, + "arrived_us": 51888, + "state": "delivered" + }, + { + "id": "g1-l1-u4", + "sent_first_us": 31888, + "arrived_us": 52552, + "state": "delivered" + }, + { + "id": "g1-l2-u0", + "sent_first_us": 32552, + "arrived_us": 52917, + "state": "delivered" + }, + { + "id": "g1-l2-u1", + "sent_first_us": 32917, + "arrived_us": 53297, + "state": "delivered" + }, + { + "id": "g1-l2-u2", + "sent_first_us": 33297, + "arrived_us": 53691, + "state": "delivered" + }, + { + "id": "g1-l2-u3", + "sent_first_us": 33691, + "arrived_us": 54100, + "state": "delivered" + }, + { + "id": "g1-l2-u4", + "sent_first_us": 34100, + "arrived_us": 54524, + "state": "delivered" + }, + { + "id": "g2-base", + "sent_first_us": 34524, + "arrived_us": 57084, + "state": "delivered" + }, + { + "id": "g2-l1-u0", + "sent_first_us": 37084, + "arrived_us": 57693, + "state": "delivered" + }, + { + "id": "g2-l1-u1", + "sent_first_us": 37693, + "arrived_us": 58317, + "state": "delivered" + }, + { + "id": "g2-l1-u2", + "sent_first_us": 38317, + "arrived_us": 58956, + "state": "delivered" + }, + { + "id": "g2-l1-u3", + "sent_first_us": 38956, + "arrived_us": 59610, + "state": "delivered" + }, + { + "id": "g2-l1-u4", + "sent_first_us": 39610, + "arrived_us": 60278, + "state": "delivered" + }, + { + "id": "g2-l2-u0", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u1", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u2", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u3", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + }, + { + "id": "g2-l2-u4", + "sent_first_us": null, + "arrived_us": null, + "state": "dropped" + } + ], + "presentations": [ + [ + 0, + 1, + 46000 + ], + [ + 0, + 2, + 48000 + ], + [ + 0, + 3, + 86000 + ], + [ + 1, + 1, + 46000 + ], + [ + 1, + 2, + 50000 + ], + [ + 1, + 3, + 88000 + ], + [ + 2, + 1, + 46000 + ], + [ + 2, + 2, + 56000 + ], + [ + 2, + 3, + 90000 + ], + [ + 3, + 1, + 46000 + ], + [ + 3, + 2, + 58000 + ], + [ + 3, + 3, + 92000 + ], + [ + 4, + 1, + 46000 + ], + [ + 4, + 2, + 64000 + ], + [ + 4, + 3, + 94000 + ], + [ + 5, + 1, + 54000 + ], + [ + 5, + 2, + 66000 + ], + [ + 5, + 3, + 96000 + ], + [ + 6, + 1, + 54000 + ], + [ + 6, + 2, + 68000 + ], + [ + 6, + 3, + 98000 + ], + [ + 7, + 1, + 54000 + ], + [ + 7, + 2, + 70000 + ], + [ + 7, + 3, + 100000 + ], + [ + 8, + 1, + 54000 + ], + [ + 8, + 2, + 72000 + ], + [ + 8, + 3, + 102000 + ], + [ + 9, + 1, + 54000 + ], + [ + 9, + 2, + 74000 + ], + [ + 9, + 3, + 104000 + ], + [ + 10, + 1, + 62000 + ], + [ + 10, + 2, + 76000 + ], + [ + 11, + 1, + 62000 + ], + [ + 11, + 2, + 78000 + ], + [ + 12, + 1, + 62000 + ], + [ + 12, + 2, + 80000 + ], + [ + 13, + 1, + 62000 + ], + [ + 13, + 2, + 82000 + ], + [ + 14, + 1, + 62000 + ], + [ + 14, + 2, + 84000 + ] + ], + "bfpf_sched": 14707, + "tfpf_us": 46000, + "tffs_us": [ + 46000, + 48000, + 86000 + ], + "offered": 55650, + "delivered": 50670, + "dropped": 4980, + "abandoned": 0, + "never_sent_capped": 0, + "missed": 0, + "stall_us": [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 2, + 0 + ], + [ + 3, + 0 + ], + [ + 4, + 0 + ], + [ + 5, + 4000 + ], + [ + 6, + 4000 + ], + [ + 7, + 4000 + ], + [ + 8, + 4000 + ], + [ + 9, + 4000 + ], + [ + 10, + 8000 + ], + [ + 11, + 8000 + ], + [ + 12, + 8000 + ], + [ + 13, + 8000 + ], + [ + 14, + 8000 + ] + ] + } + }, { "name": "audio/strict-subgroup", "stall_bound_us": 2480, diff --git a/wai/staged-measure-conformance/sim_verify.py b/wai/staged-measure-conformance/sim_verify.py index 8c72c11bf..0f94038c7 100755 --- a/wai/staged-measure-conformance/sim_verify.py +++ b/wai/staged-measure-conformance/sim_verify.py @@ -41,6 +41,8 @@ def check_model(m): frm, cnt = o["units"] if o["bytes"] < 1 or cnt < 1: raise ValueError(f"{o['id']}: no bytes or no units") + if o["layer"] == 0 and o.get("timeout_us") is not None: + raise ValueError(f"{o['id']}: a base takes the plan's timeout") for u in range(frm, frm + cnt): if (u, o["layer"]) in covered: raise ValueError(f"{o['id']}: unit {u} covered twice") @@ -71,8 +73,13 @@ def run_link(m, p): raise ValueError(f"{o['id']}: no capture time") avail.append(max(h, o["capture_us"])) to = snd["delivery_timeout_us"] - deadline = [None if (to["base"] if o["layer"] == 0 else to["refinement"]) is None - else avail[i] + (to["base"] if o["layer"] == 0 else to["refinement"]) for i, o in enumerate(objs)] + + def timeout(o): + """A base's is the plan's; a refinement's its own where the model gives one.""" + if o["layer"] == 0: + return to["base"] + return o["timeout_us"] if o.get("timeout_us") is not None else to["refinement"] + deadline = [None if timeout(o) is None else avail[i] + timeout(o) for i, o in enumerate(objs)] datagram = snd["carriage"] == "datagram" if datagram and any(not capped[i] and o["bytes"] > lnk["chunk_bytes"] for i, o in enumerate(objs)): raise ValueError("datagram-too-large") @@ -232,15 +239,20 @@ def simulate(m, p): return full -def bound(p): +def bound(m, p): + """§10.4: under fifo, T is the longest timeout a refinement of the model takes, its + own or the plan's; none where one takes none.""" snd, sink = p["sender"], p["sink"] if snd["delivery_timeout_us"]["base"] is not None: return None chunk = ceil_div(p["link"]["chunk_bytes"] * 1000, p["link"]["bytes_per_ms"]) + plan_t = snd["delivery_timeout_us"]["refinement"] + ts = [o["timeout_us"] if o.get("timeout_us") is not None else plan_t for o in m["objects"] if o["layer"] >= 1] + t = None if None in ts else max(ts, default=plan_t if plan_t is not None else 0) if snd["schedule"] == "strict-priority": link = chunk - elif snd["delivery_timeout_us"]["refinement"] is not None: - link = snd["delivery_timeout_us"]["refinement"] + chunk + elif t is not None: + link = t + chunk else: return None if sink["decode_order"] != "base-first": @@ -288,7 +300,7 @@ def main(d): continue if r["offered"] != r["delivered"] + r["dropped"] + r["abandoned"]: fails.append(f"{name}: bytes are not conserved") - b, w = bound(p), max_stall(r) + b, w = bound(m, p), max_stall(r) claim = c["claim"] if claim == "within-bound": if b is None or w is None or w > b: @@ -298,7 +310,7 @@ def main(d): fails.append(f"{name}: the plan has a bound") elif claim.startswith("exceeds:"): other = json.load(open(os.path.join(d, "sim", "plans", claim.split(":", 1)[1] + ".json"))) - ob = bound(other) + ob = bound(m, other) if b is not None or ob is None or (w is not None and w <= ob): fails.append(f"{name}: stall {w} does not pass {claim}'s bound {ob}") else: diff --git a/wai/wai-rs/src/bin/wai_stage_measure.rs b/wai/wai-rs/src/bin/wai_stage_measure.rs index c26d78025..b8febfb41 100644 --- a/wai/wai-rs/src/bin/wai_stage_measure.rs +++ b/wai/wai-rs/src/bin/wai_stage_measure.rs @@ -613,16 +613,31 @@ fn gop3() -> Model { for g in 0..3u32 { let from = g as u64 * 5; let cap = Some((from + 5) * unit_us); - objects.push(SimObject { id: format!("g{g}-base"), base: g, layer: 0, from, count: 5, bytes: 5000 + 700 * g as u64, capture_us: cap }); + objects.push(SimObject { id: format!("g{g}-base"), base: g, layer: 0, from, count: 5, bytes: 5000 + 700 * g as u64, capture_us: cap, timeout_us: None }); for (k, size) in [(1u8, 1500u64), (2, 900)] { for u in 0..5u64 { - objects.push(SimObject { id: format!("g{g}-l{k}-u{u}"), base: g, layer: k, from: from + u, count: 1, bytes: size + 37 * u + 11 * g as u64, capture_us: cap }); + objects.push(SimObject { id: format!("g{g}-l{k}-u{u}"), base: g, layer: k, from: from + u, count: 1, bytes: size + 37 * u + 11 * g as u64, capture_us: cap, timeout_us: None }); } } } Model { unit_us, objects } } +/// `gop3` with a delivery timeout on each refinement of its own, one per layer, as +/// MoQ tracks of one layer each set theirs (packaging §8.6): 60 ms on layer 1, 20 ms on +/// layer 2. +fn per_track() -> Model { + let mut m = gop3(); + for o in m.objects.iter_mut() { + o.timeout_us = match o.layer { + 0 => None, + 1 => Some(60_000), + _ => Some(20_000), + }; + } + m +} + /// The plans: two bounded link schedules over both carriages, a counter-plan for each /// unbounded part of §10.4's bound, a layer cap, and a live plan with playout /// deadlines. @@ -693,6 +708,11 @@ fn sim_cases() -> Vec<(String, String, &'static str, String)> { for (name, ..) in reorder_cases() { out.push((name.to_string(), format!("models/{name}.json"), name, "within-bound".to_string())); } + // Refinements with timeouts of their own: a fifo plan with no refinement timeout is + // bounded by theirs. + for plan in ["strict-slow-timeout", "fifo-timeout-subgroup", "fifo-no-timeout"] { + out.push((format!("per-track/{plan}"), "models/per-track.json".to_string(), plan, "within-bound".to_string())); + } for f in ["audio", "clip", "splat", "still"] { for (plan, claim) in [("strict-subgroup", "within-bound"), ("fifo-timeout-subgroup", "within-bound"), ("fifo-timeout-datagram", "within-bound"), ("fifo-no-timeout", "unbounded")] { out.push((format!("{f}/{plan}"), format!("fixture:{f}"), plan, claim.to_string())); @@ -707,7 +727,7 @@ fn sim_cases() -> Vec<(String, String, &'static str, String)> { /// decode overtook a large one's under base-first decode (`reorder-decode`). Each comes /// with its plan. fn reorder_cases() -> Vec<(&'static str, Model, Plan)> { - let o = |id: &str, base, layer, from, bytes, cap| SimObject { id: id.into(), base, layer, from, count: 1, bytes, capture_us: cap }; + let o = |id: &str, base, layer, from, bytes, cap| SimObject { id: id.into(), base, layer, from, count: 1, bytes, capture_us: cap, timeout_us: None }; let link = Plan { mode: Mode::Live, rtt_us: 0, @@ -737,6 +757,7 @@ fn reorder_cases() -> Vec<(&'static str, Model, Plan)> { fn sim_inputs() -> BTreeMap> { let mut files = BTreeMap::new(); files.insert("sim/models/gop3.json".to_string(), pretty(&gop3().to_json())); + files.insert("sim/models/per-track.json".to_string(), pretty(&per_track().to_json())); for (name, m, p) in reorder_cases() { files.insert(format!("sim/models/{name}.json"), pretty(&m.to_json())); files.insert(format!("sim/plans/{name}.json"), pretty(&p.to_json())); @@ -782,7 +803,7 @@ fn sim_results(c: &Corpus, evals: &[(Title, Evaluation)]) -> Result Result { - let b = sim::stall_bound(&read_plan(c, &format!("plans/{other}.json"))?).ok_or(format!("{name}: {other} has no bound"))?; + let b = sim::stall_bound_for(&m, &read_plan(c, &format!("plans/{other}.json"))?).ok_or(format!("{name}: {other} has no bound"))?; if bound.is_some() || worst.is_some_and(|w| w <= b as i64) { return Err(format!("{name}: the stall {worst:?} does not pass {other}'s bound {b}")); } @@ -1052,7 +1073,7 @@ fn sim_one(model: &str, plan: &str) -> Result<(), String> { let v: Value = serde_json::from_slice(&std::fs::read(plan).map_err(|e| format!("{plan}: {e}"))?).map_err(|e| e.to_string())?; let p = Plan::from_json(&v).map_err(|e| e.to_string())?; let r = sim::simulate(&m, &p).map_err(|e| e.to_string())?; - let out = json!({"label": "simulated", "plan": p.to_json(), "stall_bound_us": sim::stall_bound(&p), "max_stall_us": r.max_stall(), "result": r.to_json()}); + let out = json!({"label": "simulated", "plan": p.to_json(), "stall_bound_us": sim::stall_bound_for(&m, &p), "max_stall_us": r.max_stall(), "result": r.to_json()}); print!("{}", String::from_utf8(pretty(&out)).expect("UTF-8")); Ok(()) } diff --git a/wai/wai-rs/src/stage_measure/sim.rs b/wai/wai-rs/src/stage_measure/sim.rs index f613b91a9..5b2fe2e57 100644 --- a/wai/wai-rs/src/stage_measure/sim.rs +++ b/wai/wai-rs/src/stage_measure/sim.rs @@ -67,6 +67,10 @@ pub struct SimObject { pub bytes: u64, /// When its content is captured, for a live plan. pub capture_us: Option, + /// For a refinement, its own delivery timeout, in place of the plan's: what a + /// track's `DELIVERY_TIMEOUT` sets for each object on it (packaging §8.6). `None`: + /// the plan's timeout for its layer. + pub timeout_us: Option, } /// A title's objects, in the order the title lists them (the object index). @@ -103,9 +107,14 @@ fn mul(a: u64, b: u64) -> Result { /// Refuse members a section does not define, so a misspelt member is not ignored. fn only(v: &Value, members: &[&str], at: &str) -> Result<(), SimError> { + only_with(v, members, &[], at) +} + +/// [`only`], with `optional` members a section may leave out. +fn only_with(v: &Value, members: &[&str], optional: &[&str], at: &str) -> Result<(), SimError> { let o = v.as_object().ok_or_else(|| invalid(format!("{at}: not an object")))?; for k in o.keys() { - if !members.contains(&k.as_str()) { + if !members.contains(&k.as_str()) && !optional.contains(&k.as_str()) { return Err(invalid(format!("{at}: unknown member `{k}`"))); } } @@ -127,7 +136,7 @@ impl Model { let mut objects = Vec::new(); for (i, o) in v["objects"].as_array().ok_or_else(|| invalid("model: no objects"))?.iter().enumerate() { let at = format!("model object {i}"); - only(o, &["id", "base", "layer", "units", "bytes", "capture_us"], &at)?; + only_with(o, &["id", "base", "layer", "units", "bytes", "capture_us"], &["timeout_us"], &at)?; let units = o["units"].as_array().filter(|a| a.len() == 2).ok_or_else(|| invalid(format!("{at}: units is not [from, count]")))?; objects.push(SimObject { id: o["id"].as_str().ok_or_else(|| invalid(format!("{at}: id")))?.to_string(), @@ -137,6 +146,7 @@ impl Model { count: units[1].as_u64().ok_or_else(|| invalid(format!("{at}: units")))?, bytes: u64_of(o, "bytes", &at)?, capture_us: opt_u64(o, "capture_us", &at)?, + timeout_us: opt_u64(o, "timeout_us", &at)?, }); } let m = Model { unit_us: u64_of(v, "unit_us", "model")?, objects }; @@ -144,14 +154,20 @@ impl Model { Ok(m) } - /// The model as JSON. + /// The model as JSON; `timeout_us` only on an object that has its own. pub fn to_json(&self) -> Value { json!({ "format": MODEL_FORMAT, "unit_us": self.unit_us, - "objects": self.objects.iter().map(|o| json!({ - "id": o.id, "base": o.base, "layer": o.layer, "units": [o.from, o.count], "bytes": o.bytes, "capture_us": o.capture_us, - })).collect::>(), + "objects": self.objects.iter().map(|o| { + let mut j = json!({ + "id": o.id, "base": o.base, "layer": o.layer, "units": [o.from, o.count], "bytes": o.bytes, "capture_us": o.capture_us, + }); + if let Some(t) = o.timeout_us { + j["timeout_us"] = json!(t); + } + j + }).collect::>(), }) } @@ -169,6 +185,7 @@ impl Model { count: o.count, bytes: o.counted.total, capture_us: e.bases.get(o.base).and_then(|b| b.1), + timeout_us: None, }) .collect(); Model { unit_us: e.unit_us, objects } @@ -180,9 +197,9 @@ impl Model { Model { unit_us: self.unit_us, objects: self.objects.iter().filter(|o| o.layer == 0).cloned().collect() } } - /// A model is well formed when every object has bytes and units; no two objects - /// cover one unit at one layer; and every unit a layer `k ≥ 1` covers is covered at - /// layer `k − 1` by an object of the same base. + /// A model is well formed when every object has bytes and units; no base carries a + /// timeout of its own; no two objects cover one unit at one layer; and every unit a + /// layer `k ≥ 1` covers is covered at layer `k − 1` by an object of the same base. pub fn check(&self) -> Result<(), SimError> { if self.unit_us == 0 { return Err(invalid("model: unit_us is 0")); @@ -195,6 +212,9 @@ impl Model { if o.bytes == 0 || o.count == 0 { return Err(invalid(format!("{}: no bytes or no units", o.id))); } + if o.layer == 0 && o.timeout_us.is_some() { + return Err(invalid(format!("{}: a base takes the plan's timeout, never its own", o.id))); + } let end = o.from.checked_add(o.count).ok_or_else(|| invalid(format!("{}: units past u64", o.id)))?; if o.count > 1 << 24 { return Err(invalid(format!("{}: too many units", o.id))); @@ -488,7 +508,7 @@ fn run_link(m: &Model, p: &Plan) -> Result { .objects .iter() .zip(&avail) - .map(|(o, &a)| if o.layer == 0 { p.timeout_base_us } else { p.timeout_refinement_us }.map(|t| add(a, t)).transpose()) + .map(|(o, &a)| if o.layer == 0 { p.timeout_base_us } else { o.timeout_us.or(p.timeout_refinement_us) }.map(|t| add(a, t)).transpose()) .collect::>()?; if p.carriage == Carriage::Datagram { if let Some(o) = m.objects.iter().zip(&capped).find(|(o, c)| !**c && o.bytes > p.chunk_bytes) { @@ -731,12 +751,33 @@ fn simulate_once(m: &Model, p: &Plan) -> Result { /// timeout is not presented at all. §10.4 proves the bound for every other plan with a /// bounded link schedule and `base-first` decode, given that bases are sent and decoded /// in the order they became available, as [`simulate`] sends and decodes them. +/// +/// This is the bound over a model whose objects carry no timeout of their own; a model +/// that gives refinements their own is bounded by [`stall_bound_for`]. pub fn stall_bound(p: &Plan) -> Option { + bound_with(p, p.timeout_refinement_us) +} + +/// §10.4's bound for `p` over `m`: under `fifo`, `T` is the longest timeout any +/// refinement of `m` takes, its own or the plan's, and there is none where one takes none. +pub fn stall_bound_for(m: &Model, p: &Plan) -> Option { + let mut t: Option = None; + for o in m.objects.iter().filter(|o| o.layer >= 1) { + let own = o.timeout_us.or(p.timeout_refinement_us); + match own { + None => return bound_with(p, None), + Some(x) => t = Some(t.map_or(x, |y| y.max(x))), + } + } + bound_with(p, t.or(p.timeout_refinement_us).or(Some(0))) +} + +fn bound_with(p: &Plan, refinement_timeout: Option) -> Option { if p.timeout_base_us.is_some() { return None; } let chunk = p.serialise_us(p.chunk_bytes)?; - let link = match (p.schedule, p.timeout_refinement_us) { + let link = match (p.schedule, refinement_timeout) { (Schedule::StrictPriority, _) => chunk, (Schedule::Fifo, Some(t)) => t.checked_add(chunk)?, (Schedule::Fifo, None) => return None, @@ -760,7 +801,7 @@ pub struct StallViolation { /// Check `plan` over `model` against its bound; `Ok` for a plan with none. pub fn check_stall(model: &Model, plan: &Plan) -> Result<(), StallViolation> { - let Some(bound) = stall_bound(plan) else { return Ok(()) }; + let Some(bound) = stall_bound_for(model, plan) else { return Ok(()) }; let r = simulate(model, plan).map_err(|_| StallViolation { unit: 0, stall_us: None, bound_us: bound })?; for &(unit, s) in &r.stall_us { if s.is_none_or(|s| s > bound as i64) { diff --git a/wai/wai-rs/src/stage_measure/sim/tests.rs b/wai/wai-rs/src/stage_measure/sim/tests.rs index 66537ecb5..c33567a53 100644 --- a/wai/wai-rs/src/stage_measure/sim/tests.rs +++ b/wai/wai-rs/src/stage_measure/sim/tests.rs @@ -24,7 +24,7 @@ impl Rng { } fn obj(id: &str, base: u32, layer: u8, from: u64, count: u64, bytes: u64, capture_us: Option) -> SimObject { - SimObject { id: id.into(), base, layer, from, count, bytes, capture_us } + SimObject { id: id.into(), base, layer, from, count, bytes, capture_us, timeout_us: None } } /// `bases` groups of `per` units, each a base and `layers` refinement layers, each @@ -574,3 +574,68 @@ fn sums_past_64_bits_are_refused() { let q = Plan { chunk_bytes: MAX_FIGURE, rtt_us: MAX_FIGURE, vod_window_us: Some(0), ..p }; assert!(matches!(simulate(&edge, &q), Err(SimError::Overflow))); } + +/// A refinement's own timeout replaces the plan's for it alone (a MoQ track's +/// `DELIVERY_TIMEOUT`, packaging §8.6): of two refinements behind a base on a slow link, +/// the one whose own timeout passes first is dropped and the other delivered. A base +/// takes no timeout of its own, and the member reads and writes back. +#[test] +fn a_refinement_s_own_timeout() { + let p = Plan { + mode: Mode::Vod, + rtt_us: 0, + bytes_per_ms: 1000, + chunk_bytes: 1000, + schedule: Schedule::StrictPriority, + carriage: Carriage::Datagram, + layer_cap: None, + timeout_base_us: None, + timeout_refinement_us: Some(10_000), + vod_window_us: None, + workers: 1, + decode_order: DecodeOrder::BaseFirst, + decode_base_us: 1, + decode_refinement_us: 1, + playout_start_us: None, + late_refinement: LateRefinement::Present, + }; + // The base takes 1 ms; each refinement 1 ms after it. + let mut m = Model { unit_us: 40_000, objects: vec![obj("b", 0, 0, 0, 2, 1000, None), obj("l0", 0, 1, 0, 1, 1000, None), obj("l1", 0, 1, 1, 1, 1000, None)] }; + m.objects[1].timeout_us = Some(500); + let r = simulate(&m, &p).unwrap(); + assert_eq!((r.objects[1].state, r.objects[2].state), (ObjState::Dropped, ObjState::Delivered), "l0's own 0.5 ms passed; l1 has the plan's 10 ms"); + let back = Model::from_json(&m.to_json()).unwrap(); + assert_eq!(back, m); + assert!(m.to_json()["objects"][2].get("timeout_us").is_none(), "written only where an object has its own"); + let mut bad = m.to_json(); + bad["objects"][0]["timeout_us"] = serde_json::json!(5); + assert!(Model::from_json(&bad).is_err(), "a base takes the plan's timeout"); + // Under fifo, the bound's T is the longest refinement timeout, its own or the plan's; + // with every refinement timed, a plan with none of its own is bounded. + let fifo = Plan { schedule: Schedule::Fifo, timeout_refinement_us: None, ..p.clone() }; + assert_eq!(stall_bound(&fifo), None); + m.objects[2].timeout_us = Some(7000); + assert_eq!(stall_bound_for(&m, &fifo), Some(7000 + 1000 + 1)); + assert_eq!(stall_bound_for(&m, &Plan { timeout_refinement_us: Some(9000), ..fifo.clone() }), Some(7000 + 1000 + 1), "own timeouts replace the plan's"); +} + +/// §10.4's bound on [`targeted`] draws whose refinements take timeouts of their own, +/// some of them, under either schedule. +#[test] +fn bound_holds_with_object_timeouts() { + let mut r = Rng(0x0b1e_c7ed); + for i in 0..TARGETED_DRAWS / 4 { + let (mut m, p) = targeted(&mut r); + let c = p.serialise_us(p.chunk_bytes).unwrap(); + let all = r.coin(); + for o in m.objects.iter_mut().filter(|o| o.layer >= 1) { + if all || r.coin() { + o.timeout_us = Some([0, 1, c, 3 * c, 10 * c][r.range(0, 4) as usize]); + } + } + let p = if all && r.coin() { Plan { timeout_refinement_us: None, ..p } } else { p }; + let res = simulate(&m, &p).unwrap_or_else(|e| panic!("draw {i}: {e}\n{p:?}\n{}", m.to_json())); + conserved(&res); + check_stall(&m, &p).unwrap_or_else(|v| panic!("draw {i}: {v:?}\n{p:?}\n{}", m.to_json())); + } +} -- 2.54.0 (Apple Git-157) From abc75ec98ce4dced3c8bdc6c1c655af3d34ce866 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 14:21:54 -0400 Subject: [PATCH 02/21] =?UTF-8?q?wai-pack:=20staged=20packages=20=E2=80=94?= =?UTF-8?q?=20refinement=20layers,=20stills,=20a=20piece's=20ladder,=20liv?= =?UTF-8?q?e=20windows=20and=20simulated=20delivery=20(W4-4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The packer writes refinement layers, chooses each piece's representation by its ladder, packs live, and simulates delivery over MoQ; the corpus gains a staged golden that the independent verifier re-derives byte for byte. - Refinements (packaging §5.4): for each refinement rung of a piece's ladder, a wai.video.int_refine object over the int_motion base, written by wai-rs's refinement encoder (staged::wir1::encode) with the manifest `wai stage refine` writes; every layer applied to the decoded base by the reference sink before it is kept. The index states each prefix's tier, digest, SSE and deadline. - A piece's ladder and stills (§5.7, plan::piece_ladder): a rule's then.ladder, or the component's first base rung, its refinement chain and its floors. §3.4 item 10 now requires a rule's ladder to name exactly one base, every refinement over a rung it names and every floor. A wai.image.png base is a still, the piece's first frame held for the piece, written as a PNG whose every byte is stated (png.rs; the verifier's reader inflates with miniz_oxide, the one new dependency). - One coder (pack::Coder) for the on-demand and the live packer; the verifier holds each piece to its ladder and its plan's deadline, and decodes every prefix (a still held, each layer through the sink). - Catalogs: the MoQ catalog is a stage set of moq-streaming-format §4.1, written by staged::moq (wai_staged, a receipts track and an alias per media track, depends on a track of every layer below); `priority` (MOQT's, §8.5) and `wai_layer` leave it, and the registry drops `wai_layer`. HTTP views carry the component's default base capability only: a still's piece plays from the floor there (http-delivery §4.2), and its stage view and MoQ tracks list it. - Live (§9.2, live.rs, `wai-pack live`): frames pushed one at a time, pieces coded as the segmenter closes them, windows sealed by version-2 receipts chained live-window; its plan, bases and refinements are the on-demand package's byte for byte (`wai-pack listing --primary`). - Delivery (§8.9, delivery.rs, `wai-pack sim`): a thin consumer of stage_measure::sim — per-track timeouts, §8.5's priorities, the subscriber's cap, group receipts on each track's chain; delivered objects accepted by Merkle proof, dropped ones held by inclusion only. - Corpus: the golden video-staged-01 (fixture, policy, supplied floor and supplied_floor.py, which states how it was written), staged-mutations/, delivery/; video-base-01 keeps its objects, index and receipt (its MoQ catalog and LISTING change). - pack_verify.py re-derives every layer and still from the source (its own rANS coder and deflate), decodes every prefix (staged_verify.py's WIR1 and PNG readers), checks the MoQ catalog with staged_verify.py's §4.1 check, renders every catalog, and re-derives each delivery case through sim_verify.py's simulator. stage_diff.py runs layers, stills, piece plans and deliveries differentially. - Tests 5, 7, 12 and 13 over the staged golden (tests/staged.rs). Co-Authored-By: Claude Opus 5.5 --- wai/REGISTRY.md | 1 - wai/extensions/packaging.md | 244 +++++-- wai/pack-conformance/README.md | 52 +- wai/pack-conformance/catalogs/expected.json | 9 +- wai/pack-conformance/delivery/expected.json | 321 ++++++++ wai/pack-conformance/pack_verify.py | 689 ++++++++++++++---- .../class-named-classic-in-capitals/LISTING | 2 +- .../class-named-classic/LISTING | 2 +- .../component-medium/LISTING | 2 +- .../package-mutations/floor-cmaf/LISTING | 2 +- .../floor-extra-key-frame-allowed/LISTING | 2 +- .../floor-extra-key-frame/LISTING | 2 +- .../floor-framing-header/LISTING | 2 +- .../package-mutations/floor-motif/LISTING | 2 +- .../floor-object-corrupt/LISTING | 2 +- .../floor-object-missing/LISTING | 2 +- .../floor-opens-on-inter/LISTING | 2 +- .../floor-renderer-settings/LISTING | 2 +- .../floor-sequence-header/LISTING | 2 +- .../package-mutations/floor-target/LISTING | 2 +- .../index-capability/LISTING | 2 +- .../index-class-plan/LISTING | 2 +- .../index-class-rate/LISTING | 2 +- .../index-class-records/LISTING | 2 +- .../index-string-order/LISTING | 2 +- .../patent-pool-floor-by-override/LISTING | 2 +- .../patent-pool-floor/LISTING | 2 +- .../policy-json-relaxes-royalty-free/LISTING | 2 +- .../receipt-duplicate-member/LISTING | 2 +- .../package-mutations/receipt-newline/LISTING | 2 +- .../package-mutations/receipt-pretty/LISTING | 2 +- .../package-mutations/sha256-file/LISTING | 2 +- .../packs/video-base-01/package/LISTING | 2 +- .../video-base-01/package/moq/catalog.json | 2 +- .../packs/video-staged-01/floor/v.obu | Bin 0 -> 6316 bytes .../packs/video-staged-01/key.hex | 1 + .../packs/video-staged-01/package/LISTING | 41 ++ .../video-staged-01/package/dash/classic.mpd | 24 + .../video-staged-01/package/dash/mobile.mpd | 35 + .../packs/video-staged-01/package/dash/tv.mpd | 51 ++ ...8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s | Bin 0 -> 1824 bytes ...803c3477ed293bc1be12608be69ffd652de768.m4s | Bin 0 -> 2210 bytes ...0ae5c256fb5086b13389099cf0babf164986fa.m4s | Bin 0 -> 981 bytes ...ff68df725d27cf7768984d6f6aca0149dd2362.m4s | Bin 0 -> 2800 bytes ...64b4ad937362fa89b834f4838349bcb789fc5b.m4s | Bin 0 -> 981 bytes .../video-staged-01/package/floor/v/init.mp4 | Bin 0 -> 628 bytes .../package/hls/classic/floor-v.m3u8 | 18 + .../package/hls/classic/master.m3u8 | 5 + .../package/hls/legacy/master.m3u8 | 5 + .../package/hls/mobile/master.m3u8 | 6 + .../package/hls/mobile/wai-v.m3u8 | 20 + .../package/hls/mobile/wai.m3u8 | 7 + .../package/hls/tv/master.m3u8 | 6 + .../video-staged-01/package/hls/tv/wai-v.m3u8 | 25 + .../video-staged-01/package/hls/tv/wai.m3u8 | 7 + .../video-staged-01/package/index-v.wsix | Bin 0 -> 2792 bytes .../video-staged-01/package/moq/catalog.json | 1 + ...c8d8d0c3b5642a9784f00d30ce2ab176341c61.wai | Bin 0 -> 9698 bytes ...b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai | Bin 0 -> 2313 bytes ...bca465383cc3d478b6474e2a91e592796f320b.wai | Bin 0 -> 30699 bytes ...99007817784a12129e431066290f8ff5df562e.wai | Bin 0 -> 1234 bytes ...a88774f94be9eec5b706c16835a59bbe59a0ec.wai | Bin 0 -> 67779 bytes ...b60252cd8c8c32460c13e124ad70d9a4acd90f.wai | Bin 0 -> 974 bytes ...120d0e45a6b4651884ece2eaf5b8954c188038.wai | Bin 0 -> 2843 bytes ...69b152e55b175983e46521ed2bb40999fc511d.wai | Bin 0 -> 81407 bytes ...ee7c5814106011d57be9c4f6396c5dbd9902a9.wai | Bin 0 -> 31610 bytes ...cd00baae6574568225afa839e36af62f6bbdae.wai | Bin 0 -> 1980 bytes ...8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai | Bin 0 -> 101295 bytes ...3a0210163886054d53d35f1c4d414bf1c22d27.wai | Bin 0 -> 26030 bytes ...36412c91e97b7d0c16afb07e5f6e68a39ef66c.wai | Bin 0 -> 86299 bytes ...4efcf4702b820bd5295aebdd252dd8dbc409e9.wai | Bin 0 -> 40303 bytes .../package/package.receipt.json | 1 + .../packs/video-staged-01/package/plan-v.txt | 5 + .../packs/video-staged-01/package/policy.json | 1 + .../packs/video-staged-01/package/policy.toml | 94 +++ .../package/stages/legacy.json | 1 + .../package/stages/mobile.json | 1 + .../video-staged-01/package/stages/tv.json | 1 + .../packs/video-staged-01/policy.toml | 94 +++ wai/pack-conformance/stage_diff.py | 259 +++++++ .../staged-mutations/expected.json | 34 + .../staged-mutations/layer-dropped/LISTING | 40 + .../layer-dropped/dash/tv.mpd | 49 ++ .../layer-dropped/hls/tv/wai-v.m3u8 | 24 + .../layer-dropped/hls/tv/wai.m3u8 | 7 + .../layer-dropped/index-v.wsix | Bin 0 -> 2664 bytes .../layer-dropped/moq/catalog.json | 1 + .../layer-dropped/package.receipt.json | 1 + .../layer-dropped/stages/tv.json | 1 + .../staged-mutations/layer-replaced/LISTING | 41 ++ .../layer-replaced/dash/tv.mpd | 51 ++ .../layer-replaced/hls/tv/wai-v.m3u8 | 25 + .../layer-replaced/hls/tv/wai.m3u8 | 7 + .../layer-replaced/index-v.wsix | Bin 0 -> 2792 bytes ...dc965806c5323fa223e0f5e7da666a46fd4e3f.wai | Bin 0 -> 82235 bytes .../layer-replaced/package.receipt.json | 1 + .../layer-replaced/stages/tv.json | 1 + .../refinement-deadline/LISTING | 41 ++ .../refinement-deadline/index-v.wsix | Bin 0 -> 2792 bytes .../refinement-deadline/moq/catalog.json | 1 + .../refinement-deadline/package.receipt.json | 1 + .../refinement-deadline/stages/tv.json | 1 + .../still-held-digest/LISTING | 41 ++ .../still-held-digest/index-v.wsix | Bin 0 -> 2792 bytes .../still-held-digest/package.receipt.json | 1 + .../still-not-the-ladder/LISTING | 41 ++ .../still-not-the-ladder/index-v.wsix | Bin 0 -> 2792 bytes .../still-not-the-ladder/package.receipt.json | 1 + .../still-not-the-ladder/policy.json | 1 + .../still-not-the-ladder/policy.toml | 94 +++ .../still-not-the-ladder/stages/legacy.json | 1 + .../still-not-the-ladder/stages/mobile.json | 1 + .../still-not-the-ladder/stages/tv.json | 1 + wai/pack-conformance/supplied_floor.py | 92 +++ wai/registry/capabilities.json | 7 - wai/wai-pack/Cargo.lock | 16 + wai/wai-pack/Cargo.toml | 5 +- wai/wai-pack/src/bin/wai_pack.rs | 133 +++- wai/wai-pack/src/catalog.rs | 249 +++++-- wai/wai-pack/src/conformance.rs | 110 +++ wai/wai-pack/src/delivery.rs | 247 +++++++ wai/wai-pack/src/encode.rs | 42 ++ wai/wai-pack/src/fixture.rs | 20 +- wai/wai-pack/src/golden.rs | 235 +++++- wai/wai-pack/src/lib.rs | 14 +- wai/wai-pack/src/live.rs | 255 +++++++ wai/wai-pack/src/objects.rs | 19 +- wai/wai-pack/src/pack.rs | 441 +++++++---- wai/wai-pack/src/plan.rs | 37 +- wai/wai-pack/src/png.rs | 342 +++++++++ wai/wai-pack/src/policy.rs | 9 +- wai/wai-pack/src/verify.rs | 122 +++- wai/wai-pack/tests/refusals.rs | 43 +- wai/wai-pack/tests/staged.rs | 307 ++++++++ wai/wai-rs/src/codecs/registry.rs | 1 - 135 files changed, 4798 insertions(+), 538 deletions(-) create mode 100644 wai/pack-conformance/delivery/expected.json create mode 100644 wai/pack-conformance/packs/video-staged-01/floor/v.obu create mode 100644 wai/pack-conformance/packs/video-staged-01/key.hex create mode 100644 wai/pack-conformance/packs/video-staged-01/package/LISTING create mode 100644 wai/pack-conformance/packs/video-staged-01/package/dash/classic.mpd create mode 100644 wai/pack-conformance/packs/video-staged-01/package/dash/mobile.mpd create mode 100644 wai/pack-conformance/packs/video-staged-01/package/dash/tv.mpd create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s create mode 100644 wai/pack-conformance/packs/video-staged-01/package/floor/v/init.mp4 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/classic/floor-v.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/classic/master.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/legacy/master.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/mobile/master.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/tv/master.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/packs/video-staged-01/package/index-v.wsix create mode 100644 wai/pack-conformance/packs/video-staged-01/package/moq/catalog.json create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai create mode 100644 wai/pack-conformance/packs/video-staged-01/package/package.receipt.json create mode 100644 wai/pack-conformance/packs/video-staged-01/package/plan-v.txt create mode 100644 wai/pack-conformance/packs/video-staged-01/package/policy.json create mode 100644 wai/pack-conformance/packs/video-staged-01/package/policy.toml create mode 100644 wai/pack-conformance/packs/video-staged-01/package/stages/legacy.json create mode 100644 wai/pack-conformance/packs/video-staged-01/package/stages/mobile.json create mode 100644 wai/pack-conformance/packs/video-staged-01/package/stages/tv.json create mode 100644 wai/pack-conformance/packs/video-staged-01/policy.toml create mode 100644 wai/pack-conformance/stage_diff.py create mode 100644 wai/pack-conformance/staged-mutations/expected.json create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/LISTING create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/moq/catalog.json create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/layer-dropped/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/LISTING create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/objects/2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f.wai create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/layer-replaced/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/refinement-deadline/LISTING create mode 100644 wai/pack-conformance/staged-mutations/refinement-deadline/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/refinement-deadline/moq/catalog.json create mode 100644 wai/pack-conformance/staged-mutations/refinement-deadline/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/refinement-deadline/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/still-held-digest/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-held-digest/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-held-digest/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.json create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.toml create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/legacy.json create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/tv.json create mode 100644 wai/pack-conformance/supplied_floor.py create mode 100644 wai/wai-pack/src/delivery.rs create mode 100644 wai/wai-pack/src/live.rs create mode 100644 wai/wai-pack/src/png.rs create mode 100644 wai/wai-pack/tests/staged.rs diff --git a/wai/REGISTRY.md b/wai/REGISTRY.md index 987b33608..2bd84ae5f 100644 --- a/wai/REGISTRY.md +++ b/wai/REGISTRY.md @@ -301,7 +301,6 @@ and a role are fixed once registered. | `wai_capabilities` | catalog | defined | moq-streaming-format §4 | | `wai_delivery_timeout_ms` | catalog | defined | packaging §8 | | `wai_determinism` | catalog | defined | moq-streaming-format §4 | -| `wai_layer` | catalog | defined | packaging §8 | | `wai_receipt_alias` | catalog | defined | moq-streaming-format §4 | | `wai_receipts` | catalog | defined | moq-streaming-format §4 | | `wai_receipts_track` | catalog | defined | moq-streaming-format §4 | diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index 6c6bfae9d..63fbf7051 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -3,13 +3,15 @@ > Status: Draft. A packager turns sources into WAI objects under a declared policy; > this extension fixes what a verifier recomputes. This revision defines the policy, > its digest and what it may ask a packager to emit (§3), how a source is cut into -> pieces (§4), the objects a packager writes (§5), the stage index (§6), the floor -> (§7), what each class is offered over HTTP and MoQ (§8), the package receipt (§9), -> and the package and its verification (§11). Reference impl: `wai-pack` — `policy`, -> `capcheck`, `source`, `scenecut`, `classify`, `encode`, `floor`, `cmaf`, `catalog`, -> `pack`, `verify`; in `wai-rs`, the encoder `int_video_enc`, the stage index -> `stage_index` and the receipt `package`; the corpus -> [`pack-conformance/`](../pack-conformance/). Keywords **MUST**, **MUST NOT**, +> pieces (§4), the objects a packager writes, refinements and stills among them, and a +> piece's ladder (§5), the stage index (§6), the floor (§7), what each class is offered +> over HTTP and MoQ and how its delivery is simulated (§8), the package receipt and live +> windows (§9), and the package and its verification (§11). Reference impl: `wai-pack` — +> `policy`, `capcheck`, `source`, `scenecut`, `classify`, `plan`, `encode`, `png`, +> `floor`, `cmaf`, `catalog`, `pack`, `live`, `delivery`, `verify`; in `wai-rs`, the +> encoder `int_video_enc`, the refinement encoder `staged::wir1`, the stage index +> `stage_index`, the receipt `package` and the delivery simulator `stage_measure::sim`; +> the corpus [`pack-conformance/`](../pack-conformance/). Keywords **MUST**, **MUST NOT**, > **SHOULD**, **MAY** are RFC 2119/8174. ## 1. Scope @@ -159,8 +161,10 @@ first that fails is the refusal: refines for a refinement; the policy has one more layer than its highest rung layer. No class's and no rule's `max_layer` exceeds the highest (`policy-class-layer`). -10. A rule's `then.ladder` names known rungs, all of one component, at least one of - them a base (`policy-rule-ladder`). +10. A rule's `then.ladder` names known rungs, all of one component, each once: + exactly one base, every refinement over a rung it names (so its layers are a chain + from that base), and every floor rung of the component (every piece has a floor, + §7.1) (`policy-rule-ladder`). 11. `live.layer_lag_ms` has one entry per layer, the first 0, the entries non-decreasing (`policy-layer-lag`). 12. The deadline values (`defaults.deadline_ms` and every rule's `then.deadline_ms`) @@ -292,8 +296,9 @@ object. The reference packager writes a base's manifest as SPEC §3 orders it, w `media` the component's media, `intent` `replicate`, `model_requirement` the rung's capability with no `fallback` member, `conditioning.kind` the capability's last dot-separated segment (`int_motion`, `av1`), and `target` `{"frames": n, "h": h, "w": w}` -(the piece's frame count and dimensions; for a floor, its sequence header's maximum -frame height and width); a floor's `derived_from` (§7.2) comes after `conditioning`. +(the piece's frame count and dimensions, for a still too, whose one frame is held for +the piece, §5.7; for a floor, its sequence header's maximum frame height and width); a +floor's `derived_from` (§7.2) comes after `conditioning`. **5.3** *Name.* An object is named by its content hash ([jwp-receipts](jwp-receipts.md) §2.1), `BLAKE3("moq-jwp:object\x01" ‖ envelope)`, as `objects/<64 lowercase hex>.wai`: @@ -303,8 +308,30 @@ its HTTP strong `ETag` ([http-delivery](http-delivery.md) §5.2). **5.4** *Refinements.* A refinement object carries `refines` ([staged-delivery](staged-delivery.md) §3): `base` the content hash of its piece's base, `layer` its layer, and as producers in `prev` only objects of the layers below it, the -object its entry refines (§6.2) among them. The reference packager does not write -refinements yet. +object its entry refines (§6.2) among them. + +The reference packager writes, for each refinement rung of a piece's ladder (§5.7), one +object of `wai.video.int_refine` over the piece's `wai.video.int_motion` base, layer by +layer. Its manifest is the one `wai stage refine` writes, as SPEC §3 orders it: `media` +the component's, `intent` `replicate`, the capability with no `fallback`, +`conditioning.kind` `wir1`, `refines` with `base` the base's content hash, `prev` the +content hash of the object of the layer below (the base for layer 1) as a string, and +`layer`, and `target` null. Its payload is a `WIR1` (staged-delivery Appendix A) of unit +kind RGB8, one unit per frame of the piece, the identity predictor and one table, +written by `wai-rs`'s refinement encoder (`staged::wir1::encode`) at the rung's step +`q`: + +- each sample's residual is `rdiv(source − below, q)`, the quotient rounded half away + from zero, where *below* is the stage the layer refines as a sink reconstructs it; + no frame predicts another; +- the table's `offset` is the least residual and it spans every residual (where they + span more than 4095, the 4095 around the lower median), fitted by §5.5's rule, the + escape at 1; a residual past it is coded through the escape; +- one rANS stream per frame. + +The stage the layer reconstructs, `clamp(below + r·q, 0, 255)`, is what the next layer +refines. The packer applies each layer to the decoded base with the reference sink +(staged-delivery §4) before it keeps it, and the sink's output must be the encoder's. **5.5** *Integer video.* An object of `wai.video.int_motion` carries a `WIV1` payload with keyframe 0, as SPEC §5 pins it ([integer-payloads](integer-payloads.md) §3); this @@ -356,6 +383,39 @@ candidates that meet it alike are decided by their step. Where no candidate meet it chooses the smallest. The function the reference packager's base encode calls is `wai_pack::encode::choose_q`. +**5.7** *A piece's ladder, and stills.* A piece is coded in the rungs of its **ladder**: +those the matching rules' `then.ladder` names (§3.4 folds them: the last that sets it), +where they are rungs of the piece's component; otherwise the component's default ladder, +its first base rung in ladder order, the chain of refinement rungs over it, and its floor +rungs. A rule's ladder therefore chooses a piece's representation: its base, how many +layers refine it, and nothing else (§3.4 item 10). A piece has one entry per rung of its +ladder: its base, each refinement in layer order, then each floor (§6.2), and a +verifier holds an index to that (§11.3). + +A base of `wai.image.png` is a **still**: the piece's first frame, held for the piece. A +sink presents that frame for each of the piece's frames, so its prefix is the frame +repeated and its digest the clip digest of the repeats (§6.5), its `sse` is over the +repeats against the piece's frames (§6.6), and its manifest's `target` names the piece's +frames (§5.2). The reference packager writes the frame as a PNG every byte of which is a +pure function of the frame, so an independent implementation writes the same file: + +- the signature, `IHDR` (the width, the height, bit depth 8, colour type 2, compression + 0, filter 0, interlace 0), one `IDAT` holding the whole compressed stream, and `IEND`; + each chunk a big-endian length, its type, its data and the CRC-32 of type and data; +- every row filtered `Sub` (filter byte 1): each byte less the byte three before it in + the row, modulo 256, the first pixel's bytes less 0; +- a zlib stream (RFC 1950) of `0x78 0x01`, one final deflate block (RFC 1951) of the + fixed Huffman codes, and the Adler-32 of the filtered rows; +- the block's symbols, from the first byte of the filtered rows: where at least three + bytes remain and an earlier position within 32768 bytes starts with the same three + bytes, the latest such position is the match, as long as the two runs agree (at most + 258 bytes), written as that length and the distance to it; otherwise one literal. + Every position a symbol covers that has three bytes left becomes the latest position + of its three bytes. The block ends with symbol 256, and the last byte is padded with + zero bits. + +A verifier decodes a still with any PNG reader of 8-bit RGB without interlace. + ## 6. Stage index **6.1** A package carries, per component, one stage index, the file `index-.wsix` @@ -517,8 +577,8 @@ entropy-consistency for `NeuralEntropyExact`, and none otherwise (SPEC §5, §7) **6.5** `prefix_digest` is the SHA-256 decoded-output digest (SPEC §7, *Digests*) of the prefix the entry completes when `prefix_tier` is decode-equivalence, and all zero -otherwise: for video, the clip digest over RGB8 frames; for an image, the RGB8 frame -digest; for audio, the digest of the s16le interleaved samples; for world, feed, avatar +otherwise: for video, the clip digest over RGB8 frames (for a still, of its frame held +for the piece's frames, §5.7); for an image, the RGB8 frame digest; for audio, the digest of the s16le interleaved samples; for world, feed, avatar and film entries, zero (their state hashes live in their receipts). A verifier decodes the prefix and compares. @@ -641,14 +701,19 @@ offered in playlists of their own. and **MUST NOT** be used to decide whether a `wai1.*` value can be decoded ([http-delivery](http-delivery.md) §4.3). - Each class's WAI playlist **MUST** name its class and its stage view. - +- A variant lists no bases without a capability in common ([http-delivery](http-delivery.md) + §4.2, `codecs_no_common_capability`). A class's HTTP view lists a piece only where the + piece's base is of the component's default base capability (§5.7, its first base rung's), + and plays the floor for any other piece, as for a piece the class takes as floor only: a + still (§5.7) among motion bases is carried over HTTP by the floor. The class's stage view + (§8.8) and its MoQ tracks (§8.4) list it. Durations: a piece of *d* units lasts `floor(d · den · 1000 / num)` milliseconds, written `S.mmm` (whole seconds, a full stop, three digits). A target duration is the largest `ceil(d · den / num)`. A rate is `ceil(8 · bytes · num / (d · den))` bits per second; a variant's `BANDWIDTH` is the largest over the pieces it lists and `AVERAGE-BANDWIDTH` the rate of all their bytes over all their durations. A floor variant's bytes per piece are its fragment's; a WAI variant's are the listed bases' and -refinements', over the pieces the class does not take as floor only. Every playlist +refinements', over the pieces its HTTP view lists (above). Every playlist line ends with a line feed. For component *c*, of frame size `W×H`, whose floor has codecs string *F* and maximum frame size `FW×FH`: @@ -662,9 +727,9 @@ codecs string *F* and maximum frame size `FW×FH`: `#EXT-X-INDEPENDENT-SEGMENTS`, `#EXT-X-MAP:URI="../../floor//init.mp4"`, then per piece `#EXTINF:,` and `../../floor//.m4s`, then `#EXT-X-ENDLIST`. - `hls//master.m3u8`: the classical master, byte for byte, with, when the class - lists a WAI object, `#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8"` + lists a WAI object over HTTP, `#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8"` after `#EXT-X-INDEPENDENT-SEGMENTS`, and no other change. -- `hls//wai.m3u8`, when the class lists a WAI object: `#EXTM3U`, +- `hls//wai.m3u8`, when the class's HTTP view lists a WAI object: `#EXTM3U`, `#EXT-X-VERSION:7`, `#EXT-X-INDEPENDENT-SEGMENTS`, `#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE=""`, `#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/.json"`, @@ -673,7 +738,8 @@ codecs string *F* and maximum frame size `FW×FH`: over the objects the class lists. - `hls//wai-.m3u8`: the floor playlist's lines without `#EXT-X-MAP`, adjacent to it ([http-delivery](http-delivery.md) §11.2); per piece, `#EXT-X-GAP` when the - class takes it as floor only, `#EXTINF:,`, each refinement the class lists, + class's HTTP view lists nothing of it (a floor-only piece, or one whose base is not of + the default base capability), `#EXTINF:,`, each refinement the class lists, in entry order, as `#WAI-LAYER:N=,URI="../../objects/.wai#n="`, then its base as `../../objects/.wai#n=`: a layer line follows its segment's `EXTINF` ([http-delivery](http-delivery.md) §10.4). @@ -693,7 +759,7 @@ one `AdaptationSet`, `` and ``, indented by two spaces a level: ``: ``, a `SegmentTimeline` of one `` per piece, and one `` per piece. -- `dash/.mpd`, when the class lists a WAI object: +- `dash/.mpd`, when the class's HTTP view lists a WAI object: `` holding ``, ``s of schemes `tag:transaction.science,2026:wai/floor` (value the @@ -711,31 +777,40 @@ of one component; how the variants and groups of several components are written defined with the packing of several components. **8.4** *MoQ.* `moq/catalog.json` is RFC 8785 canonical JSON and a line feed: -`{"tracks": [...], "version": 1}`, the tracks by layer, then by deadline ascending, -then the floor. Each (component, layer, deadline) is one track of -`packaging` `"wai"`: name `/l`, or `/l@` for a refinement whose -deadline is not `defaults.deadline_ms`; `renderGroup` 1; `priority` §8.5's; -`wai_capabilities` its entries' capabilities by first appearance; `wai_layer` *k*; -`wai_receipts` false (no object of this revision carries a `WAI_RECEIPT` property); -`wai_determinism` the weakest tier of its entries, absent when that is none. The base -track has `altGroup` 1 when the component has a floor. MSF's catalog defines `depends` -as the tracks on which a track is dependent, and this section has a sink receive every -one of them (below): a dependency is all of its tracks, not any one. So a refinement track -`depends` on every track that carries an entry its own entries refine, in track order — -the layer below at the same deadline, where a piece's layers share its deadline as the -reference packager writes them — and never on a track the catalog does not hold. It -carries `wai_delivery_timeout_ms` (§8.6). The floor is a track -`/floor` of `packaging` `"cmaf"` with `altGroup` 1, `renderGroup` 1, `codec`, -`width` and `height`. A sink **MUST NOT** subscribe to a track unless it also receives -every track that track depends on, so a sink that selects the floor subscribes to no -layer track. `wai_layer` and `wai_delivery_timeout_ms` are catalog fields this -extension defines under the `wai_` prefix of -[moq-streaming-format](moq-streaming-format.md) §4; a later revision of that extension -may define them there instead. +`{"tracks": [...], "version": 1}`. A component's bases and refinements are a stage set of +[moq-streaming-format](moq-streaming-format.md) §4.1, one track per (layer, deadline), +by layer, then by deadline ascending, each written as that section's entry: namespace +the policy's `asset_id`; name `/l`, or `/l@` for a refinement +whose deadline is not `defaults.deadline_ms`; `packaging` `"wai"`; `isLive` false; +`role` `video`; `renderGroup` 1; `wai_staged` with `set` the component's id, `layer` *k* +and `lead_ms` null (an on-demand package has no live schedule); `wai_capabilities` its +entries' capabilities by first appearance (a still among them on the base track, §5.7); +`wai_determinism` the weakest tier of its entries, absent when that is none; and +receipts declared: `wai_receipts` true, `wai_receipt_alias` the first eight bytes of +`BLAKE3("wai:pack-receipt-alias\x01" ‖ namespace ‖ 0x00 ‖ name)` as a little-endian +integer, less than `2^53` (so two packages published under one key keep apart chains), +and `wai_receipts_track` `/receipts`. A track `depends` on every track that +carries an entry below one of its own entries, down the chain to the base, in track +order: a track of each layer below it, none at or above it, and never one the catalog +does not hold. A sink receives every track a track depends on (moq-streaming-format +§4.1), so a dependency is all of them. A refinement track carries +`wai_delivery_timeout_ms` (§8.6), a catalog field this extension defines under the `wai_` +prefix of moq-streaming-format §4. The base track has `altGroup` 1 when the component has +a floor: the floor is a track `/floor` of `packaging` `"cmaf"`, in the same namespace, +with `altGroup` 1, `renderGroup` 1, `codec`, `width` and `height`, and a sink that +selects the floor subscribes to no layer track. The receipts tracks follow, one per media +track in its order, each `{"isLive": false, "name": "/receipts", "namespace", +"packaging": "wai-receipts"}`. The catalog passes moq-streaming-format §4.1's check, and a +packager writes no other. A package holds no receipt: the publisher signs each track's +groups (a group a piece) on that track's chain as it publishes them (jwp-receipts §2.4.1; +§8.9 simulates it). **8.5** *Priority.* The base track's publisher priority is 64 and layer *k*'s -`64 + 32·k`: a refinement track's value is strictly greater than that of the track it -depends on, so it is delivered after it (MOQT: a lower value is delivered first). +`64 + 32·k`: a refinement track's value is strictly greater than that of every track it +depends on, so it is delivered after them (MOQT: a lower value is delivered first), and +moq-streaming-format §4.1's rule holds; a receipts track's is its media track's. The +priority is MOQT's (`DEFAULT_PUBLISHER_PRIORITY`), set by the publisher, not a catalog +field. **8.6** *Deadline.* An entry's `deadline_ms` is the longest a refinement may arrive after the base of its piece was published and still be presented; the rules' or @@ -752,12 +827,43 @@ to wait for a refinement. What the receipts of a dropped object say is **8.8** *Stage views.* `stages/.json` is RFC 8785 canonical JSON and a line feed: `{"class": "", "components": {"": {"media_playlist": "../hls//wai-.m3u8", "pieces": [...]}}, "policy_digest": ""}`, with -`media_playlist` only when the class lists a WAI object, and each piece +`media_playlist` only when the class's HTTP view lists a WAI object (§8.3), and each piece `{"dur", "p", "start", "stages": [...]}` with `"floor_only": true` when the class takes it as floor only (and `stages` empty), and each listed stage `{"bytes", "h", "layer"}`, a refinement's with `"refines"` (the content hash of the entry it refines) and `"deadline_ms"`. +**8.9** *Delivery, simulated.* How a package's objects reach a subscriber over a link is +simulated by staged-measurement's delivery simulator ([staged-measurement](staged-measurement.md) +§10, Appendix B); its figures are labelled simulated, with their plan, and are never a +measurement of a network. The reference packager's `delivery` states the model and the +plan, and reads the outcome: + +- **Model.** Each object a class lists of each piece (§8.1; a floor-only piece gives none), + on the component's frames as units (a frame of a whole number of microseconds): base the + piece, its layer, units the piece's frames, bytes the whole envelope, and its capture + time the instant it is published. A live packer (§9.2) publishes a piece's base when the + frame that decides the piece's end has arrived (the next piece's first frame plus + `lookahead_frames`, or the source's last frame, §4.3; frame *f* arriving at + `f · frame_us`), plus the encoder's delay, and layer *k* `layer_lag_ms[k]` after its + base. Each refinement takes its track's delivery timeout, `deadline_ms − + layer_lag_ms[k]` milliseconds (§8.6), as its own (staged-measurement Appendix B). +- **Plan.** Live; the link's round trip, rate and chunk; strict priority, which is §8.5's + order; each (piece, layer) a subgroup of its own; no base timeout; the subscriber's + layer cap (moq-streaming-format §4.1, *Capping*); one decode worker, bases first, each + decode 1 µs (the simulation is of the link); playout from the start-up, and a refinement + that arrives after its piece's instant discarded (§8.7). +- **Outcome.** Per piece, at its first frame's presentation instant (the start-up plus its + start): the highest layer presented by then, or none where its base arrived later; its + base's arrival; the layers dropped or abandoned. A class that lists nothing of any piece + sends nothing. + +Under §8.5's priorities a refinement delays a base by at most one chunk and one decode +(staged-measurement §10.4). The publisher seals each track's group with a group receipt on +the track's chain (`wai_receipt_alias`, §8.4); a subscriber accepts a delivered object by +its Merkle proof, and holds a dropped one by inclusion alone, never as delivered +([jwp-receipts](jwp-receipts.md) §4.4). + ## 9. Package receipt **9.1** A version-2 package receipt binds the asset id; the policy digest; its parent @@ -829,6 +935,19 @@ that does not verify (`receipt-signature`). **9.2** Live windows chain by the parent link with parent kind `live-window`. A re-pack of an earlier package **MAY** name it with parent kind `re-pack`. +The reference live packer (`wai-pack live`, `wai_pack::live`) takes a source a frame at a +time, cuts it with the segmenter of §4.3, which decides a frame once `lookahead_frames` +more have arrived, and codes each piece as it closes with the coder the on-demand packer +uses (§5.4–§5.7). So a live and an on-demand pack of one source under one policy write +the same plan and the same bases and refinements, byte for byte. It writes no floor: a +supplied floor is made from the whole source (§7.3), so it is the on-demand package's. +Its pieces are gathered into windows: each a stage index of its pieces' bases and +refinements (`first_piece` and `first_entry` continuing the chain, `prev_index_digest` +the previous window's index digest, zero for the first) sealed by a version-2 receipt +whose parent is the previous window's (`live-window`; none for the first). A piece's +objects are published when the frame that decides its end has arrived: its base then, +and layer *k* `layer_lag_ms[k]` after it (§8.6, §8.9). + **9.3** A version-1 receipt (`wai.video.package`'s segment map) keeps its bytes and still verifies. Its JSON form is RFC 8785 canonical with `version` 1: `asset_id`, `energy_provenance` (`null` when unmetered) with its evidence, `grant`, `joules_micro`, @@ -931,15 +1050,20 @@ failed check names is not applied: class are the component's; its string table is the component's id, the class ids in policy order, then each capability at its first use; its class records are the policy's classes, in order, with their `max_layer`, floor-only flag and `max_kbps`; - every entry's role, capability and layer are a rung's of its component (a base, a - refinement at the rung's layer, or a floor); every piece's kind is one a video piece - has, and its rules, moments and class plan are §8.1's from its kind, span and + every piece's kind is one a video piece has; its rules and moments are §8.1's from + its kind and span; its entries are the rungs of its ladder (§5.7), by role, + capability and layer: its base, each refinement in layer order, each floor; each + refinement's deadline is its plan's (§8.6); and its class plan is §8.1's from its entries; - the plan file holds the index's starts and its digest is the header's (`plan-mismatch`); - every entry's object is present (`object-missing`) and keeps the object rules of §6.3, its counted bytes included; - - every decode-equivalent prefix decodes to its `prefix_digest` (`prefix-digest`); + - every decode-equivalent prefix decodes to its `prefix_digest` (`prefix-digest`): + each piece's base by its capability's decode (a still held for its piece, §5.7), and + each refinement applied in layer order by a staged-delivery §4 sink under the + reference tools' cost limit and chain budget; a layer that does not apply to every + frame of the stage below it is `prefix-digest` too; - when all of that holds, the floor: per piece, one floor object of the floor rung's capability whose renderer, settings, framing header, unit count and target are §7.2's (`floor-object-mismatch`) and which presents the piece's base @@ -957,15 +1081,17 @@ failed check names is not applied: catalog file past the ones given (`catalog-unexpected`); any other file (`unexpected-file`). -**11.4** *What this revision packs.* The reference packager packs one video component -with a `wai.video.int_motion` base (§5.5, §5.6) and a supplied AV1 floor (§7.3). It -refuses by name, before writing anything, what it does not pack yet: another medium -(`pack-media-unsupported`), a refinement rung (`pack-refinement-unsupported`), a rule's -ladder subset (`pack-rule-ladder-unsupported`), a floor to encode -(`pack-floor-encode-unsupported`), and a rung of a capability it does not encode -(`pack-capability-unsupported:`, every one naming the capability of the rung it -does not pack); and a component with no source (`pack-source-missing`) or whose source's -frame rate is not its timebase (`pack-source-rate`). Where its own work fails it +**11.4** *What this revision packs.* The reference packager packs one video component: +bases of `wai.video.int_motion` (§5.5, §5.6) and stills of `wai.image.png` (§5.7); +refinements of `wai.video.int_refine` over an `int_motion` base (§5.4); each piece by its +ladder, a rule's ladder subset included (§5.7); and a supplied AV1 floor (§7.3). Its live +packer packs the same pieces in windows, without a floor (§9.2). It refuses by name, +before writing anything, what it does not pack yet: another medium +(`pack-media-unsupported`), a floor to encode (`pack-floor-encode-unsupported`), and a rung +of a capability it does not encode, or a refinement over a base it does not refine +(`pack-capability-unsupported:`, every one naming the capability of the rung it does +not pack); and a component with no source (`pack-source-missing`) or whose source's frame +rate is not its timebase (`pack-source-rate`). Where its own work fails it refuses by name too, and writes nothing: an encode the encoder refuses, such as a piece past the samples it holds, or an object it wrote that does not count (`pack-encode`); a piece past `2^32 − 1` units (`pack-piece-length`); catalogs it cannot write, the case @@ -994,5 +1120,5 @@ would leave behind the ones it does not write. | `receipt-json`, `receipt-version`, `receipt-index-count`, `receipt-index-digest`, `receipt-stage-root`, `receipt-component`, `receipt-policy-digest`, `receipt-counts`, `receipt-parent-kind`, `receipt-energy`, `receipt-signature` | §9.1 | | `measurement-claim-missing`, `measurement-claim-invalid` | §9.4 | | `unexpected-file`, `listing-missing`, `listing-mismatch`, `policy-digest-mismatch`, `policy-json-mismatch`, `policy-component-mismatch`, `index-missing`, `index-policy-mismatch`, `plan-mismatch`, `object-missing`, `prefix-digest`, `catalog-mismatch`, `catalog-unexpected`, `object-unlisted` | §11.3 | -| `pack-id`, `pack-source-missing`, `pack-source-rate`, `pack-media-unsupported`, `pack-refinement-unsupported`, `pack-rule-ladder-unsupported`, `pack-floor-encode-unsupported`, `pack-capability-unsupported` (naming the capability) | §11.1, §11.4 | +| `pack-id`, `pack-source-missing`, `pack-source-rate`, `pack-media-unsupported`, `pack-floor-encode-unsupported`, `pack-capability-unsupported` (naming the capability) | §11.1, §11.4 | | `pack-encode`, `pack-piece-length`, `pack-catalog`, `pack-meter`, `pack-measurement-claim`, `pack-listing`, `pack-out-not-empty` | §11.4 | diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index 8b42af2be..666aeb31c 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -2,9 +2,10 @@ Vectors for [`extensions/packaging.md`](../extensions/packaging.md): the policy, its digest and what it may ask a packager to emit (§3), how a source is cut into pieces -(§4), and the integer video payloads a packager writes (§5.5). +(§4), the integer video payloads, refinement layers and stills a packager writes (§5), +packages (§6–§11), and their delivery over a simulated link (§8.9). -**Provenance.** Generated here: the `wai-pack` fixture generator (`wai-pack/src/fixture.rs`, `wai-pack/src/conformance.rs`) writes every frame from integer functions of the frame and pixel index, and a package's floor is an AV1 coding of those frames. No footage. +**Provenance.** Generated here: the `wai-pack` fixture generator (`wai-pack/src/fixture.rs`, `wai-pack/src/conformance.rs`) writes every frame from integer functions of the frame and pixel index; a package's bases, refinement layers and stills are codings of those frames, and its floor an AV1 coding of them by an encoder, as `supplied_floor.py` states. No footage. - `policy/` — policies a packager accepts (every rung one it may emit, §3.6), and `expected.json`: each policy's effective canonical JSON and @@ -47,6 +48,14 @@ digest and what it may ask a packager to emit (§3), how a source is cut into pi `video-base-01`: objects, the stage index, the floor's objects and CMAF files, the catalogs, the receipt and `LISTING`. The fixture's bumper plays twice, so its pieces are one object each time. +- `packs/video-staged-01/` — a staged golden package (§5.4, §5.7): a moving shot, the + bumper, a still shot, the bumper again and a second moving shot, under a policy of an + `int_motion` base with two `int_refine` layers, a still for a static piece (a rule's + ladder), three classes (one capped by rate, one floor only), a moment whose rule + moves the last piece's layers to an 800 ms deadline, and layer lags. Its floor was + written by `supplied_floor.py`. The bumper's base, layers and floor are one object + each; the still's piece is carried over HTTP by the floor (§8.3) and over MoQ on the + base track. - `mutations/` — stage indexes, objects and version-2 receipts, and `expected.json`: each case's reason (§6.2–§6.4, §9.1), a well-formed case of each kind among them, and three receipts carrying a signature only a verifier that checks the equation @@ -58,6 +67,16 @@ digest and what it may ask a packager to emit (§3), how a source is cut into pi `"signature": "refused"`. - `catalogs/` — stage indexes no golden package has (refinements on more than one deadline), with the MoQ catalog each gives (§8.4). +- `staged-mutations/` — the staged golden altered and re-sealed with its key, in the + layout of `package-mutations/`: a refinement on another deadline than its plan's, a + piece missing its top layer, a still where the policy's rule now gives the piece the + default ladder (each `index-policy-mismatch`), a top layer coded again at another step + and a still's digest of its one frame rather than its frame held for the piece (each + `prefix-digest`). +- `delivery/` — `expected.json`: the staged golden packed live and delivered to a `tv` + subscriber over a simulated link (§8.9), under §8.5's priorities, without them, and + capped at layer 1: each piece's presented layer, base arrival and dropped layers, and + the byte counts. Labelled simulated. - `package-mutations/` — packages a verifier must refuse (§11.3), each the golden altered and, where the receipt or `LISTING` alone would catch the alteration, re-sealed with the golden's key: what a dishonest packager holding the key could @@ -81,6 +100,8 @@ index and the pixel position, and the corpus pins its SHA-256: piece, cut where `max` forces it. - `video-base-01` — 64 × 36: three shots with a one-second bumper between them, the bumper the same frames both times (its span reads time from its own start). +- `video-staged-01` — 64 × 36: a moving shot, the bumper, a shot that does not change, + the bumper again, and a second moving shot. Files are compared as bytes; `.gitattributes` turns off line-ending conversion. @@ -116,10 +137,17 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. and sequence header reader, the CMAF files, and every catalog: it verifies each golden package in §11.3's order, rebuilding the floor's files and re-rendering the catalogs byte for byte, gives every mutation its reason, checks the version-1 receipts, and - verifies every package mutation in the same order, refused with its reason. - From the regenerated fixture it re-derives what only the source decides: the pieces, - their rules, moments and class plan, each base's SSE and step, and the encoder's - choices; and it checks the supplied floor against the policy's listing and the plan. + verifies every package mutation and staged mutation in the same order, refused with + its reason. Every prefix is decoded: a still by its own PNG reader, and each refinement + layer by the `WIR1` decoder of `../staged-conformance/staged_verify.py` (written from + staged-delivery); and the MoQ catalog is checked by that file's moq-streaming-format + §4.1 check. From the regenerated fixture it re-derives what only the source decides: + the pieces, their rules, moments, ladders and class plan, each base's SSE and step and + the encoder's choices, each still's bytes, and each refinement layer's bytes with its + own rANS coder and table fit; and it checks the supplied floor against the policy's + listing and the plan. The delivery cases it re-derives from the fixture's cut and the + package's index by its own mapping onto the simulator of + `../staged-measure-conformance/sim_verify.py`. python3 pack-conformance/pack_verify.py pack-conformance @@ -138,6 +166,18 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. python3 pack-conformance/segment_diff.py 1 2000 +- `stage_diff.py` runs the staged packer differentially: refinement layers over random + frames and steps, stills of random pictures, piece plans under random policies (rules + with ladder subsets, maximum layers and deadlines, classes with rates, layer lags) and + deliveries of random publications over random links, each result of + `wai-pack stage-json` equal to `pack_verify.py`'s. + + python3 pack-conformance/stage_diff.py 1 3000 + +- `supplied_floor.py` is how a golden's supplied floor was written (an AV1 encoder over + each piece of the generated fixture, the policy's listing set to it); it is not a step + of verification, since an encoder's output is pinned by the listing, not re-derived. + - `source_diff.py` runs the source readers differentially: seeded Y4M and WAV mutations (dimensions and channel counts at and past their range, every format field, truncations, single-byte changes), each file's verdict from diff --git a/wai/pack-conformance/catalogs/expected.json b/wai/pack-conformance/catalogs/expected.json index 9be559248..52110366b 100644 --- a/wai/pack-conformance/catalogs/expected.json +++ b/wai/pack-conformance/catalogs/expected.json @@ -2,6 +2,7 @@ "cases": [ { "index": "catalogs/one-deadline.wsix", + "namespace": "catalogs", "layer_lag_ms": [ 0, 200, @@ -13,10 +14,11 @@ "w": 64, "h": 36 }, - "moq": "{\"tracks\":[{\"altGroup\":1,\"name\":\"v/l0\",\"packaging\":\"wai\",\"priority\":64,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":0,\"wai_receipts\":false},{\"depends\":[\"v/l0\"],\"name\":\"v/l1\",\"packaging\":\"wai\",\"priority\":96,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1300,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":1,\"wai_receipts\":false},{\"depends\":[\"v/l1\"],\"name\":\"v/l2\",\"packaging\":\"wai\",\"priority\":128,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1100,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":2,\"wai_receipts\":false},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64}],\"version\":1}\n" + "moq": "{\"tracks\":[{\"altGroup\":1,\"isLive\":false,\"name\":\"v/l0\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":6758122338894672,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l0/receipts\",\"wai_staged\":{\"layer\":0,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\"],\"isLive\":false,\"name\":\"v/l1\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1300,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":7122743273789469,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l1/receipts\",\"wai_staged\":{\"layer\":1,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\",\"v/l1\"],\"isLive\":false,\"name\":\"v/l2\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1100,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":5138816494470210,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l2/receipts\",\"wai_staged\":{\"layer\":2,\"lead_ms\":null,\"set\":\"v\"}},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"namespace\":\"catalogs\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64},{\"isLive\":false,\"name\":\"v/l0/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l1/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l2/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"}],\"version\":1}\n" }, { "index": "catalogs/layer-1-on-its-own-deadline.wsix", + "namespace": "catalogs", "layer_lag_ms": [ 0, 200, @@ -28,10 +30,11 @@ "w": 64, "h": 36 }, - "moq": "{\"tracks\":[{\"altGroup\":1,\"name\":\"v/l0\",\"packaging\":\"wai\",\"priority\":64,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":0,\"wai_receipts\":false},{\"depends\":[\"v/l0\"],\"name\":\"v/l1@800\",\"packaging\":\"wai\",\"priority\":96,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":600,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":1,\"wai_receipts\":false},{\"depends\":[\"v/l1@800\"],\"name\":\"v/l2\",\"packaging\":\"wai\",\"priority\":128,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1100,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":2,\"wai_receipts\":false},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64}],\"version\":1}\n" + "moq": "{\"tracks\":[{\"altGroup\":1,\"isLive\":false,\"name\":\"v/l0\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":6758122338894672,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l0/receipts\",\"wai_staged\":{\"layer\":0,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\"],\"isLive\":false,\"name\":\"v/l1@800\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":600,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":6300050926507138,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l1@800/receipts\",\"wai_staged\":{\"layer\":1,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\",\"v/l1@800\"],\"isLive\":false,\"name\":\"v/l2\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":1100,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":5138816494470210,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l2/receipts\",\"wai_staged\":{\"layer\":2,\"lead_ms\":null,\"set\":\"v\"}},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"namespace\":\"catalogs\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64},{\"isLive\":false,\"name\":\"v/l0/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l1@800/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l2/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"}],\"version\":1}\n" }, { "index": "catalogs/both-layers-on-800.wsix", + "namespace": "catalogs", "layer_lag_ms": [ 0, 200, @@ -43,7 +46,7 @@ "w": 64, "h": 36 }, - "moq": "{\"tracks\":[{\"altGroup\":1,\"name\":\"v/l0\",\"packaging\":\"wai\",\"priority\":64,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":0,\"wai_receipts\":false},{\"depends\":[\"v/l0\"],\"name\":\"v/l1@800\",\"packaging\":\"wai\",\"priority\":96,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":600,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":1,\"wai_receipts\":false},{\"depends\":[\"v/l1@800\"],\"name\":\"v/l2@800\",\"packaging\":\"wai\",\"priority\":128,\"renderGroup\":1,\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":400,\"wai_determinism\":\"decode-equivalence\",\"wai_layer\":2,\"wai_receipts\":false},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64}],\"version\":1}\n" + "moq": "{\"tracks\":[{\"altGroup\":1,\"isLive\":false,\"name\":\"v/l0\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_motion\"],\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":6758122338894672,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l0/receipts\",\"wai_staged\":{\"layer\":0,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\"],\"isLive\":false,\"name\":\"v/l1@800\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":600,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":6300050926507138,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l1@800/receipts\",\"wai_staged\":{\"layer\":1,\"lead_ms\":null,\"set\":\"v\"}},{\"depends\":[\"v/l0\",\"v/l1@800\"],\"isLive\":false,\"name\":\"v/l2@800\",\"namespace\":\"catalogs\",\"packaging\":\"wai\",\"renderGroup\":1,\"role\":\"video\",\"wai_capabilities\":[\"wai.video.int_refine\"],\"wai_delivery_timeout_ms\":400,\"wai_determinism\":\"decode-equivalence\",\"wai_receipt_alias\":2193082958864004,\"wai_receipts\":true,\"wai_receipts_track\":\"v/l2@800/receipts\",\"wai_staged\":{\"layer\":2,\"lead_ms\":null,\"set\":\"v\"}},{\"altGroup\":1,\"codec\":\"av01.0.00M.08\",\"height\":36,\"name\":\"v/floor\",\"namespace\":\"catalogs\",\"packaging\":\"cmaf\",\"renderGroup\":1,\"width\":64},{\"isLive\":false,\"name\":\"v/l0/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l1@800/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"},{\"isLive\":false,\"name\":\"v/l2@800/receipts\",\"namespace\":\"catalogs\",\"packaging\":\"wai-receipts\"}],\"version\":1}\n" } ] } diff --git a/wai/pack-conformance/delivery/expected.json b/wai/pack-conformance/delivery/expected.json new file mode 100644 index 000000000..59396b3b2 --- /dev/null +++ b/wai/pack-conformance/delivery/expected.json @@ -0,0 +1,321 @@ +{ + "pack": "video-staged-01", + "class": "tv", + "label": "simulated", + "cases": [ + { + "name": "strict", + "plan": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200, + "enc_us": 0, + "startup_us": 3200000, + "layer_cap": null, + "fifo": false + }, + "sim_plan": { + "plan": "wai-sim/1", + "mode": "live", + "link": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200 + }, + "sender": { + "schedule": "strict-priority", + "carriage": "subgroup", + "layer_cap": null, + "delivery_timeout_us": { + "base": null, + "refinement": null + }, + "vod_window_us": null + }, + "sink": { + "workers": 1, + "decode_order": "base-first", + "decode_us": { + "base": 1, + "refinement": 1 + }, + "playout_start_us": 3200000, + "late_refinement": "discard" + } + }, + "presented": [ + 0, + 1, + 0, + 1, + 0 + ], + "outcome": { + "label": "simulated", + "never_sent_capped": 0, + "pieces": [ + { + "piece": 0, + "due_us": 3200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 2890750, + "dropped": [ + 1 + ] + }, + { + "piece": 1, + "due_us": 5200000, + "presented": 1, + "floor_only": false, + "base_arrival_us": 3493200, + "dropped": [ + 2 + ] + }, + { + "piece": 2, + "due_us": 6200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 4864350, + "dropped": [] + }, + { + "piece": 3, + "due_us": 7800000, + "presented": 1, + "floor_only": false, + "base_arrival_us": 6082450, + "dropped": [ + 2 + ] + }, + { + "piece": 4, + "due_us": 8800000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 8750250, + "dropped": [ + 1, + 2 + ] + } + ], + "delivered": 139408, + "dropped": 275887, + "abandoned": 55200 + } + }, + { + "name": "fifo", + "plan": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200, + "enc_us": 0, + "startup_us": 3200000, + "layer_cap": null, + "fifo": true + }, + "sim_plan": { + "plan": "wai-sim/1", + "mode": "live", + "link": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200 + }, + "sender": { + "schedule": "fifo", + "carriage": "subgroup", + "layer_cap": null, + "delivery_timeout_us": { + "base": null, + "refinement": null + }, + "vod_window_us": null + }, + "sink": { + "workers": 1, + "decode_order": "base-first", + "decode_us": { + "base": 1, + "refinement": 1 + }, + "playout_start_us": 3200000, + "late_refinement": "discard" + } + }, + "presented": [ + 0, + 0, + 0, + 1, + 0 + ], + "outcome": { + "label": "simulated", + "never_sent_capped": 0, + "pieces": [ + { + "piece": 0, + "due_us": 3200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 2890750, + "dropped": [ + 1 + ] + }, + { + "piece": 1, + "due_us": 5200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 4003200, + "dropped": [ + 1, + 2 + ] + }, + { + "piece": 2, + "due_us": 6200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 4864350, + "dropped": [] + }, + { + "piece": 3, + "due_us": 7800000, + "presented": 1, + "floor_only": false, + "base_arrival_us": 6082450, + "dropped": [ + 2 + ] + }, + { + "piece": 4, + "due_us": 8800000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 8750250, + "dropped": [ + 1, + 2 + ] + } + ], + "delivered": 108709, + "dropped": 275386, + "abandoned": 86400 + } + }, + { + "name": "capped", + "plan": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200, + "enc_us": 0, + "startup_us": 3200000, + "layer_cap": 1, + "fifo": false + }, + "sim_plan": { + "plan": "wai-sim/1", + "mode": "live", + "link": { + "rtt_us": 0, + "bytes_per_ms": 40, + "chunk_bytes": 1200 + }, + "sender": { + "schedule": "strict-priority", + "carriage": "subgroup", + "layer_cap": 1, + "delivery_timeout_us": { + "base": null, + "refinement": null + }, + "vod_window_us": null + }, + "sink": { + "workers": 1, + "decode_order": "base-first", + "decode_us": { + "base": 1, + "refinement": 1 + }, + "playout_start_us": 3200000, + "late_refinement": "discard" + } + }, + "presented": [ + 0, + 1, + 0, + 1, + 0 + ], + "outcome": { + "label": "simulated", + "never_sent_capped": 181901, + "pieces": [ + { + "piece": 0, + "due_us": 3200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 2890750, + "dropped": [ + 1 + ] + }, + { + "piece": 1, + "due_us": 5200000, + "presented": 1, + "floor_only": false, + "base_arrival_us": 3493200, + "dropped": [] + }, + { + "piece": 2, + "due_us": 6200000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 4864350, + "dropped": [] + }, + { + "piece": 3, + "due_us": 7800000, + "presented": 1, + "floor_only": false, + "base_arrival_us": 6082450, + "dropped": [] + }, + { + "piece": 4, + "due_us": 8800000, + "presented": 0, + "floor_only": false, + "base_arrival_us": 8750250, + "dropped": [ + 1 + ] + } + ], + "delivered": 139408, + "dropped": 122786, + "abandoned": 26400 + } + } + ] +} diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index 9db9b0dbf..dcc586843 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -66,16 +66,22 @@ import json import os import re import shutil +import struct import sys import tempfile import tomllib +import zlib HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(HERE, "..", "claim-conformance")) sys.path.insert(0, os.path.join(HERE, "..", "int-payload-conformance")) +sys.path.insert(0, os.path.join(HERE, "..", "staged-conformance")) +sys.path.insert(0, os.path.join(HERE, "..", "staged-measure-conformance")) from _blake3 import blake3 # noqa: E402 import _ed25519 as ED # noqa: E402 import int_payload_verify as IPV # noqa: E402 +import staged_verify as SV # noqa: E402 +import sim_verify as SIM # noqa: E402 MAX_INTEGER = (1 << 53) - 1 U32 = (1 << 32) - 1 @@ -504,9 +510,14 @@ def constrain(P, lag): no("policy-class-layer") for r in P["rule"]: if "ladder" in r["then"]: - rs = [by.get(i) for i in r["then"]["ladder"]] + ids = r["then"]["ladder"] + rs = [by.get(i) for i in ids] + # One component, each rung once, exactly one base, every refinement over a + # rung it names, and every floor rung of its component (§3.4 item 10). ok = rs and all(rs) and all(x["component"] == rs[0]["component"] for x in rs) \ - and any(x["role"] == "base" for x in rs) + and len(set(ids)) == len(ids) and sum(x["role"] == "base" for x in rs) == 1 \ + and all(x.get("refines", ids[0]) in ids for x in rs) \ + and all(f["id"] in ids for f in P["ladder"] if f["component"] == rs[0]["component"] and f["role"] == "floor") if not ok: no("policy-rule-ladder") lag = [0] * n if lag is None else lag @@ -819,6 +830,13 @@ def fixture_spans(name): (50, ramp(30, 220, 2, 1, 1, True, 12, 150, 120), None), (25, shot, None, LOCAL), (40, ramp(16, 160, 1, 2, 1, True, 13, 128, 128), None)] + if name == "video-staged-01": + shot = blocks([40, 120, 200], 8, 6, 1, 100, 160) + return 64, 36, [(50, ramp(20, 200, 1, 1, 1, True, 21, 110, 140), None), + (25, shot, None, LOCAL), + (40, ramp(60, 180, 2, 1, 0, False, 0, 140, 116), None), + (25, shot, None, LOCAL), + (60, ramp(30, 120, 1, 2, 1, True, 22, 150, 120), None)] if name == "cuts-01": a = ramp(20, 100, 1, 1, 1, True, 1, 110, 140) b = ramp(120, 220, 2, 1, 1, True, 2, 150, 120) @@ -1594,6 +1612,259 @@ def object_rules(ix, i, data): return m +# ---------------------------------------------------------------- stages (§5.4, §5.7) + +RANS_L = 1 << 31 + + +def rans_encode(values, cdf, offset): + """integer-payloads §2's coder, written from the text: each value's symbols (its table + symbol, and past the table's span the bypass escape: a count of 4-bit groups, itself + in groups of at most 15, then the groups low first), coded last first into a 64-bit + state that emits its low 32 bits while too large; the stream is the final state, low + word first, then the emitted words in the order a decoder reads them.""" + n = len(cdf) + top = n - 2 + syms = [] + for value in values: + v = value - offset + sym, raw = (top, -2 * v - 1) if v < 0 else (top, 2 * (v - top)) if v >= top else (v, None) + syms.append((cdf[sym], cdf[sym + 1] - cdf[sym])) + if raw is not None: + groups = 0 + while raw >> (4 * groups): + groups += 1 + count = groups + while count >= 15: + syms.append((None, 15)) + count -= 15 + syms.append((None, count)) + syms += [(None, (raw >> (4 * j)) & 15) for j in range(groups)] + x, words = RANS_L, [] + for start, f in reversed(syms): + freq = f if start is not None else 1 << 12 + x_max = ((RANS_L >> 16) << 32) * freq + if start is not None: + while x >= x_max: + words.append(x & M32) + x >>= 32 + x = ((x // freq) << 16) + x % freq + start + else: + if x >= x_max: + words.append(x & M32) + x >>= 32 + x = (x << 4) | f + return struct.pack("> 32) + b"".join(struct.pack("= 8: + self.out.append(self.acc & 255) + self.acc >>= 8 + self.n -= 8 + + def code(self, c, n): + self.put(int(format(c, f"0{n}b")[::-1], 2) if n else 0, n) + + def lit(self, s): + """RFC 1951 §3.2.6's fixed literal/length code.""" + if s <= 143: + self.code(0x30 + s, 8) + elif s <= 255: + self.code(0x190 + s - 144, 9) + elif s <= 279: + self.code(s - 256, 7) + else: + self.code(0xC0 + s - 280, 8) + + def finish(self): + return bytes(self.out + (bytes([self.acc]) if self.n else b"")) + + +def deflate_fixed(d): + """§5.7's deflate: one final block of the fixed codes; at each position with three + bytes left, the latest earlier position within 32768 bytes that starts with the same + three bytes is the match, as long as the two runs agree (at most 258); else a literal; + every position a symbol covers with three bytes left becomes its three bytes' latest.""" + b = DeflateBits() + b.put(1, 1) + b.put(1, 2) + last, n, i = {}, len(d), 0 + while i < n: + j = last.get(d[i:i + 3]) if i + 3 <= n else None + if j is not None and i - j <= 32768: + m = 0 + while m < min(258, n - i) and d[j + m] == d[i + m]: + m += 1 + li = max(k for k, x in enumerate(LEN_BASE) if x <= m) + b.lit(257 + li) + b.put(m - LEN_BASE[li], LEN_EXTRA[li]) + di = max(k for k, x in enumerate(DIST_BASE) if x <= i - j) + b.code(di, 5) + b.put(i - j - DIST_BASE[di], DIST_EXTRA[di]) + else: + b.lit(d[i]) + m = 1 + for q in range(i, i + m): + if q + 3 <= n: + last[d[q:q + 3]] = q + i += m + b.lit(256) + return b.finish() + + +def png_still(rgb, w, h): + """§5.7: a frame as the still the reference packager writes: IHDR (8-bit RGB), one IDAT + of a zlib stream (0x78 0x01, the deflate above over the rows each filtered Sub, the + Adler-32), IEND.""" + stride = 3 * w + raw = bytearray() + for y in range(h): + row = rgb[y * stride:(y + 1) * stride] + raw.append(1) + raw += bytes((row[i] - (row[i - 3] if i >= 3 else 0)) & 255 for i in range(stride)) + z = b"\x78\x01" + deflate_fixed(bytes(raw)) + struct.pack(">I", zlib.adler32(bytes(raw))) + + def chunk(kind, data): + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data)) + return b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0)) + chunk(b"IDAT", z) + chunk(b"IEND", b"") + + +def png_frame(b): + """A still's frame, by staged_verify.py's PNG reader (zlib, the five filters): its + width, height and RGB8 bytes.""" + (h, w, _), out = SV.decode_png(b) + return w, h, bytes(out) + + +# ---------------------------------------------------------------- delivery (§8.9) + + +def deliver(P, row, published, plan): + """§8.9: a live packer's publications delivered to a subscriber whose class plan is + `row`, simulated by staged-measurement Appendix B's simulator (sim_verify.py's, written + from that text): the objects the class lists, on the frames as units, each published + at its frame's instant plus the encoder's delay plus its layer's lag, a refinement with + its track's timeout (deadline − lag); live, strict priority (§8.5) or fifo, a subgroup + each, the subscriber's cap, one worker, decodes of 1 µs, playout from the start-up, a + late refinement discarded. Per piece, at its first frame's instant: the highest layer + presented, its base's arrival and the layers dropped.""" + comp = P["component"][0] + num, den = comp["timebase"]["num"], comp["timebase"]["den"] + if 1_000_000 * den % num: + raise ValueError("a frame that is not a whole number of microseconds") + frame_us = 1_000_000 * den // num + lag = P["live"]["layer_lag_ms"] + objects = [] + for x in published: + listed = row[x["piece"]] + if listed == 0xFF or x["layer"] > listed: + continue + lg = lag[x["layer"]] if x["layer"] < len(lag) else 0 + o = {"id": f"p{x['piece']}/l{x['layer']}", "base": x["piece"], "layer": x["layer"], + "units": [x["start"], x["duration"]], "bytes": x["bytes"], + "capture_us": x["at_frame"] * frame_us + plan["enc_us"] + lg * 1000} + if x["layer"]: + o["timeout_us"] = max(0, x["deadline_ms"] - lg) * 1000 + objects.append(o) + m = {"unit_us": frame_us, "objects": objects} + sp = {"plan": "wai-sim/1", "mode": "live", + "link": {"rtt_us": plan["rtt_us"], "bytes_per_ms": plan["bytes_per_ms"], "chunk_bytes": plan["chunk_bytes"]}, + "sender": {"schedule": "fifo" if plan.get("fifo") else "strict-priority", "carriage": "subgroup", + "layer_cap": plan.get("layer_cap"), "delivery_timeout_us": {"base": None, "refinement": None}, + "vod_window_us": None}, + "sink": {"workers": 1, "decode_order": "base-first", "decode_us": {"base": 1, "refinement": 1}, + "playout_start_us": plan["startup_us"], "late_refinement": "discard"}} + # A class that lists nothing of any piece sends nothing. + r = SIM.simulate(m, sp) if objects else {"objects": [], "presentations": [], "delivered": 0, "dropped": 0, + "abandoned": 0, "never_sent_capped": 0} + pieces = {} + for x in published: + pieces.setdefault(x["piece"], {"piece": x["piece"], "due_us": plan["startup_us"] + x["start"] * frame_us, + "presented": None, "floor_only": row[x["piece"]] == 0xFF, + "base_arrival_us": None, "dropped": [], "_start": x["start"]}) + for o, ev in zip(objects, r["objects"]): + pc = pieces[o["base"]] + if o["layer"] == 0: + pc["base_arrival_us"] = ev["arrived_us"] + if ev["state"] in ("dropped", "abandoned"): + pc["dropped"].append(o["layer"]) + for pc in pieces.values(): + at = [l for u, l, tt in r["presentations"] if u == pc["_start"] and tt <= pc["due_us"]] + if 1 in at: + pc["presented"] = max(at) - 1 + del pc["_start"] + return {"label": "simulated", "pieces": [pieces[k] for k in sorted(pieces)], "delivered": r["delivered"], + "dropped": r["dropped"], "abandoned": r["abandoned"], "never_sent_capped": r["never_sent_capped"]} + + +def published_of(P, ix, n_frames): + """What a live packer publishes for the package whose index is `ix` (§9.2, §4.3): each + piece's bases and refinements, published once the frame that decides the piece's end + has arrived — the frame lookahead_frames past the next piece's start, or the source's + last.""" + L = P["segmentation"]["lookahead_frames"] + pcs, E = ix["pieces"], ix["entries"] + out = [] + for k, pc in enumerate(pcs): + at = min(pcs[k + 1]["start"] + L, n_frames - 1) if k + 1 < len(pcs) else n_frames - 1 + for e in E[pc["first_entry"]:pc["first_entry"] + pc["n_entries"]]: + if e["role"] in (BASE, REFINEMENT): + out.append({"piece": ix["first_piece"] + k, "start": pc["start"], "duration": pc["duration"], "at_frame": at, + "layer": e["layer"], "deadline_ms": e["deadline"], "bytes": e["byte_len"]}) + return out + + # ---------------------------------------------------------------- package receipts (§9) @@ -2111,25 +2382,58 @@ def listed_bytes(ix, k, top): if e["role"] in (BASE, REFINEMENT) and e["layer"] <= top) +def piece_then(P, ix, k): + """§8.1: piece k's matching rules and its moments as bits, and the rules' `then` + folded in document order (a later member overrides an earlier one).""" + num, den = ix["tb"] + pc = ix["pieces"][k] + s_ms, e_ms = pc["start"] * den * 1000 // num, (pc["start"] + pc["duration"]) * den * 1000 // num + moments = [i for i, m in enumerate(P["moment"]) if s_ms < m["to_ms"] and e_ms > m["from_ms"]] + tags = {t for i in moments for t in P["moment"][i]["tags"]} + rules, then = [], {} + for i, r in enumerate(P["rule"]): + w = r["when"] + if "piece_kind" in w and PIECE_KINDS[pc["kind"]] not in w["piece_kind"]: + continue + if "moment_tag" in w and not tags & set(w["moment_tag"]): + continue + rules.append(i) + then.update(r["then"]) + return sum(1 << i for i in rules), sum(1 << i for i in moments), then + + +def piece_ladder(P, cid, then): + """§5.7: the rungs a piece of component cid is coded in, (base, [refinements in layer + order], [floors]): the rules' `then.ladder` where it names rungs of cid, else the + component's first base rung in ladder order, the refinements over it and its floors.""" + mine = [r for r in P["ladder"] if r["component"] == cid] + ids = then.get("ladder") + if ids is not None and all(any(r["id"] == i for r in mine) for i in ids): + mine = [r for r in mine if r["id"] in ids] + base = next(r for r in mine if r["role"] == "base") + chain, cur = [], base + while True: + nxt = next((r for r in mine if r["role"] == "refinement" and r.get("refines") == cur["id"]), None) + if nxt is None: + break + chain.append(nxt) + cur = nxt + return base, chain, [r for r in mine if r["role"] == "floor"] + + +def default_base_cap(P, cid): + """§5.7: the capability of a component's first base rung, which its HTTP views carry.""" + return next((r["capability"] for r in P["ladder"] if r["component"] == cid and r["role"] == "base"), "") + + def class_plan(P, ix): """§8.1: each piece's moments and matching rules as bits, and per class the highest layer it lists for each piece, 0xFF for the floor only.""" num, den = ix["tb"] bits, rows = [], [[] for _ in P["class"]] for k, pc in enumerate(ix["pieces"]): - s_ms, e_ms = pc["start"] * den * 1000 // num, (pc["start"] + pc["duration"]) * den * 1000 // num - moments = [i for i, m in enumerate(P["moment"]) if s_ms < m["to_ms"] and e_ms > m["from_ms"]] - tags = {t for i in moments for t in P["moment"][i]["tags"]} - rules, then = [], {} - for i, r in enumerate(P["rule"]): - w = r["when"] - if "piece_kind" in w and PIECE_KINDS[pc["kind"]] not in w["piece_kind"]: - continue - if "moment_tag" in w and not tags & set(w["moment_tag"]): - continue - rules.append(i) - then.update(r["then"]) - bits.append((sum(1 << i for i in rules), sum(1 << i for i in moments))) + rb, mb, then = piece_then(P, ix, k) + bits.append((rb, mb)) for row, cl in zip(rows, P["class"]): if cl["floor_only"]: row.append(0xFF) @@ -2153,26 +2457,37 @@ def listed(ix, k, top): def codecs_value(objects): - """http-delivery §4.2 over a sequence of WAI1 objects (entries): the union, an alt per - distinct capability of the objects that are not layers, a layer's capability after `+` - in its base's alt; and the floor, the alts every such object has.""" - alts, joined = [], {} - for e in objects: - if e["role"] != REFINEMENT and e["cap"] not in alts: - alts.append(e["cap"]) - bases = [e for e in objects if e["role"] != REFINEMENT] + """http-delivery §4.2 over a sequence of WAI1 objects (entries, in order; an object + listed twice is one object): each object that is not a layer is an element of one alt, + its capability; a layer's capability follows `+` in the alt of the object its base is. + The union: every alt by first appearance, with every capability joined to it in any + object; the floor: the alts every such object has, each with the capabilities joined + to it in every one.""" + seen, seq = set(), [] for e in objects: + if e["hash"] not in seen: + seen.add(e["hash"]) + seq.append(e) + els = [[e["cap"], []] if e["role"] != REFINEMENT else None for e in seq] + for e in seq: if e["role"] == REFINEMENT: - base_cap = e.get("base_cap") - joined.setdefault(base_cap, []) - if e["cap"] not in joined[base_cap]: - joined[base_cap].append(e["cap"]) + k = next(k for k, b in enumerate(seq) if b["role"] != REFINEMENT and b["hash"] == e["base_hash"]) + if e["cap"] not in els[k][1]: + els[k][1].append(e["cap"]) + els = [x for x in els if x is not None] + union = [] + for cap, plus in els: + u = next((a for a in union if a[0] == cap), None) + if u is None: + union.append([cap, list(plus)]) + else: + u[1] += [p for p in plus if p not in u[1]] + floor = [[cap, [p for p in plus if all(p in x[1] for x in els if x[0] == cap)]] for cap, plus in union + if all(x[0] == cap for x in els)] - def alt(c): - return "+".join(x[len("wai."):] for x in [c] + joined.get(c, [])) - union = "wai1." + "|".join(alt(c) for c in alts) - floor = "wai1." + "|".join(alt(c) for c in alts if all(b["cap"] == c for b in bases)) - return union, floor + def alt(cap, plus): + return "+".join(x[len("wai."):] for x in [cap] + plus) + return "wai1." + "|".join(alt(*a) for a in union), "wai1." + "|".join(alt(*a) for a in floor) def render_catalogs(P, ix, c, floor, size, policy_hex): @@ -2224,13 +2539,15 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): lines += [f' ' for name, _ in frags] out["dash/classic.mpd"] = text(lines + [" ", " "] + mpd_tail) E = ix["entries"] - by_hash = {} - for e in E: - by_hash.setdefault(e["hash"], e) + # §8.3: an HTTP view lists a piece only where its base is of the component's default + # base capability (http-delivery §4.2: no variant mixes bases with nothing in common). + base_cap = default_base_cap(P, c) + bases = [next(e for e in E[pc["first_entry"]:pc["first_entry"] + pc["n_entries"]] if e["role"] == BASE) for pc in pieces] for ci, cl in enumerate(P["class"]): name, row = cl["id"], plan[ci] lists = [listed(ix, k, row[k]) for k in range(len(pieces))] - wai = any(lists) + http = [lists[k] if bases[k]["cap"] == base_cap else [] for k in range(len(pieces))] + wai = any(http) lines = head[:] if wai: lines.append('#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8"') @@ -2248,7 +2565,7 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): v["stages"].append(s) views.append(v) comp = {"pieces": views} - if any(lists[k] for k in range(len(pieces))): + if wai: comp["media_playlist"] = f"../hls/{name}/wai-{c}.m3u8" view = {"class": name, "components": {c: comp}, "policy_digest": policy_hex} out[f"stages/{name}.json"] = (jcs(view) + "\n").encode() @@ -2256,12 +2573,11 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): continue objs = [] for k in range(len(pieces)): - base = next((e for e in lists[k] if e["role"] == BASE), None) - for e in lists[k]: - objs.append(dict(e, base_cap=base and base["cap"])) + for e in http[k]: + objs.append(dict(e, base_hash=bases[k]["hash"])) union, fl = codecs_value(objs) - wb, wa = rates([(sum(e["byte_len"] for e in lists[k]), pieces[k]["duration"]) - for k in range(len(pieces)) if lists[k]]) + wb, wa = rates([(sum(e["byte_len"] for e in http[k]), pieces[k]["duration"]) + for k in range(len(pieces)) if http[k]]) out[f"hls/{name}/wai.m3u8"] = text(head + [ f'#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="{name}"', f'#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/{name}.json"', @@ -2271,13 +2587,13 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): for k, pc in enumerate(pieces): p0 = pc["first_entry"] base = next(e for e in E[p0:p0 + pc["n_entries"]] if e["role"] == BASE) - if row[k] == 0xFF: + if not http[k]: lines.append("#EXT-X-GAP") # A layer line follows its segment's EXTINF, before the segment's URI # (http-delivery §10.4). lines.append(f"#EXTINF:{ms_text(pc['duration'], num, den)},") lines += [f'#WAI-LAYER:N={e["layer"]},URI="../../objects/{e["hash"].hex()}.wai#n={e["byte_len"]}"' - for e in lists[k] if e["role"] == REFINEMENT] + for e in http[k] if e["role"] == REFINEMENT] lines.append(f"../../objects/{base['hash'].hex()}.wai#n={base['byte_len']}") out[f"hls/{name}/wai-{c}.m3u8"] = text(lines + ["#EXT-X-ENDLIST"]) lines = mpd_head + [ @@ -2288,9 +2604,9 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): f' ', ' '] - for layer in range(max(v for v in row if v != 0xFF) + 1): - ks = [k for k in range(len(pieces)) if any(e["layer"] == layer for e in lists[k])] - mine = [[e for e in lists[k] if e["layer"] == layer] for k in ks] + for layer in range(max(row[k] for k in range(len(pieces)) if http[k]) + 1): + ks = [k for k in range(len(pieces)) if any(e["layer"] == layer for e in http[k])] + mine = [[e for e in http[k] if e["layer"] == layer] for k in ks] peak = max((bps(sum(e["byte_len"] for e in m), pieces[k]["duration"], num, den) for k, m in zip(ks, mine)), default=0) dep = f' dependencyId="{c}-l{layer - 1}"' if layer else "" @@ -2301,15 +2617,24 @@ def render_catalogs(P, ix, c, floor, size, policy_hex): out[f"dash/{name}.mpd"] = text(lines + mpd_tail) has_floor = any(e["role"] == FLOOR for e in E) out["moq/catalog.json"] = render_moq(c, E, P["live"]["layer_lag_ms"], P["defaults"]["deadline_ms"], - (F, FW, FH) if has_floor else None) + (F, FW, FH) if has_floor else None, P["asset_id"]) return out -def render_moq(c, E, lag, default_deadline, floor): - """§8.4: moq/catalog.json. One track per (layer, deadline), by layer, then by deadline - ascending; a refinement track depends on every track that carries an entry its entries - refine, in track order, so it never names a track the catalog does not hold. floor: - (codecs, width, height) or None.""" +def receipt_alias(ns, name): + """§8.4: a track's wai_receipt_alias, the first eight bytes of + BLAKE3("wai:pack-receipt-alias\x01" ‖ namespace ‖ 0x00 ‖ name), little-endian, below 2^53.""" + return int.from_bytes(blake3(b"wai:pack-receipt-alias\x01" + ns.encode() + b"\x00" + name.encode())[:8], "little") & MAX_INTEGER + + +def render_moq(c, E, lag, default_deadline, floor, ns): + """§8.4: moq/catalog.json, the component's bases and refinements as a stage set of + moq-streaming-format §4.1. One track per (layer, deadline), by layer, then deadline + ascending, of namespace `ns`; each depends on every track carrying an entry below one + of its own down its chain; each with wai_staged, receipts declared with its alias and a + receipts track of its own; then the floor (cmaf, altGroup 1 with the base track), then + the receipts tracks. floor: (codecs, width, height) or None. The catalog must pass + §4.1's check (staged_verify.py's).""" keys = sorted({(e["layer"], e["deadline"]) for e in E if e["role"] in (BASE, REFINEMENT)}) def name(key): @@ -2322,27 +2647,40 @@ def render_moq(c, E, lag, default_deadline, floor): for e in es: if e["cap"] not in caps: caps.append(e["cap"]) - t = {"name": name((layer, deadline)), "packaging": "wai", "renderGroup": 1, - "priority": 64 + 32 * layer, "wai_capabilities": caps, "wai_layer": layer, "wai_receipts": False} - if min(e["tier"] for e in es): - t["wai_determinism"] = TIER_NAMES[min(e["tier"] for e in es)] - if layer == 0 and floor is not None: - t["altGroup"] = 1 - if layer: - deps = set() - for e in es: - below = E[e["refines"]] - dt = keys.index((below["layer"], below["deadline"])) + deps = set() + for e in es: + cur = e + while cur["role"] == REFINEMENT: + cur = E[cur["refines"]] + dt = keys.index((cur["layer"], cur["deadline"])) if dt >= ti: raise ValueError("a refinement refines an entry on no earlier track") deps.add(dt) + nm = name((layer, deadline)) + t = {"namespace": ns, "name": nm, "packaging": "wai", "isLive": False, "role": "video", "renderGroup": 1, + "wai_capabilities": caps, "wai_receipts": True, "wai_receipt_alias": receipt_alias(ns, nm), + "wai_receipts_track": f"{nm}/receipts", "wai_staged": {"set": c, "layer": layer, "lead_ms": None}} + if deps: t["depends"] = [name(keys[x]) for x in sorted(deps)] + weakest = min(e["tier"] for e in es) + if weakest: + t["wai_determinism"] = TIER_NAMES[weakest] + if layer: t["wai_delivery_timeout_ms"] = max(0, deadline - (lag[layer] if layer < len(lag) else 0)) + elif floor is not None: + t["altGroup"] = 1 tracks.append(t) if floor is not None: - tracks.append({"altGroup": 1, "codec": floor[0], "height": floor[2], "name": f"{c}/floor", "packaging": "cmaf", - "renderGroup": 1, "width": floor[1]}) - return (jcs({"tracks": tracks, "version": 1}) + "\n").encode() + tracks.append({"altGroup": 1, "codec": floor[0], "height": floor[2], "name": f"{c}/floor", "namespace": ns, + "packaging": "cmaf", "renderGroup": 1, "width": floor[1]}) + tracks += [{"namespace": ns, "name": f"{name(k)}/receipts", "packaging": "wai-receipts", "isLive": False} for k in keys] + if len({receipt_alias(ns, name(k)) for k in keys}) != len(keys): + raise ValueError("two tracks' receipt aliases are equal") + cat = {"tracks": tracks, "version": 1} + bad = [s for s in SV.check_catalog(cat, ns) if s["reason"] is not None] + if bad: + raise ValueError(f"the stage set fails moq-streaming-format §4.1: {bad}") + return (jcs(cat) + "\n").encode() # ---------------------------------------------------------------- the corpus @@ -2562,16 +2900,16 @@ def package_verdict(d, name, pkg): return m.group(1) if m else f"no reason named: {cap.failed[0]}" -def verify_package_mutations(rep, d): +def verify_package_mutations(rep, d, sub="package-mutations"): """package-mutations/: each case the golden less the files it removes (and the directories that leaves with nothing in them), with its overlay and the directories it adds that hold nothing (`empty_dirs`) or cannot be read (`unreadable_dirs`, mode 0 where the platform has modes), written out and verified as a package (§11.3); refused with the corpus's reason, or accepted where it says `ok`.""" - m = os.path.join(d, "package-mutations") + m = os.path.join(d, sub) held = tree(m) if held is None: - rep.check(False, "package-mutations/: a symbolic link or an entry that is neither file nor directory") + rep.check(False, f"{sub}/: a symbolic link or an entry that is neither file nor directory") return pm = json.loads(load(os.path.join(m, "expected.json"))) golden = os.path.join(d, "packs", pm["pack"], "package") @@ -2598,9 +2936,9 @@ def verify_package_mutations(rep, d): got = package_verdict(d, pm["pack"], pkg) for p in c.get("unreadable_dirs", []): os.chmod(os.path.join(pkg, p), 0o755) - rep.check(got == c["reason"], f"package-mutations {c['name']}: {got}, the corpus says {c['reason']}") + rep.check(got == c["reason"], f"{sub} {c['name']}: {got}, the corpus says {c['reason']}") rest = [p for p in held if p not in used] - rep.check(not rest, f"package-mutations/: files no case uses: {rest}") + rep.check(not rest, f"{sub}/: files no case uses: {rest}") def unreached(files, c): @@ -2626,17 +2964,25 @@ def index_against_policy(P, comp, c, ix): want = [(x["id"], min(x["max_layer"], 255), int(x["floor_only"]), min(x.get("max_kbps", 0), U32)) for x in P["class"]] if [(x["name"], x["max_layer"], x["flags"], x["max_kbps"]) for x in ix["classes"]] != want: return "the class records" - lay = layers(P) - for i, e in enumerate(ix["entries"]): - role = ROLES[e["role"]] - if not any(r["component"] == c["id"] and r["role"] == role and r["capability"] == e["cap"] and lay[r["id"]] == e["layer"] - for r in P["ladder"]): - return f"entry {i}: no rung of the component" - if any(pc["kind"] >= len(KINDS) for pc in ix["pieces"]): - return "a piece kind a video piece does not have" - bits, rows = class_plan(P, ix) - if bits != [(pc["rules"], pc["moments"]) for pc in ix["pieces"]]: - return "the rules and moments" + # Each piece: its kind, its rules and moments, its entries the rungs of its ladder + # (§5.7) with the plan's deadline on each refinement (§8.6), then the class plan. + E = ix["entries"] + for k, pc in enumerate(ix["pieces"]): + if pc["kind"] >= len(KINDS): + return f"piece {k}: a kind a video piece does not have" + rb, mb, then = piece_then(P, ix, k) + if (rb, mb) != (pc["rules"], pc["moments"]): + return f"piece {k}: the rules and moments" + base, chain, floors = piece_ladder(P, c["id"], then) + want = [(BASE, base["capability"], 0)] + [(REFINEMENT, r["capability"], j + 1) for j, r in enumerate(chain)] \ + + [(FLOOR, r["capability"], 0) for r in floors] + mine = E[pc["first_entry"]:pc["first_entry"] + pc["n_entries"]] + if [(e["role"], e["cap"], e["layer"]) for e in mine] != want: + return f"piece {k}: its entries are not the rungs of its ladder" + deadline = then.get("deadline_ms", P["defaults"]["deadline_ms"]) + if any(e["role"] == REFINEMENT and e["deadline"] != deadline for e in mine): + return f"piece {k}: a refinement's deadline is not its plan's" + _, rows = class_plan(P, ix) if rows != ix["plan"]: return "the class plan" return None @@ -2686,32 +3032,52 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): if len(mans) != len(E): return named | unreached(files, c['id']) size = None + own = owners(ix) + stage = {} # entry index → the frames of the prefix it completes, decoded for i, e in enumerate(E): - if e["role"] != BASE: - continue ml = e["man_len"] - try: - w = IPV.parse_wiv1(objs[i][12 + ml:], 0) - if e["hash"] not in decoded: - decoded[e["hash"]] = (w, IPV.decode_wiv1_k0(w)) - except IPV.Refused as x: - rep.check(False, f"{t}: entry {i}: the WIV1 payload is refused ({x})") - return named | unreached(files, c['id']) - size = size or (w["w"], w["h"]) - # §5.2: the reference packager's base manifest, as SPEC §3 orders it. - want = {"wai": "1.0", "media": comp["media"], "intent": "replicate", - "model_requirement": {"capability": e["cap"]}, "conditioning": {"kind": e["cap"].split(".")[-1]}, - "target": {"frames": pieces[owners(ix)[i]]["duration"], "h": w["h"], "w": w["w"]}} - rep.check(objs[i][8:8 + ml] == json.dumps(want, separators=(",", ":")).encode(), - f"{t}: entry {i}: the base's manifest is §5.2's") + payload = objs[i][12 + ml:] + k = own[i] + n = pieces[k]["duration"] + if e["role"] == BASE: + try: + if e["cap"] == "wai.image.png": + # §5.7: a still, its one frame held for the piece. + pw, ph, rgb = png_frame(payload) + wv, frames = None, [rgb] * n + else: + wv = IPV.parse_wiv1(payload, 0) + frames, pw, ph = IPV.decode_wiv1_k0(wv), wv["w"], wv["h"] + except (IPV.Refused, ValueError, TypeError, IndexError, struct.error, zlib.error) as x: + rep.check(False, f"{t}: entry {i}: the base's payload is refused ({x})") + return named | unreached(files, c['id']) + decoded.setdefault(e["hash"], (wv, frames)) + stage[i] = frames + size = size or (pw, ph) + # §5.2: the reference packager's base manifest, as SPEC §3 orders it. + want = {"wai": "1.0", "media": comp["media"], "intent": "replicate", + "model_requirement": {"capability": e["cap"]}, "conditioning": {"kind": e["cap"].split(".")[-1]}, + "target": {"frames": n, "h": ph, "w": pw}} + rep.check(objs[i][8:8 + ml] == json.dumps(want, separators=(",", ":")).encode(), + f"{t}: entry {i}: the base's manifest is §5.2's") + elif e["role"] == REFINEMENT: + base = E[pieces[k]["first_entry"]] + # §5.4: the refinement's manifest, as `wai stage refine` writes it. + want = {"wai": "1.0", "media": comp["media"], "intent": "replicate", + "model_requirement": {"capability": e["cap"]}, "conditioning": {"kind": "wir1"}, + "refines": {"base": base["hash"].hex(), "prev": E[e["refines"]]["hash"].hex(), "layer": e["layer"]}, + "target": None} + rep.check(objs[i][8:8 + ml] == json.dumps(want, separators=(",", ":")).encode(), + f"{t}: entry {i}: the refinement's manifest is §5.4's") + try: + stage[i] = wir1_apply(payload, stage[e["refines"]]) + except (SV.Refused, KeyError, IndexError, struct.error) as x: + rep.check(False, f"{t}: entry {i}: the layer does not apply to the stage below it ({x}) (prefix-digest)") + return named | unreached(files, c['id']) for i, e in enumerate(E): - if e["tier"] != 2: - continue - if e["role"] != BASE: - rep.check(False, f"{t}: entry {i}: a refinement's prefix, which this verifier does not decode") - continue - rep.check(clip_digest(decoded[e["hash"]][1]) == e["prefix"], - f"{t}: entry {i}: the prefix decodes to its prefix_digest (prefix-digest)") + if e["tier"] == 2: + rep.check(clip_digest(stage[i]) == e["prefix"], + f"{t}: entry {i}: the prefix decodes to its prefix_digest (prefix-digest)") # The floor objects (§7.2) and the CMAF files their temporal units give (§7.6). rung = next((r for r in P["ladder"] if r["component"] == c["id"] and r["role"] == "floor"), None) @@ -2792,13 +3158,16 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): rep.check(path in files and load(os.path.join(pkg, path)) == cats[path], f"{t}: {path} is the one the index, the objects and the floor give (catalog-mismatch)") if inputs: - verify_sources(rep, t, root, P, comp, ix, decoded, units_of) + verify_sources(rep, t, root, P, comp, ix, stage, units_of, objs) return named -def verify_sources(rep, t, root, P, comp, ix, decoded, units_of): +def verify_sources(rep, t, root, P, comp, ix, stage, units_of, objs): """What only the source decides: the pieces (§4.3, §4.6) and their rules, moments and - class plan (§8.1), each base's sse (§6.6) and step (§5.6), and the supplied floor (§7.3).""" + class plan (§8.1); each piece's ladder (§5.7) re-derived from the source: an + `int_motion` base's step (§5.6) and choices (§5.5), a still's bytes (§5.7), each + refinement's bytes and reconstruction (§5.4), and every entry's sse (§6.6); and the + supplied floor (§7.3). `stage` is each entry's prefix as the package decodes.""" num, den = comp["timebase"]["num"], comp["timebase"]["den"] source = comp.get("source", "") name = source[:-len(".y4m")] @@ -2815,35 +3184,56 @@ def verify_sources(rep, t, root, P, comp, ix, decoded, units_of): rep.check(bits == [(pc["rules"], pc["moments"]) for pc in ix["pieces"]], f"{t}: every piece's rules and moments (§8.1)") rep.check(rows == ix["plan"], f"{t}: the class plan is §8.1's") - rung = next(r for r in P["ladder"] if r["component"] == comp["id"] and r["role"] == "base") - p = rung["params"] - own, cache, chosen = owners(ix), {}, set() - for i, e in enumerate(ix["entries"]): - if e["role"] != BASE: - continue - pc = ix["pieces"][own[i]] - src = frames[pc["start"]:pc["start"] + pc["duration"]] - wv, dec = decoded[e["hash"]] - sse = sum((a - b) ** 2 for s, f in zip(src, dec) for a, b in zip(s, f)) - rep.check(len(dec) == len(src) and sse == e["sse"], f"{t}: entry {i}: sse against the source (§6.6)") - n = len(src) - rep.check((wv["block"], wv["mv_precision"]) == ((p["block"], p["mv_precision"]) if n > 1 else (1, 1)), - f"{t}: entry {i}: the payload's block and precision are the rung's (§5.5)") - key = hashlib.sha256(b"".join(src)).digest() + E = ix["entries"] + cache, chosen = {}, set() - def sse_of(q, key=key, src=src): - if (key, q) not in cache: - cache[key, q] = closed_loop(src, h, w, p["block"], p["search"], p["mv_precision"], q) - return sse_px_milli(cache[key, q][1], h, w, len(src)) - q = choose_q(sse_of, p["q_candidates"], p["max_sse_px_milli"]) - rep.check(wv["q"] == q, f"{t}: entry {i}: the step is §5.6's choice ({wv['q']}, the rule {q})") - rep.check([bytes(f) for f in cache[key, q][0]] == dec, - f"{t}: entry {i}: the payload decodes to the encoder's reconstruction at that step (§5.5)") - if (e["hash"], key) not in chosen: - chosen.add((e["hash"], key)) - bad = encoder_choices(wv, p, src, dec) - rep.check(not bad, f"{t}: entry {i}: every vector, coded residual and the table are the encoder's (§5.5)" - + (f": {bad[0]}" if bad else "")) + def load_object_payload(ix, i): + return objs[i][12 + E[i]["man_len"]:] + for k, pc in enumerate(ix["pieces"]): + _, _, then = piece_then(P, ix, k) + base_rung, chain, _ = piece_ladder(P, comp["id"], then) + src = frames[pc["start"]:pc["start"] + pc["duration"]] + n = len(src) + i = pc["first_entry"] + e = E[i] + recon = stage[i] + rep.check(sum((a - b) ** 2 for s, f in zip(src, recon) for a, b in zip(s, f)) == e["sse"] and len(recon) == n, + f"{t}: entry {i}: sse against the source (§6.6)") + if base_rung["capability"] == "wai.image.png": + # §5.7: the still is the piece's first frame, written as the module states. + m = load_object_payload(ix, i) + rep.check(m == png_still(src[0], w, h), f"{t}: entry {i}: the still is the piece's first frame, §5.7's bytes") + else: + p = base_rung["params"] + wv = IPV.parse_wiv1(load_object_payload(ix, i), 0) + rep.check((wv["block"], wv["mv_precision"]) == ((p["block"], p["mv_precision"]) if n > 1 else (1, 1)), + f"{t}: entry {i}: the payload's block and precision are the rung's (§5.5)") + key = hashlib.sha256(b"".join(src)).digest() + + def sse_of(q, key=key, src=src, p=p): + if (key, q, p["block"], p["search"], p["mv_precision"]) not in cache: + cache[key, q, p["block"], p["search"], p["mv_precision"]] = closed_loop(src, h, w, p["block"], p["search"], p["mv_precision"], q) + return sse_px_milli(cache[key, q, p["block"], p["search"], p["mv_precision"]][1], h, w, len(src)) + q = choose_q(sse_of, p["q_candidates"], p["max_sse_px_milli"]) + rep.check(wv["q"] == q, f"{t}: entry {i}: the step is §5.6's choice ({wv['q']}, the rule {q})") + rep.check([bytes(f) for f in cache[key, q, p["block"], p["search"], p["mv_precision"]][0]] == recon, + f"{t}: entry {i}: the payload decodes to the encoder's reconstruction at that step (§5.5)") + if (e["hash"], key) not in chosen: + chosen.add((e["hash"], key)) + bad = encoder_choices(wv, p, src, recon) + rep.check(not bad, f"{t}: entry {i}: every vector, coded residual and the table are the encoder's (§5.5)" + + (f": {bad[0]}" if bad else "")) + # §5.4: each refinement, written again from the source and the stage below. + below = recon + for j, rung in enumerate(chain): + i = pc["first_entry"] + 1 + j + e = E[i] + mine, nxt = wir1_layer(below, src, h, w, rung["params"]["q"]) + rep.check(load_object_payload(ix, i) == mine, f"{t}: entry {i}: the layer is §5.4's bytes from the source") + rep.check(nxt == stage[i], f"{t}: entry {i}: it reconstructs what the package decodes") + rep.check(sum((a - b) ** 2 for s, f in zip(src, nxt) for a, b in zip(s, f)) == e["sse"], + f"{t}: entry {i}: sse against the source (§6.6)") + below = nxt # The supplied floor (§7.3): its listing, its temporal units and its key frames. sup = os.path.join(root, "floor") @@ -3080,14 +3470,37 @@ def verify(d): ix = parse_index(load(os.path.join(d, c["index"]))) index_rules(ix) f = c["floor"] - got = render_moq(ix["component"], ix["entries"], c["layer_lag_ms"], c["default_deadline"], (f["codec"], f["w"], f["h"])) + got = render_moq(ix["component"], ix["entries"], c["layer_lag_ms"], c["default_deadline"], (f["codec"], f["w"], f["h"]), + c["namespace"]) rep.check(got == c["moq"].encode(), f"{c['index']}: the MoQ catalog §8.4 gives") names = {t["name"] for t in json.loads(got)["tracks"]} rep.check(all(x in names for t in json.loads(got)["tracks"] for x in t.get("depends", [])), f"{c['index']}: no dependency names a track the catalog does not hold") rep.check(tree(os.path.join(d, "catalogs")) == sorted([c["index"][len("catalogs/"):] for c in cc["cases"]] + ["expected.json"]), "catalogs/: every index is a case") + # delivery/: the staged golden packed live and its delivery simulated (§8.9), each case + # re-derived from the fixture's cut and the package's index by this verifier's own + # mapping onto sim_verify.py's simulator. + dl = json.loads(load(os.path.join(d, "delivery", "expected.json"))) + pkg = os.path.join(d, "packs", dl["pack"], "package") + P = parse_policy_bytes(load(os.path.join(pkg, "policy.toml"))) + comp = P["component"][0] + ix = parse_index(load(os.path.join(pkg, f"index-{comp['id']}.wsix"))) + n_frames = len(read_y4m(fixture(comp["source"][:-len(".y4m")]))[4]) + pub = published_of(P, ix, n_frames) + ci = [c["id"] for c in P["class"]].index(dl["class"]) + for c in dl["cases"]: + got = deliver(P, ix["plan"][ci], pub, c["plan"]) + rep.check(got == c["outcome"], f"delivery {c['name']}: the outcome sim_verify.py's simulator gives") + rep.check([0xFF if x["presented"] is None else x["presented"] for x in got["pieces"]] == c["presented"], + f"delivery {c['name']}: the presented vector") + if not c["plan"].get("fifo"): + rep.check(all(x["base_arrival_us"] is not None and x["base_arrival_us"] <= x["due_us"] for x in got["pieces"]), + f"delivery {c['name']}: under §8.5's priorities every base arrives by its piece's instant") + rep.check(tree(os.path.join(d, "delivery")) == ["expected.json"], "delivery/: no file that is no case") verify_package_mutations(rep, d) + # staged-mutations/: the staged golden altered (§5.4, §5.7, §6.5, §8.6). + verify_package_mutations(rep, d, "staged-mutations") return rep diff --git a/wai/pack-conformance/package-mutations/class-named-classic-in-capitals/LISTING b/wai/pack-conformance/package-mutations/class-named-classic-in-capitals/LISTING index 01bf4d047..9c1b5f81a 100644 --- a/wai/pack-conformance/package-mutations/class-named-classic-in-capitals/LISTING +++ b/wai/pack-conformance/package-mutations/class-named-classic-in-capitals/LISTING @@ -16,7 +16,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 13b7a7cf473134c1a9dfb0a380c4464bcd5e3a7f2e007efe65f32fd9422175c0 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/class-named-classic/LISTING b/wai/pack-conformance/package-mutations/class-named-classic/LISTING index 6265087ff..fc2080697 100644 --- a/wai/pack-conformance/package-mutations/class-named-classic/LISTING +++ b/wai/pack-conformance/package-mutations/class-named-classic/LISTING @@ -16,7 +16,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 44692fd01ae15a4d7edaa1813241e55e8b2367180dc700b0c780d2d4014a4f95 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/component-medium/LISTING b/wai/pack-conformance/package-mutations/component-medium/LISTING index 9c51bd196..ddb71b31f 100644 --- a/wai/pack-conformance/package-mutations/component-medium/LISTING +++ b/wai/pack-conformance/package-mutations/component-medium/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 1cfc2a0d1b54f1a6ec47ebec95de9bc1cba3a9df40dbc3868d684ffa1f33db8d index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-cmaf/LISTING b/wai/pack-conformance/package-mutations/floor-cmaf/LISTING index 319a49f2a..5423767da 100644 --- a/wai/pack-conformance/package-mutations/floor-cmaf/LISTING +++ b/wai/pack-conformance/package-mutations/floor-cmaf/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-extra-key-frame-allowed/LISTING b/wai/pack-conformance/package-mutations/floor-extra-key-frame-allowed/LISTING index c2d314047..288bfc1c7 100644 --- a/wai/pack-conformance/package-mutations/floor-extra-key-frame-allowed/LISTING +++ b/wai/pack-conformance/package-mutations/floor-extra-key-frame-allowed/LISTING @@ -17,7 +17,7 @@ c4044eca76959c9f87230484a2b207fb12c0bcaedb8d921eab806014fca53cb8 hls/mobile/wai ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 b1ebb904e14be6aa830e400b4d732bc46ac47986f5b20202be0a261ace2369f0 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-extra-key-frame/LISTING b/wai/pack-conformance/package-mutations/floor-extra-key-frame/LISTING index 64a0b6e16..ca27450fc 100644 --- a/wai/pack-conformance/package-mutations/floor-extra-key-frame/LISTING +++ b/wai/pack-conformance/package-mutations/floor-extra-key-frame/LISTING @@ -17,7 +17,7 @@ c4044eca76959c9f87230484a2b207fb12c0bcaedb8d921eab806014fca53cb8 hls/mobile/wai ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 216c7a0baa5505fb92e3fe5cd2101962f220c6b188fe4ec3a5f655703373738a index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-framing-header/LISTING b/wai/pack-conformance/package-mutations/floor-framing-header/LISTING index 931e0e34a..76c6338dd 100644 --- a/wai/pack-conformance/package-mutations/floor-framing-header/LISTING +++ b/wai/pack-conformance/package-mutations/floor-framing-header/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 e1ff9d513a5576dd32236a21b4ad00d1e97b3d9247c8a0b05f44ff2ee3080594 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai 4dacc52e68b4fd119cb6da1387961aeabaa7dda4ed75ea9df881e92bfb2552d3 objects/504d5519c3690a705d7e2cb4089ef6bf474311b8809bfe1a7c6a3f00628e8b4c.wai diff --git a/wai/pack-conformance/package-mutations/floor-motif/LISTING b/wai/pack-conformance/package-mutations/floor-motif/LISTING index ec84d7965..6711c2407 100644 --- a/wai/pack-conformance/package-mutations/floor-motif/LISTING +++ b/wai/pack-conformance/package-mutations/floor-motif/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 44b744229777bd582f45bf670e2dd873798cb21c205f55efaea164acb3a6128f index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai 3ba33185bdcba397875e354354a5f54b06b561da6fa98f98584458752eddd41e objects/455892cdcffca9b861b34ac980ef50e2cfbd4062e81ed7029da687026ef4bd11.wai diff --git a/wai/pack-conformance/package-mutations/floor-object-corrupt/LISTING b/wai/pack-conformance/package-mutations/floor-object-corrupt/LISTING index 68d854de0..4ba7b1276 100644 --- a/wai/pack-conformance/package-mutations/floor-object-corrupt/LISTING +++ b/wai/pack-conformance/package-mutations/floor-object-corrupt/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-object-missing/LISTING b/wai/pack-conformance/package-mutations/floor-object-missing/LISTING index bf9412dae..17eb2c80d 100644 --- a/wai/pack-conformance/package-mutations/floor-object-missing/LISTING +++ b/wai/pack-conformance/package-mutations/floor-object-missing/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-opens-on-inter/LISTING b/wai/pack-conformance/package-mutations/floor-opens-on-inter/LISTING index 17d460995..bfa8ccea5 100644 --- a/wai/pack-conformance/package-mutations/floor-opens-on-inter/LISTING +++ b/wai/pack-conformance/package-mutations/floor-opens-on-inter/LISTING @@ -17,7 +17,7 @@ fa991c3f0242f452b02f5c70b72e10e864db388a40b654bfe64dcca8423b077e hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 f2f570cba26269e220795992dd224078b6c05473940ab1f6a900f2c8d1b220b1 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-renderer-settings/LISTING b/wai/pack-conformance/package-mutations/floor-renderer-settings/LISTING index 8a5621993..3eeb438f0 100644 --- a/wai/pack-conformance/package-mutations/floor-renderer-settings/LISTING +++ b/wai/pack-conformance/package-mutations/floor-renderer-settings/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 ed10f43afaa754358e36b19a8fe5a9b614a272d5741bfaebe5fb24ea2a457086 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/floor-sequence-header/LISTING b/wai/pack-conformance/package-mutations/floor-sequence-header/LISTING index 648e939f1..08fcb6588 100644 --- a/wai/pack-conformance/package-mutations/floor-sequence-header/LISTING +++ b/wai/pack-conformance/package-mutations/floor-sequence-header/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 752666c9b5af4bcf8d7eb7a09e43b368069828d2b9848dd882660e64025c74a2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 59c59bd437e2dac861a3b442f36b2387faa5942315c2b9fc0ffadd4163431973 objects/092d88d17b4fd8de5ed3d820c618dccba83a80a0b2d863a797ca9afb4c9ba259.wai 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai diff --git a/wai/pack-conformance/package-mutations/floor-target/LISTING b/wai/pack-conformance/package-mutations/floor-target/LISTING index 09f31dbac..b50d665d0 100644 --- a/wai/pack-conformance/package-mutations/floor-target/LISTING +++ b/wai/pack-conformance/package-mutations/floor-target/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 11eee7726f33e9ca6e1d16eddb0a01bdd4cff18cfe6caa625585f19429173bbf index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/index-capability/LISTING b/wai/pack-conformance/package-mutations/index-capability/LISTING index b6a910fa9..6c1203980 100644 --- a/wai/pack-conformance/package-mutations/index-capability/LISTING +++ b/wai/pack-conformance/package-mutations/index-capability/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 baabd81a9cad6e8afacbb18311924557956a0e37f5f271e8d458d651921707c4 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/index-class-plan/LISTING b/wai/pack-conformance/package-mutations/index-class-plan/LISTING index ed3027502..7040a088e 100644 --- a/wai/pack-conformance/package-mutations/index-class-plan/LISTING +++ b/wai/pack-conformance/package-mutations/index-class-plan/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 3c481132f733d1076cad2742c8a4b4667dad83397c1a57abfeb8333ab9944cbb index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/index-class-rate/LISTING b/wai/pack-conformance/package-mutations/index-class-rate/LISTING index 740278e5d..1a7d25f27 100644 --- a/wai/pack-conformance/package-mutations/index-class-rate/LISTING +++ b/wai/pack-conformance/package-mutations/index-class-rate/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 8bfd45971ced265bfc01d043d53359c2d23aa8ac4c6512b0a421f78e1bdfe476 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/index-class-records/LISTING b/wai/pack-conformance/package-mutations/index-class-records/LISTING index 7cc454ca4..7b0eb9732 100644 --- a/wai/pack-conformance/package-mutations/index-class-records/LISTING +++ b/wai/pack-conformance/package-mutations/index-class-records/LISTING @@ -20,7 +20,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 00641a12e923f55c78bdc6d1262a0d4dc218a7c35904590e3dbaf16a7607f745 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/index-string-order/LISTING b/wai/pack-conformance/package-mutations/index-string-order/LISTING index e9eaac9b9..10e49e106 100644 --- a/wai/pack-conformance/package-mutations/index-string-order/LISTING +++ b/wai/pack-conformance/package-mutations/index-string-order/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 c3a5e23418c1ff2cc8e12281df47a82bebe61b31260997a71311f1275d104334 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/patent-pool-floor-by-override/LISTING b/wai/pack-conformance/package-mutations/patent-pool-floor-by-override/LISTING index 68171dd88..3a85f5d42 100644 --- a/wai/pack-conformance/package-mutations/patent-pool-floor-by-override/LISTING +++ b/wai/pack-conformance/package-mutations/patent-pool-floor-by-override/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 ad140ff82229d3f47f4b2b67be03006ae5a86f89444b4db2b1265ed32766b965 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/patent-pool-floor/LISTING b/wai/pack-conformance/package-mutations/patent-pool-floor/LISTING index c7f7266ef..209182060 100644 --- a/wai/pack-conformance/package-mutations/patent-pool-floor/LISTING +++ b/wai/pack-conformance/package-mutations/patent-pool-floor/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 ad140ff82229d3f47f4b2b67be03006ae5a86f89444b4db2b1265ed32766b965 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/policy-json-relaxes-royalty-free/LISTING b/wai/pack-conformance/package-mutations/policy-json-relaxes-royalty-free/LISTING index 3e0643153..2092e52e2 100644 --- a/wai/pack-conformance/package-mutations/policy-json-relaxes-royalty-free/LISTING +++ b/wai/pack-conformance/package-mutations/policy-json-relaxes-royalty-free/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 108469a87ed44e9a7ce517eae4507fc789b7a3ffa926ded766ca463d6443e161 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai f3eedf9eb2870c0160801397412e7a4c3aca4a6e16f1a69a276e1356d38c9220 objects/61c7c3b7ccab07ed01df5b0d7ca0240598b914dbf2cabe545aa001e8de12d365.wai diff --git a/wai/pack-conformance/package-mutations/receipt-duplicate-member/LISTING b/wai/pack-conformance/package-mutations/receipt-duplicate-member/LISTING index 434078306..5547ab3e4 100644 --- a/wai/pack-conformance/package-mutations/receipt-duplicate-member/LISTING +++ b/wai/pack-conformance/package-mutations/receipt-duplicate-member/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/receipt-newline/LISTING b/wai/pack-conformance/package-mutations/receipt-newline/LISTING index c3efd6ffa..063494dfe 100644 --- a/wai/pack-conformance/package-mutations/receipt-newline/LISTING +++ b/wai/pack-conformance/package-mutations/receipt-newline/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/receipt-pretty/LISTING b/wai/pack-conformance/package-mutations/receipt-pretty/LISTING index 69775e496..0f972a990 100644 --- a/wai/pack-conformance/package-mutations/receipt-pretty/LISTING +++ b/wai/pack-conformance/package-mutations/receipt-pretty/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/package-mutations/sha256-file/LISTING b/wai/pack-conformance/package-mutations/sha256-file/LISTING index d3a83f4cb..6f18fd74d 100644 --- a/wai/pack-conformance/package-mutations/sha256-file/LISTING +++ b/wai/pack-conformance/package-mutations/sha256-file/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/packs/video-base-01/package/LISTING b/wai/pack-conformance/packs/video-base-01/package/LISTING index a9920d840..e2a8366c8 100644 --- a/wai/pack-conformance/packs/video-base-01/package/LISTING +++ b/wai/pack-conformance/packs/video-base-01/package/LISTING @@ -17,7 +17,7 @@ c5902f61ffaf77f940c4d3dc6e850340aaaeb3f433e4541f935c32443c50b341 hls/tv/master. ff4edf2929e380d205c1c74156621185feec53d593ffa968b8dfaf0d05448bcd hls/tv/wai-v.m3u8 5d42147b308330913c3d9c8a868ca83ce4d3f3e460da8d84c84014eb24aaf9d7 hls/tv/wai.m3u8 0b65400d0bbde57ed6a2c19d90cb72ec753de0e76165e4f286acfd5a7bda14d2 index-v.wsix -57ecdf226998f26618121279f42ebe674cb5467c489f6e9aa12feba24989d9ab moq/catalog.json +115e8e1e7ee0765de9bd2f71576cae01ce6576a5ce2a24f3374879eb74d9a6af moq/catalog.json 07e98fb9ef9ff3fa6bf04f9078da2a3d2423e586feea1061256131a1d1fbb943 objects/24761a434c91c627f11abd6265371966b50573602a200dd7bb04e0d90889326d.wai 4d583d03292b77f9d46b02f2c80a774b1cda1b07df38243d18fe586b82eaca60 objects/35c7a45323b7afe979c123fa885f35cc55fabd2a1954759aebe4b95dbc76361f.wai cfb71256ef44758ea279846a8938823994d321abce83ef73afd2ed269a8e0259 objects/618e0a89be8c7e556b380326b219c2f6fbfc5a0cb97754e871c1c23a99bdb481.wai diff --git a/wai/pack-conformance/packs/video-base-01/package/moq/catalog.json b/wai/pack-conformance/packs/video-base-01/package/moq/catalog.json index 40bccfcac..c3211ef22 100644 --- a/wai/pack-conformance/packs/video-base-01/package/moq/catalog.json +++ b/wai/pack-conformance/packs/video-base-01/package/moq/catalog.json @@ -1 +1 @@ -{"tracks":[{"altGroup":1,"name":"v/l0","packaging":"wai","priority":64,"renderGroup":1,"wai_capabilities":["wai.video.int_motion"],"wai_determinism":"decode-equivalence","wai_layer":0,"wai_receipts":false},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","packaging":"cmaf","renderGroup":1,"width":64}],"version":1} +{"tracks":[{"altGroup":1,"isLive":false,"name":"v/l0","namespace":"video-base-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_motion"],"wai_determinism":"decode-equivalence","wai_receipt_alias":1379901173764869,"wai_receipts":true,"wai_receipts_track":"v/l0/receipts","wai_staged":{"layer":0,"lead_ms":null,"set":"v"}},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","namespace":"video-base-01","packaging":"cmaf","renderGroup":1,"width":64},{"isLive":false,"name":"v/l0/receipts","namespace":"video-base-01","packaging":"wai-receipts"}],"version":1} diff --git a/wai/pack-conformance/packs/video-staged-01/floor/v.obu b/wai/pack-conformance/packs/video-staged-01/floor/v.obu new file mode 100644 index 0000000000000000000000000000000000000000..22908cd30fe3823735180ca700217e5d436ec353 GIT binary patch literal 6316 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 N + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/packs/video-staged-01/package/dash/mobile.mpd b/wai/pack-conformance/packs/video-staged-01/package/dash/mobile.mpd new file mode 100644 index 000000000..6fdc05cc3 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/dash/mobile.mpd @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/packs/video-staged-01/package/dash/tv.mpd b/wai/pack-conformance/packs/video-staged-01/package/dash/tv.mpd new file mode 100644 index 000000000..698e85327 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/dash/tv.mpd @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/packs/video-staged-01/package/floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s b/wai/pack-conformance/packs/video-staged-01/package/floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s new file mode 100644 index 0000000000000000000000000000000000000000..2ba26d8a11d3b7ef138447f276bb067040140a87 GIT binary patch literal 1824 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 SQQX7A5MU&1B!On{J_i6mj*~e6 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s b/wai/pack-conformance/packs/video-staged-01/package/floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s new file mode 100644 index 0000000000000000000000000000000000000000..62b794088b8c7fb0a1ab98cefb6ae0ba165760ed GIT binary patch literal 2800 zcma)63slTm9KJJ6HL77oW3*Fhno<)jx4+PX9%_+A508^anbgu`dZ@9I#T+VmEYTrp z)sVujN0CGx6M01{?LkXe>8aYXrCr3{e{+Y+Ii0g}=FHsl-QVx~zTfx1_YZ)MBr-aB z832GAxhy;k{nH;^0Cc#D*pPl)LF3GRoPmD%ie+I6dVxRN0bssV#IA}0K%a&up%U|MH2o9kC|ykptjX1K?z@F;PSEj3DrV>aB?y8)8k?e?#CRVol^pf682Ea*9$pAGZj|EXfW=Q5n za*}IXqL<_(Ye=4fX2iYvw<5zr6b1(9^fFI%WiEki>Yff4WP%VS5ak0=or9~J!1?n1 zgrNG&jA`MVXX*mG$;G{b8&LwPOhh1Q? zpbJ`EEqRjEoz3#w8Zvm*q8SrxM==g2Gj8wlP{{Rk+vc8eZ>+t3lq-(h!kg&d)?^^f z84`M;h+*$Fecx7iYy$ee$&DuJttnVNmJ2@w=VmX9|_j}5A>NK{z1-=6ZaW)E-tOKEB1&j)vTy?CZFdV4rGuX$|J=rxhq7tL}l z*5nn$2^~+1kJT9W=6e*BOmtotVVhG>)S{Su|H76*@e?Xq9Iy9I9c6m=gV&SxrERVw z3x6N|B;@S*8$I=z^JELzm)F$U{I%L6_|$q%<8T-A#I}dO+?war7`1ZxhF<>TYX*iw zk4pdUg1(##zU)0?QJ%lSiyg1-aFPxT%}GDi?iXGbJ@|p0>%OA+*m}ws>Zp|f=v4uc z5=a1GgMGf#P{?Ai`a!@%n@X_Tp0b5{Otz9PL{DE*jB2ZTQ&>Vq8jC&E?favK06v{p zJ-JANn%OK4S6fIkZlsgPsk3+7t`z5^MkB5Orl6uV z&ox#OC`&k4Bve;pEFZz;eUFnL910$JZ)bYnKQ5+9)W!wb{z9Tnfv}+r{Xaty=7g!_ z6c6&5?5^Vo!$ZlDEdh(e>p>7bQ$Kr;oBpP0vkDe$=>G16OS^8Ck}`!R$oqPt=;oqY zH0J%4H8{)cU~v3lbj7=G>*M zp=GJYvu1o0@THY{@8_xuDnsMgQ<6n%XQj~7ikbQiQY5IIESVY(tyxArM=@565-?TmIqU&a7H|T#wGp#lvTIt`;m|92*L=v` zlZ$wqut_bC6!ExVOPEKBTuvgq&_`6=5K*Dm?SUSY8#ba(TEzRvyA#^5^Pn3pc!o5fiE~L;7lx~JxqBu#@z>s4f)p9V*Bk<NUE0Yjvk zc8y~&nxMlTi>0(LC-q4m`KwuR XIj@ybL(Ynkhysg{+5Lz`K4teGXtcc( literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s b/wai/pack-conformance/packs/video-staged-01/package/floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s new file mode 100644 index 0000000000000000000000000000000000000000..66a9616f467608c404d3ae20e3943f0596ebdddd GIT binary patch literal 981 zcmZQzU~I|F&rf4uU=YYn%SZvzAi%=Fz?f4~ln53t0f{q##TkL5NJ&~s35Wp&#Xz+V zB}Juq3=HfbVM!pr2S|h1#t>~_wk9J)6NnTcf-Q`sh8VUQiRy_l6WN_QNaiDpQ^FR7 zn8_5Fo03?<#RUu*ru83Z#~W}MsfsWtJ1{Ub0ELVsY`6?w#U^-VKiU5G+e>L?^@sQ7 z%Ffa;+NNNnZp8QisEj!S7~ZZ8j1COvpWLszzD)k{YrQ(F8foJVl_z&I8Lka75;9_9 zXo4yPYH(oa`T`VVW@K@IXoiS20L56C*gBBJ+8c}njaXSYE+~L40jp~T3bAo;OSm9} znt(#=0=x+s%^;_MRe^*!82nfCB2?8FsTgrGG=JC#R^@sC=#EPQbqenp8IRqmSlaNV z^3?hBkv#H1ja-Z@71t0NbAUqJK!^Q87!MNS0lLc`p(+C?#0zwnJ35N03S2D(|3il#OZDrXHF*9Xt4iVyflo2Lr+O&i_bGHW)? zR{&~y95}iJXIX&p%jQpQGF?B~?~xj%c7FFx1BxQB@bmZMan=XA(#B}71zfv-;*cAH bzj}3s&}4iCt=&kQ??7UsT;x3O>uhg-8t^^c literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/classic/floor-v.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/classic/floor-v.m3u8 new file mode 100644 index 000000000..609fa33ba --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/classic/floor-v.m3u8 @@ -0,0 +1,18 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-MAP:URI="../../floor/v/init.mp4" +#EXTINF:2.000, +../../floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +#EXTINF:1.000, +../../floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +#EXTINF:1.600, +../../floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +#EXTINF:1.000, +../../floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +#EXTINF:2.400, +../../floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/classic/master.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/classic/master.m3u8 new file mode 100644 index 000000000..93630b459 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/classic/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=9334,AVERAGE-BANDWIDTH=8796,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/legacy/master.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/legacy/master.m3u8 new file mode 100644 index 000000000..93630b459 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/legacy/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=9334,AVERAGE-BANDWIDTH=8796,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/master.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/master.m3u8 new file mode 100644 index 000000000..c9f4be8cb --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=9334,AVERAGE-BANDWIDTH=8796,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..1b2cdf31e --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai-v.m3u8 @@ -0,0 +1,20 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai.m3u8 new file mode 100644 index 000000000..c4646ab7d --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/mobile/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="mobile" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/mobile.json" +#EXT-X-STREAM-INF:BANDWIDTH=323176,AVERAGE-BANDWIDTH=173043,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/tv/master.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/master.m3u8 new file mode 100644 index 000000000..c9f4be8cb --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=9334,AVERAGE-BANDWIDTH=8796,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai-v.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..6097d50b1 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai#n=101295" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai.m3u8 b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai.m3u8 new file mode 100644 index 000000000..97f423ab5 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=714374,AVERAGE-BANDWIDTH=586902,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/packs/video-staged-01/package/index-v.wsix b/wai/pack-conformance/packs/video-staged-01/package/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..fee174a88849a5a595a07e2c200511bb7f8ac10f GIT binary patch literal 2792 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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai new file mode 100644 index 0000000000000000000000000000000000000000..d817a5851f785d7555918a0d7e9e92c430e9bb1c GIT binary patch literal 2313 zcmZ`(2~ZPf82!0q2T=o7l!zGCql}IC?`Ah7hzgx*m9dHzm7*QI-uK@3{hN8o z(}-(+0F>fIjEE=TL>$DmcrMS040gctui~|6>lN%anRq%&5}Jn~@LRiux8T%0p>+E=hopGnfQO)4UK* z(wxyK7)hFCAxp5vc$3}~FR(^h&l*TWJSiYI6l6(CZ=y_kBTvxML<@ME4TUa1vI0vP zX=6O2BRG;1bTmb?0>kPU9Vd{2i6D83L?B8K&j^IxKuS8afidcN$g>n}k_KsGtl2zT zNGUE@nOq*p(olm4vQHbfHayX!I2hLPF53e_cmS zCd9}Je-)?!j2&p|f#x=FspmrMx^rS&#B&R4-ksryob-BZdC7{zuzS-Fipy^IkJ=Lj zQv3u1sCPZv6{9W>#81y|zaMmT+giWoNa2g91Jkan9)+9{n>QA@HkCUb{Z55drMG|i zRgtJQ7Cl+djjkLax{g(UxUmOBU{~UBVdw9e*MDo}>lfHi4L4A9+roy>%`KA)rnU7QG3p10t_Z+{Jdg;!R z*?qEpxOMQcdg{IU36;)YVpjkA_4#?p!z+nfVPZL-QK&4= zb>(U3v-AYDTRsA+!1h^cMZuZet!XM54fhNFja8thhaR%U%IG`(SjRpE+7Qt_r&dlL z5!f%R%Y#bEPF!XIPX+rRS{IN0z5jP z=#@dHj?t*kHY(9W&uV2*3%1W0q5zM%rg+anoR8(>=__OQHOXA4@Z2-9S>;J`T++wz zN%M0I%2dk7u|s{+E>rbBh8tFzH+zK*3dho(VEYXnvqf5N#u7t>>TADbPSy6@x7yG)3KmrJ{&wg+z54V7GqoX&h~C3d95^b z2apP-^wt0LojTmDG8DeAiAYREW0gRu(}R^&*J~=V2RgKx{syS$t`wSIK#)%-NY^3Giznx1wN+Q&eeM3eCA{79=XQ*U&my* gm)JWfS9gkEX!fu5a%U6RIBBh7g&k>Wb@!|P1K<9Lx&QzG literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai new file mode 100644 index 0000000000000000000000000000000000000000..f9c13f7d3d431c86404981ffa1ea1b6acfa6badf GIT binary patch literal 30699 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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai new file mode 100644 index 0000000000000000000000000000000000000000..f5c1230819c5ea3b457591883dea629e77c2e409 GIT binary patch literal 1234 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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai new file mode 100644 index 0000000000000000000000000000000000000000..949dacb1f32011f372eb1d59d01b055ce634a795 GIT binary patch literal 2843 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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai new file mode 100644 index 0000000000000000000000000000000000000000..21929485d4795b5760b9fe58e85cdd09e229b855 GIT binary patch literal 86299 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�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­v||)y!C|1< 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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/package.receipt.json b/wai/pack-conformance/packs/video-staged-01/package/package.receipt.json new file mode 100644 index 000000000..09e0296f1 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"6842a02b45fb2ddf8095621836901abdbf2526451d740976ffe7079fc210932c","media_class":"video","stage_root":"c10304943d547971b05fdb57231dcd6e097d7df38cc68ea92c376ddc957e022d"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"4441a653f101172ee9d4b76f788a10f23ab359e7bf3116fb09327699c2c83ef0","sig":"f62bd725148a4203b440769906eacf7083895accc0191782f7e43197d92a598b90f1a5ea04eedd493dda87a5e622889562f0f09a5010cb053700b016f44cb504","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484464,"total_bytes":566398,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/packs/video-staged-01/package/plan-v.txt b/wai/pack-conformance/packs/video-staged-01/package/plan-v.txt new file mode 100644 index 000000000..ce9efa1e3 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/plan-v.txt @@ -0,0 +1,5 @@ +0 +50 +75 +115 +140 diff --git a/wai/pack-conformance/packs/video-staged-01/package/policy.json b/wai/pack-conformance/packs/video-staged-01/package/policy.json new file mode 100644 index 000000000..2b02a3984 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/policy.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","class":[{"floor_only":false,"id":"tv","max_layer":2},{"floor_only":false,"id":"mobile","max_kbps":330,"max_layer":1},{"floor_only":true,"id":"legacy","max_layer":0}],"classify":{"motion_high_milli":9000,"static_mad_milli":300,"static_permille":5,"synthetic_bins":12},"component":[{"id":"v","media":"video","source":"video-staged-01.y4m","timebase":{"den":1,"num":25}}],"defaults":{"deadline_ms":1500},"floor":{"extra_keyframes":false,"supplied_listing":"f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150"},"ladder":[{"capability":"wai.video.int_motion","component":"v","id":"b0","params":{"block":16,"max_sse_px_milli":60000,"mv_precision":1,"q_candidates":[24,16],"search":4},"role":"base"},{"capability":"wai.video.int_refine","component":"v","id":"r1","params":{"q":12},"refines":"b0","role":"refinement"},{"capability":"wai.video.int_refine","component":"v","id":"r2","params":{"q":4},"refines":"r1","role":"refinement"},{"capability":"wai.image.png","component":"v","id":"still","params":{},"role":"base"},{"capability":"wai.video.av1","component":"v","id":"f0","params":{"source":"supplied"},"role":"floor"}],"lead":"v","live":{"layer_lag_ms":[0,200,400]},"moment":[{"from_ms":5600,"id":"m-event","tags":["event"],"to_ms":8000}],"royalty_free_only":true,"rule":[{"id":"static","then":{"ladder":["still","f0"]},"when":{"piece_kind":["static"]}},{"id":"motion","then":{"max_layer":1},"when":{"piece_kind":["motion_high"]}},{"id":"event","then":{"deadline_ms":800},"when":{"moment_tag":["event"]}}],"schema":"wai.pack.policy/1","segmentation":{"closed":true,"cut_abs_permille":300,"cut_rel_permille":3000,"cut_window":8,"lookahead_frames":6,"max_ms":4000,"min_ms":1000,"scene_cut":"wai.pack.scenecut/1","target_ms":2000}} \ No newline at end of file diff --git a/wai/pack-conformance/packs/video-staged-01/package/policy.toml b/wai/pack-conformance/packs/video-staged-01/package/policy.toml new file mode 100644 index 000000000..9541c224c --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/policy.toml @@ -0,0 +1,94 @@ +schema = "wai.pack.policy/1" +asset_id = "video-staged-01" +royalty_free_only = true +lead = "v" + +[[component]] +id = "v" +media = "video" +source = "video-staged-01.y4m" + +[component.timebase] +num = 25 +den = 1 + +[segmentation] +target_ms = 2000 +min_ms = 1000 +max_ms = 4000 + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" +params = { block = 16, search = 4, mv_precision = 1, q_candidates = [24, 16], max_sse_px_milli = 60000 } + +[[ladder]] +id = "r1" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "b0" +params = { q = 12 } + +[[ladder]] +id = "r2" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "r1" +params = { q = 4 } + +[[ladder]] +id = "still" +component = "v" +capability = "wai.image.png" +role = "base" + +[[ladder]] +id = "f0" +component = "v" +capability = "wai.video.av1" +role = "floor" +params = { source = "supplied" } + +[floor] +supplied_listing = "f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150" + +[live] +layer_lag_ms = [0, 200, 400] + +[[class]] +id = "tv" +max_layer = 2 + +[[class]] +id = "mobile" +max_layer = 1 +max_kbps = 330 + +[[class]] +id = "legacy" +floor_only = true + +[[moment]] +id = "m-event" +from_ms = 5600 +to_ms = 8000 +tags = ["event"] + +[[rule]] +id = "static" +when = { piece_kind = ["static"] } +then = { ladder = ["still", "f0"] } + +[[rule]] +id = "motion" +when = { piece_kind = ["motion_high"] } +then = { max_layer = 1 } + +[[rule]] +id = "event" +when = { moment_tag = ["event"] } +then = { deadline_ms = 800 } diff --git a/wai/pack-conformance/packs/video-staged-01/package/stages/legacy.json b/wai/pack-conformance/packs/video-staged-01/package/stages/legacy.json new file mode 100644 index 000000000..c4964ed93 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/stages/legacy.json @@ -0,0 +1 @@ +{"class":"legacy","components":{"v":{"pieces":[{"dur":50,"floor_only":true,"p":0,"stages":[],"start":0},{"dur":25,"floor_only":true,"p":1,"stages":[],"start":50},{"dur":40,"floor_only":true,"p":2,"stages":[],"start":75},{"dur":25,"floor_only":true,"p":3,"stages":[],"start":115},{"dur":60,"floor_only":true,"p":4,"stages":[],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/packs/video-staged-01/package/stages/mobile.json b/wai/pack-conformance/packs/video-staged-01/package/stages/mobile.json new file mode 100644 index 000000000..b09903278 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/packs/video-staged-01/package/stages/tv.json b/wai/pack-conformance/packs/video-staged-01/package/stages/tv.json new file mode 100644 index 000000000..d55038742 --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/package/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101295,"deadline_ms":800,"h":"a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/packs/video-staged-01/policy.toml b/wai/pack-conformance/packs/video-staged-01/policy.toml new file mode 100644 index 000000000..9541c224c --- /dev/null +++ b/wai/pack-conformance/packs/video-staged-01/policy.toml @@ -0,0 +1,94 @@ +schema = "wai.pack.policy/1" +asset_id = "video-staged-01" +royalty_free_only = true +lead = "v" + +[[component]] +id = "v" +media = "video" +source = "video-staged-01.y4m" + +[component.timebase] +num = 25 +den = 1 + +[segmentation] +target_ms = 2000 +min_ms = 1000 +max_ms = 4000 + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" +params = { block = 16, search = 4, mv_precision = 1, q_candidates = [24, 16], max_sse_px_milli = 60000 } + +[[ladder]] +id = "r1" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "b0" +params = { q = 12 } + +[[ladder]] +id = "r2" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "r1" +params = { q = 4 } + +[[ladder]] +id = "still" +component = "v" +capability = "wai.image.png" +role = "base" + +[[ladder]] +id = "f0" +component = "v" +capability = "wai.video.av1" +role = "floor" +params = { source = "supplied" } + +[floor] +supplied_listing = "f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150" + +[live] +layer_lag_ms = [0, 200, 400] + +[[class]] +id = "tv" +max_layer = 2 + +[[class]] +id = "mobile" +max_layer = 1 +max_kbps = 330 + +[[class]] +id = "legacy" +floor_only = true + +[[moment]] +id = "m-event" +from_ms = 5600 +to_ms = 8000 +tags = ["event"] + +[[rule]] +id = "static" +when = { piece_kind = ["static"] } +then = { ladder = ["still", "f0"] } + +[[rule]] +id = "motion" +when = { piece_kind = ["motion_high"] } +then = { max_layer = 1 } + +[[rule]] +id = "event" +when = { moment_tag = ["event"] } +then = { deadline_ms = 800 } diff --git a/wai/pack-conformance/stage_diff.py b/wai/pack-conformance/stage_diff.py new file mode 100644 index 000000000..8292c90bb --- /dev/null +++ b/wai/pack-conformance/stage_diff.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +"""The staged packer, differentially: wai-pack against pack_verify.py, which shares no +code with it, on seeded random inputs (extensions/packaging.md §5.4, §5.7, §8.1, §8.9). + +Each case is one line to `wai-pack stage-json` and the same computation in +pack_verify.py, and the two results must be equal: + +- `refine`: a refinement layer (§5.4) over random frames of 1×1 to 7×6, one to four of + them, the stage below near the source or anywhere, at steps from 1 to 255: the WIR1 + payload byte for byte and each reconstructed frame; +- `still`: a still (§5.7) of a random, flat, striped or repeating picture up to 48×40: + the PNG byte for byte, and its decode; +- `plan`: a random policy (bases, refinement chains, a still, classes with rates and + floors, moments, rules with ladder subsets, maximum layers and deadlines, layer lags) + over random pieces with random layer sizes: the policy's verdict, and per piece its + rules and moments, its ladder's rungs, its deadline and every class's plan (§3.4, + §5.7, §8.1); +- `deliver`: random publications (pieces, layers, sizes, deadlines, the frame each is + published after) under a random link, start-up, encoder delay, cap and schedule: every + piece's outcome and the byte counts (§8.9), pack_verify.py running sim_verify.py's + simulator. + + python3 stage_diff.py [seed] [count] + +Exit status 0 only if every case agrees. +""" +import hashlib +import json +import os +import random +import subprocess +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import pack_verify as PV # noqa: E402 + + +def frames(rng, n, size): + return [[rng.randrange(256) for _ in range(size)] for _ in range(n)] + + +def refine_case(rng): + h, w, n = rng.randint(1, 6), rng.randint(1, 7), rng.randint(1, 4) + src = frames(rng, n, 3 * w * h) + if rng.random() < 0.6: + spread = rng.choice([1, 3, 12, 40]) + below = [[min(255, max(0, x + rng.randint(-spread, spread))) for x in f] for f in src] + else: + below = frames(rng, n, 3 * w * h) + q = rng.choice([1, 2, 3, 5, 7, 12, 16, 64, 255]) + return {"op": "refine", "h": h, "w": w, "q": q, "below": below, "src": src} + + +def refine_py(c): + payload, recon = PV.wir1_layer([bytes(f) for f in c["below"]], [bytes(f) for f in c["src"]], c["h"], c["w"], c["q"]) + return {"payload": payload.hex(), "recon": [hashlib.sha256(f).hexdigest() for f in recon]} + + +def still_case(rng): + w, h = rng.randint(1, 48), rng.randint(1, 40) + kind = rng.randrange(4) + if kind == 0: + rgb = [rng.randrange(256) for _ in range(3 * w * h)] + elif kind == 1: + v = rng.randrange(256) + rgb = [v] * (3 * w * h) + elif kind == 2: + period = rng.randint(1, 9) + rgb = [(i // period * 37) % 256 for i in range(3 * w * h)] + else: + tile = [rng.randrange(256) for _ in range(rng.randint(1, 20))] + rgb = [tile[i % len(tile)] for i in range(3 * w * h)] + return {"op": "still", "w": w, "h": h, "rgb": rgb} + + +def still_py(c): + png = PV.png_still(bytes(c["rgb"]), c["w"], c["h"]) + _, _, back = PV.png_frame(png) + return {"png": png.hex(), "decoded_sha256": hashlib.sha256(back).hexdigest()} + + +KINDS = ["natural", "static", "synthetic", "motion_high"] + + +def policy_text(rng, layers_wanted=None): + """A random policy: one video component, a base and a chain of refinements, maybe a + still, a floor; classes, moments, rules; layer lags and deadlines that may break §3.4.""" + tb = rng.choice([(25, 1), (30000, 1001), (24, 1), (50, 1)]) + depth = layers_wanted if layers_wanted is not None else rng.randint(0, 3) + lines = ['schema = "wai.pack.policy/1"', 'asset_id = "diff"', 'lead = "v"', "", + "[[component]]", 'id = "v"', 'media = "video"', "", "[component.timebase]", f"num = {tb[0]}", f"den = {tb[1]}", ""] + rungs = [("b0", "wai.video.int_motion", "base", None, "")] + prev = "b0" + for k in range(depth): + rungs.append((f"r{k + 1}", "wai.video.int_refine", "refinement", prev, f"params = {{ q = {rng.randint(1, 255)} }}")) + prev = f"r{k + 1}" + still = rng.random() < 0.5 + if still: + rungs.append(("still", "wai.image.png", "base", None, "")) + rungs.append(("f0", "wai.video.av1", "floor", None, "")) + if rng.random() < 0.15: + rng.shuffle(rungs) + for rid, cap, role, ref, params in rungs: + lines += ["[[ladder]]", f'id = "{rid}"', 'component = "v"', f'capability = "{cap}"', f'role = "{role}"'] + if ref: + lines.append(f'refines = "{ref}"') + if params: + lines.append(params) + lines.append("") + lag, x = [0], 0 + for _ in range(depth): + x += rng.choice([0, 0, 100, 200, 400]) + lag.append(x) + if rng.random() < 0.1: + lag = lag[:-1] or [0, 0] + lines += ["[live]", f"layer_lag_ms = {lag}", ""] + if rng.random() < 0.7: + lines += ["[defaults]", f"deadline_ms = {rng.choice([300, 800, 1500, 2000, 401])}", ""] + for i in range(rng.randint(0, 3)): + lines += ["[[class]]", f'id = "c{i}"', f"max_layer = {rng.randint(0, depth)}"] + if rng.random() < 0.5: + lines.append(f"max_kbps = {rng.choice([1, 5, 50, 200, 1000, 50000])}") + if rng.random() < 0.2: + lines.append("floor_only = true") + lines.append("") + tags = ["a", "b", "event"] + for i in range(rng.randint(0, 3)): + lo = rng.randint(0, 8000) + lines += ["[[moment]]", f'id = "m{i}"', f"from_ms = {lo}", f"to_ms = {lo + rng.randint(1, 4000)}", + f"tags = {json.dumps(rng.sample(tags, rng.randint(0, 2)))}", ""] + ids = [r[0] for r in rungs] + for i in range(rng.randint(0, 4)): + when = [] + if rng.random() < 0.6: + when.append(f"piece_kind = {json.dumps(rng.sample(KINDS, rng.randint(1, 2)))}") + if rng.random() < 0.4: + when.append(f"moment_tag = {json.dumps(rng.sample(tags, rng.randint(1, 2)))}") + then = [] + r = rng.random() + if r < 0.35: + if still and rng.random() < 0.5: + lad = ["still", "f0"] + else: + lad = ["b0"] + [f"r{k + 1}" for k in range(rng.randint(0, depth))] + ["f0"] + if rng.random() < 0.15: + lad = rng.sample(ids, rng.randint(1, len(ids))) + then.append(f"ladder = {json.dumps(lad)}") + if rng.random() < 0.4: + then.append(f"max_layer = {rng.randint(0, depth)}") + if rng.random() < 0.4: + then.append(f"deadline_ms = {rng.choice([300, 800, 1500, 600, 2])}") + lines += ["[[rule]]", f'id = "u{i}"', f"when = {{ {', '.join(when)} }}", f"then = {{ {', '.join(then)} }}", ""] + return "\n".join(lines) + "\n", tb, depth + + +def plan_case(rng): + text, tb, depth = policy_text(rng) + pieces, layer_bytes, start = [], [], 0 + for _ in range(rng.randint(1, 6)): + d = rng.randint(1, 120) + pieces.append({"start": start, "duration": d, "kind": rng.randrange(4)}) + layer_bytes.append([rng.randint(1, 60000) for _ in range(rng.randint(1, depth + 1))]) + start += d + return {"op": "plan", "policy": text, "pieces": pieces, "layer_bytes": layer_bytes} + + +def plan_py(c): + try: + P = PV.parse_policy(c["policy"]) + except PV.Refused as e: + return {"verdict": e.code} + comp = P["component"][0] + tb = (comp["timebase"]["num"], comp["timebase"]["den"]) + # A stage index in miniature: each piece's layers, one entry each, of the given sizes. + entries, ps = [], [] + for pc, lb in zip(c["pieces"], c["layer_bytes"]): + ps.append(dict(pc, first_entry=len(entries), n_entries=len(lb))) + entries += [{"role": PV.BASE if k == 0 else PV.REFINEMENT, "layer": k, "byte_len": b} for k, b in enumerate(lb)] + ix = {"tb": tb, "pieces": ps, "entries": entries} + bits, rows = PV.class_plan(P, ix) + out = [] + for k in range(len(ps)): + _, _, then = PV.piece_then(P, ix, k) + base, chain, floors = PV.piece_ladder(P, comp["id"], then) + out.append({"rules": bits[k][0], "moments": bits[k][1], "ladder": [base["id"]] + [r["id"] for r in chain] + [r["id"] for r in floors], + "deadline_ms": then.get("deadline_ms", P["defaults"]["deadline_ms"]), "class_plan": [row[k] for row in rows]}) + return {"verdict": "ok", "pieces": out} + + +def deliver_case(rng): + depth = rng.randint(0, 2) + while True: + text, tb, _ = policy_text(random.Random(rng.random()), depth) + try: + P = PV.parse_policy(text) + except PV.Refused: + continue + num, den = P["component"][0]["timebase"]["num"], P["component"][0]["timebase"]["den"] + if 1_000_000 * den % num == 0: + break + deadlines = sorted({P["defaults"]["deadline_ms"]} | {r["then"]["deadline_ms"] for r in P["rule"] if "deadline_ms" in r["then"]}) + published, start, n = [], 0, rng.randint(1, 5) + L = P["segmentation"]["lookahead_frames"] + for p in range(n): + d = rng.randint(1, 60) + at = start + d + L if p + 1 < n else start + d - 1 + dl = rng.choice(deadlines) + for k in range(rng.randint(0, depth) + 1): + published.append({"piece": p, "start": start, "duration": d, "at_frame": at, "layer": k, + "deadline_ms": 0 if k == 0 else dl, "bytes": rng.randint(1, 40000)}) + start += d + row = [rng.choice([0, 1, 2, 2, 0xFF]) for _ in range(n)] + plan = {"rtt_us": rng.choice([0, 1, 40000]), "bytes_per_ms": rng.randint(1, 300), "chunk_bytes": rng.choice([1, 100, 1200, 50000]), + "enc_us": rng.choice([0, 1000, 33000]), "startup_us": rng.randint(0, 6_000_000), + "layer_cap": rng.choice([None, None, 0, 1]), "fifo": rng.random() < 0.3} + return {"op": "deliver", "policy": text, "class_plan": row, "published": published, "plan": plan} + + +def deliver_py(c): + P = PV.parse_policy(c["policy"]) + return PV.deliver(P, c["class_plan"], c["published"], c["plan"]) + + +def main(argv): + if len(argv) < 2: + print(__doc__) + return 2 + binary = argv[1] + seed = int(argv[2]) if len(argv) > 2 else 1 + count = int(argv[3]) if len(argv) > 3 else 1000 + rng = random.Random(seed) + makers = [(refine_case, refine_py), (still_case, still_py), (plan_case, plan_py), (deliver_case, deliver_py)] + cases = [] + for i in range(count): + make, py = makers[i % len(makers)] + cases.append((make(rng), py)) + lines = "".join(json.dumps(c) + "\n" for c, _ in cases) + out = subprocess.run([binary, "stage-json"], input=lines.encode(), capture_output=True, check=True).stdout.decode().splitlines() + if len(out) != len(cases): + print(f"FAIL wai-pack answered {len(out)} of {len(cases)} lines") + return 1 + fails, seen = 0, {} + for (c, py), got in zip(cases, out): + rust = json.loads(got) + mine = py(c) + seen[c["op"]] = seen.get(c["op"], 0) + 1 + if "verdict" in mine and mine["verdict"] != "ok": + seen["refused"] = seen.get("refused", 0) + 1 + if rust != mine: + fails += 1 + if fails <= 5: + print(f"FAIL {c['op']}: {json.dumps(c)[:300]}\n wai-pack: {json.dumps(rust)[:400]}\n pack_verify.py: {json.dumps(mine)[:400]}") + print(f"stage_diff: {len(cases) - fails}/{len(cases)} agree ({', '.join(f'{k} {v}' for k, v in sorted(seen.items()))})") + return 0 if fails == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/wai/pack-conformance/staged-mutations/expected.json b/wai/pack-conformance/staged-mutations/expected.json new file mode 100644 index 000000000..b99d0c157 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/expected.json @@ -0,0 +1,34 @@ +{ + "pack": "video-staged-01", + "cases": [ + { + "name": "refinement-deadline", + "reason": "index-policy-mismatch", + "remove": [] + }, + { + "name": "layer-dropped", + "reason": "index-policy-mismatch", + "remove": [ + "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" + ] + }, + { + "name": "still-not-the-ladder", + "reason": "index-policy-mismatch", + "remove": [] + }, + { + "name": "layer-replaced", + "reason": "prefix-digest", + "remove": [ + "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" + ] + }, + { + "name": "still-held-digest", + "reason": "prefix-digest", + "remove": [] + } + ] +} diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/LISTING b/wai/pack-conformance/staged-mutations/layer-dropped/LISTING new file mode 100644 index 000000000..497bde805 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/LISTING @@ -0,0 +1,40 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +6329f7722e2cfd5b690ae3e8761f626b9a513f0525ab78b1d520222f8560f497 dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +9a3c159f30e372273ae966c3eea837c9539bf66dff217d8e1cc4432002a203ea hls/tv/wai-v.m3u8 +d6dc24d2117d3183179ba84cf134d84b27e1b5e410620921ec56e353f7332af5 hls/tv/wai.m3u8 +a1ecbd6ea4c08332f6ae0465791551f4371a4cbf4aa1c45f4674062e92631673 index-v.wsix +8ae5eb928b7f51890d539d4db18556cb9e6f61e98e3630eb064074bf97225be0 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +908d00b2cef08fc0e207a6aabf68fc4ca4b42b1f13afe3b8ae72eda6645c1d43 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +be4045b592cd50c23581bfb69bd09506e826434c7f02d71e18cb207e5ae415d2 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/dash/tv.mpd b/wai/pack-conformance/staged-mutations/layer-dropped/dash/tv.mpd new file mode 100644 index 000000000..daf6ee14a --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/dash/tv.mpd @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..222d09fb3 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai-v.m3u8 @@ -0,0 +1,24 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai.m3u8 new file mode 100644 index 000000000..f463c8ca3 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=645600,AVERAGE-BANDWIDTH=460283,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/index-v.wsix b/wai/pack-conformance/staged-mutations/layer-dropped/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..8c2b105c4a749b8e7e495e4948af316e6751caa6 GIT binary patch literal 2664 zcmWF!_KaX;U|^60Vi3&=#DYM~0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJrT28IbE z$R3y(=NUs;7iV*qZRK(N7;nWSAZF3V>KlAHUdGqa`{N;9^>wLG1EAD@AOPuy`GMcj z`GMai+q?x^zn+nQeP$lBzCy%*rxvGGJw;cKSsN_YKlUH2a~o9un-nMmN>#nu^=D4N zrOvJ2qDmT8w3qz!e);a`cD92Vrb}nD2?Vg2d9ZtMq z;5Xl4ulL0pcCThX7V((0ckYh22OCPt1-n;^d;i#R z%g$jU8;3!bLc`z5?bYlk_Mh=bHP2K@F#1r0N{;Wfipobj5As_-k=fI+?x3oltlG;P z)h^1MoM7#60u=sipwI_J0ucbD9t18!t<{1!2d-0lYFn+5fzY+&4L?rwpSW@1@YWQq z=`A044bEw8EHjZwe549d3Uf8cEadoqeeBp#DTBRC?29?~`o8gqOhO?*13GT(^% zbvdg2AiI&{zv%FjPc}I^Cv9g(?3O;HtEU>EBa`OdS?+sRX(ey}#IFnu5DQ`PKc@_4 zGlc$n>TEuzu3;@_(c>F${r-F@&~DsjgpP`8eyF ze)isVd!XqRW*^9Ya{PY=8h}qg5r_ncP5&@wgX{-I3Ag|N()3FIF#ADeJUIOW0|Zq5 z(JTGK?B|Bq&jj*6$bNdIf0+I7_-6w;ga`mq4+60A4?X?Q-;~TY|94gZt6t@}Z zxw7ipy^r3i=VV>ito{Ao44m>{<=>7fR7cO)9ldvpT<7OW9ZTnJ$=n$9G+I2e`AMqc zTkS2JGyeq2{knCv`N!7S_l-gyU>%7zQ^^0_YCk}A>i=FzLKJErBnUl)KkdvC8m|Q8sP@b5nSC*NQny;6cR}!C_Uy_-hhb~%_nwFWD%8R5n VvCI&{&CE?qPt_~PONTg%0RR$@kB|TW literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/moq/catalog.json b/wai/pack-conformance/staged-mutations/layer-dropped/moq/catalog.json new file mode 100644 index 000000000..99b1979ff --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/moq/catalog.json @@ -0,0 +1 @@ +{"tracks":[{"altGroup":1,"isLive":false,"name":"v/l0","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_motion","wai.image.png"],"wai_determinism":"decode-equivalence","wai_receipt_alias":6826975590310646,"wai_receipts":true,"wai_receipts_track":"v/l0/receipts","wai_staged":{"layer":0,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1@800","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":600,"wai_determinism":"decode-equivalence","wai_receipt_alias":4051354593871184,"wai_receipts":true,"wai_receipts_track":"v/l1@800/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1300,"wai_determinism":"decode-equivalence","wai_receipt_alias":5745064158311842,"wai_receipts":true,"wai_receipts_track":"v/l1/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0","v/l1"],"isLive":false,"name":"v/l2","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1100,"wai_determinism":"decode-equivalence","wai_receipt_alias":6697012243957121,"wai_receipts":true,"wai_receipts_track":"v/l2/receipts","wai_staged":{"layer":2,"lead_ms":null,"set":"v"}},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","namespace":"video-staged-01","packaging":"cmaf","renderGroup":1,"width":64},{"isLive":false,"name":"v/l0/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1@800/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l2/receipts","namespace":"video-staged-01","packaging":"wai-receipts"}],"version":1} diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/package.receipt.json b/wai/pack-conformance/staged-mutations/layer-dropped/package.receipt.json new file mode 100644 index 000000000..4d0a8277e --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"d575f6a574a1071e88402aba762f31ad28d60f8986d03561b02a621319883549","media_class":"video","stage_root":"2f036a0a49ba5f09ef0a2aaaecabdb8ac179706fbe03427968a53e9bc5b7ecfa"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"86dd2e2e75bc342bae920fef1cb0eb60f113a72753146f979cb5f6285ca3d35b","sig":"2221ea6e710b83c81a1e28db737c2ebd331f7052054d603e2a236871d74b14e8730690d2b886e03346eceb7a52c898c3143d617864edea4e2abddb6a36ae080b","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":383169,"total_bytes":465103,"total_entries":17,"unique_objects":13,"version":2,"work_units":17} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/layer-dropped/stages/tv.json b/wai/pack-conformance/staged-mutations/layer-dropped/stages/tv.json new file mode 100644 index 000000000..7fcf2cfd4 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-dropped/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/LISTING b/wai/pack-conformance/staged-mutations/layer-replaced/LISTING new file mode 100644 index 000000000..b95ddbfec --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +9f6fa5094b9f075b1bdbc6f22f89c3208f9108958fb45adcf9b7ea60ac24f7a8 dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +ec09c87f857b5d0eecaa0b50e728938696896d514ac5e7a66aaa3da10aaebe3a hls/tv/wai-v.m3u8 +cf1448779f95e872d6799e969f904c4fcf5a75bc189af32656c04936000eddc4 hls/tv/wai.m3u8 +328c5486665e380888f13951172882a68f1d97190c16e76f1c7f013b25838f39 index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +640bbd18bfdbfe43041201d54647bb3b89dddac3d827c3b9558d6f4c1f7aa9ef objects/2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f.wai +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +7913d4989c560605b6c0ebf8e1d881caa3e9bc14c67748b2fa69c8622b53382f package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +fc65cfd8e8725ede3ac50720908d30363821a75e8bb994e8ae2cafc7c7aea9a1 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/dash/tv.mpd b/wai/pack-conformance/staged-mutations/layer-replaced/dash/tv.mpd new file mode 100644 index 000000000..a1defddea --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/dash/tv.mpd @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..783566ec0 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f.wai#n=82235" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai.m3u8 new file mode 100644 index 000000000..6e46a0c48 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=650840,AVERAGE-BANDWIDTH=563077,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/index-v.wsix b/wai/pack-conformance/staged-mutations/layer-replaced/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..1c220dfcda2c4a0282ca1045930afdfa576fcb30 GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJt3JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+j zf!`+Eyaijoo{@ijW*)P?Ld1Wk7N=D`MOTkm8!Xm8_8+Ws8&vF2h61ieXPJR0NH;B7}>M!o`q^1PP}2@ zH{W5e_r)7_uVz0M@tCyZ_ko;}Ey@TV+0(J^psJs&+RGc& zF3OyoVC`@M6#i_W&<8~V5dfqf1TI6Z)q*$&u2XwzTdk3S(6!_ZKThbJ0@OU115)$ii%#qii=7|&nJZ_{l2i+u!&fS{x%+8Nd^)Kz--!El zIja32yOHC+=3M>t>R&qcK#nB-96-~2)xt8HLl zc&YP)uBkrKX_pgt8IP~ryqIPoKaZ`p3kcfiWuP_JI{~3Od`< zW8}so{7_|W!ObOWZt|Z46{0`@(*}xvP!fXi|J(>O&oC|Uf07qwm}_F8>G)7VF30N7 z@2@jTpI#1fO#y2L5-Xq#DX4ic@g;&GvX@?m#>~IE?f!$a47=8)mV`B_u3usKIP05! z_TF`Spy?H6AIN@k{C@@-fKNaXhy;jD|1f8R><2{&xBvjs^h*CQ`$1(qIQ;_y1XTXf zEB(Xl=Z4tN1oA(~etM;UnEmkhX9GHf2mn$K0~IzJUk`-uf(i~b*vmy<>mCc4$U{Y zvg+HtkKU^1WL?*+{r%nyobq7h-;OF&N6*+Dy?2XT=jTZsOXqFL+!*vUT0FA(Nvh&o z?Jb-${{+hZw}m8JnEjwS99I4*00Twt-2$^oPqYp@YkjGGDpXVMf8dX`TXI-f8t?WE z*{^*<>2!?(hFjqgm0U^{ALzexGP*XOs=vfTdq z*;_lJ+RqQMADsL__FrJLRw__V^W3a19CNWjwL)T2N#do6+$O(6eyLerWH7zl1*yPb z_P?=5wSThnR0XrF|9j^hSSlMX#LMTpY)h&{%bQ29zV4XnBhg?X4N7o80P{a6{K?7x zu=uw@t4|r>B^ksXVEc-}3(1KUO`?u H#90gg7nrbG literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/objects/2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f.wai b/wai/pack-conformance/staged-mutations/layer-replaced/objects/2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f.wai new file mode 100644 index 0000000000000000000000000000000000000000..f41e72b3cd9441768d2cc2f95702ce0657a88726 GIT binary patch literal 82235 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⪕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 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/package.receipt.json b/wai/pack-conformance/staged-mutations/layer-replaced/package.receipt.json new file mode 100644 index 000000000..61b0b98a2 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"26a2ab38aa3a111f4e20b002d4865dee22979a45e05ac8dc87a85ef0dc5eaaee","media_class":"video","stage_root":"ee8c7f3bc403737be16d1c2ef468dfc2501edf038a74d5c3bbfba6f90fa3c486"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"293d85123e773ced00d6e321f3c19bd4eab627a8fea5bd253c61b36b3a70c218","sig":"bf6825f0958e47597d32de456994434b36bd4df14f0bfe9d8af74ba5f72b1960a01a647a20d97120e456c2c62803e091bf52b27c3cb0bbe8fa24104e01b6f40a","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":465404,"total_bytes":547338,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/layer-replaced/stages/tv.json b/wai/pack-conformance/staged-mutations/layer-replaced/stages/tv.json new file mode 100644 index 000000000..bcd95661d --- /dev/null +++ b/wai/pack-conformance/staged-mutations/layer-replaced/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":82235,"deadline_ms":800,"h":"2a61c2099ae5ac83c3da578d33dc965806c5323fa223e0f5e7da666a46fd4e3f","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/refinement-deadline/LISTING b/wai/pack-conformance/staged-mutations/refinement-deadline/LISTING new file mode 100644 index 000000000..87207c884 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-deadline/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +1ea26eaea415f8b459f5994a950e14016a501c2464b9ed8d77db800e16da31d2 hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +91d119b931466472d04db80f36672a2127a88c1785ab8ac779e464583eaeae24 index-v.wsix +f067d52b986c834745fed1ede3ee8e55e14cca70e739a4f183f60a9a3082b7e9 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +db854d9d598122af9def8c9218d4026cbc5aa71b7df6a8f8b76504bd62b60591 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +98c5eca61a270ce5793a982705a51ad888d3b9bb4169004997cb90a5280e4cd7 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/refinement-deadline/index-v.wsix b/wai/pack-conformance/staged-mutations/refinement-deadline/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..333fce458c9e43e629687ad5de173246e04a6c9a GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJt3JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+j zf!`+Eyaijoo{@ijW*)P?Ld1Wk7N=D`MOTkm8!Xm8_8+Ws8&vF2h61ieXPJR0NH;B7}>M!o`q^1PP}2@ zH{W5e_r)7_uVz0M@tCyZ_ko;}Ey@TV+0(J^psJs&+RGc& zF3OyoVC`@M6#i_W&<8~V5dfqf1TI6Z)q*$&u2XwzTdk3S(6!_ZKThbJ0@OU115)$ii%#qii=7|&nJZ_{l2i+u!&fS{x%+8Nd^)Kz--!El zIja32yOHC+=3M>t>R&qcK#nB-96-~2)xt8HLl zc&YP)uBkrKX_pgt8IP~ryqIPoKaZ`p3kcfiWuP_JI{~3Od`< zW8}so{7_|W!ObOWZt|Z46{0`@(*}xvP!fXi|J(>O&oC|Uf07qwm}_F8>G)7VF30N7 z@2@jTpI#1fO#y2L5-Xq#DX4ic@g;&GvX@?m#>~IE?f!$a47=8)mV`B_u3usKIP05! z_TF`Spy?H6AIN@k{C@@-fKNaXhy;jD|1f8R><2{&xBvjs^h*CQ`$1(qIQ;_y1XTXf zEB(Xl=Z4tN1oA(~etM;UnEmkhX9GHf2mn$K0~IzJUk`-uf(i~b*vmy<>mCc4$U{Y zvg+HtkKU^1WL?*+{r%nyobq7h-;OF&N6*+Dy?2XT=jTZsOXqFL+!*vUT0FA(Nvh&o z?Jb-${{+hZw}m8JnEjwS99I4*00Twt-2$^oPqYp@YkjGGDpXVMf8dX`TXI-f8t?WE z*{^zd6eyWyyG z`}%HF`}raE!|T5bY}QHz%4wdP)rDg&HmFueOe#sdG?ClncgQa_%Zm)Am%AVp7|i~J zE>!y`J5NLr^y=%5sXh`77Sf;u2Lv$xgTkMj{11zN z8?^eA5nhr(>;bl~7`%|22n=E*9+1NX%KD5z2y)4PAYI19P*TRmkei>BnUl)KkdvC8 zm|Q8sP@b5nSC*NQny;6cR}!C_Uy_-hhb~%_nwFWD%8R5nvCI&{&CE?qPt_~PONTg% F0RU6VujK#$ literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/refinement-deadline/moq/catalog.json b/wai/pack-conformance/staged-mutations/refinement-deadline/moq/catalog.json new file mode 100644 index 000000000..e98963bc8 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-deadline/moq/catalog.json @@ -0,0 +1 @@ +{"tracks":[{"altGroup":1,"isLive":false,"name":"v/l0","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_motion","wai.image.png"],"wai_determinism":"decode-equivalence","wai_receipt_alias":6826975590310646,"wai_receipts":true,"wai_receipts_track":"v/l0/receipts","wai_staged":{"layer":0,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1@800","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":600,"wai_determinism":"decode-equivalence","wai_receipt_alias":4051354593871184,"wai_receipts":true,"wai_receipts_track":"v/l1@800/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1300,"wai_determinism":"decode-equivalence","wai_receipt_alias":5745064158311842,"wai_receipts":true,"wai_receipts_track":"v/l1/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0","v/l1@800","v/l1"],"isLive":false,"name":"v/l2","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1100,"wai_determinism":"decode-equivalence","wai_receipt_alias":6697012243957121,"wai_receipts":true,"wai_receipts_track":"v/l2/receipts","wai_staged":{"layer":2,"lead_ms":null,"set":"v"}},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","namespace":"video-staged-01","packaging":"cmaf","renderGroup":1,"width":64},{"isLive":false,"name":"v/l0/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1@800/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l2/receipts","namespace":"video-staged-01","packaging":"wai-receipts"}],"version":1} diff --git a/wai/pack-conformance/staged-mutations/refinement-deadline/package.receipt.json b/wai/pack-conformance/staged-mutations/refinement-deadline/package.receipt.json new file mode 100644 index 000000000..34b1f3638 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-deadline/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"5444f17f41695562be99397f4db028d232ae8619c39be0378f915daf6cff7520","media_class":"video","stage_root":"3007bcbc7476a09fbd33229f2dbb55b260aaeba9e5c31a111526af8c05748503"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"cca77dcb5768f9ac52c778e6b6328bb994bb54ab51be42a9b8629187686cfbf7","sig":"1697273ef344a669c6e9febb9c13d6f07ef0586e090d030dc7c35def98236cef07588dd92c3c45037ef9fb21bd487fcab6ec0578ae46f6b5e16efd066193c907","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484464,"total_bytes":566398,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/refinement-deadline/stages/tv.json b/wai/pack-conformance/staged-mutations/refinement-deadline/stages/tv.json new file mode 100644 index 000000000..1216857e8 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-deadline/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101295,"deadline_ms":1500,"h":"a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-held-digest/LISTING b/wai/pack-conformance/staged-mutations/still-held-digest/LISTING new file mode 100644 index 000000000..217b941c5 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-held-digest/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +1ea26eaea415f8b459f5994a950e14016a501c2464b9ed8d77db800e16da31d2 hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +363d8ccb40ea92cdc6ce5b8e00b9281db2c180af287999072ce0dfd0be1977d2 index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +d7d7d5cbd6f11916451d71fd7a48f5b1d6d8ee65e9f1fad97bcc10553c3557cd package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +9c4eda5ea5972cbca98b3b89503235fcd807450c129545ca86c322337e27d872 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-held-digest/index-v.wsix b/wai/pack-conformance/staged-mutations/still-held-digest/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..fbf9ed58e3af3b45401f3cd972683d13f4b7052e GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJt3JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+j zf!`+Eyaijoo{@ijW*)P?Ld1Wk7N=D`MOTkm8!Xm8_8+Ws8&vF2h61ieXPJR0NH;B7}>M!o`q^1PP}2@ zH{W5e_r)7_uVz0M@tCyZ_ko;}Ey@TV+0(J^psJs&+RGc& zF3OyoVC`@M6#i_W&<8~V5dfqf1TI6Z)q*$&u2XwzTdk3S(6!_ZKThbJ0@OU115)$ii%#qii=7|&nJZ_{l2i+u!&fS{x%+8Nd^)Kz--!El zIja32yOHC+=3M>t>R&qcK#nB-96-~2)xt8HLl zc&YP)uBkrKX_pgt8IP~ryqIPoKaZ`p3kcfiWuP_JI{~3Od`< zW8}so{7_|W!ObOWZt|Z46{0`@(*}xvP!fXig;)Jt_CxaE#Y8pLGU)|{r%gZ2S;$%B zckcX}_^qq0E`I}S1`;cv3@NC2F!3dVA+ncVhsMmmy6yghvkbe|rIv&>sjgpP`8eyF ze)isVd!XqRW*^9Ya{PY=8h}qg5r_ncP5&@wgX{;Vf2f1#mHuJ&gUWbt`Ulz%D*xz} z{$ciWL+ob)6@Vc7>6QLr_QT_!4Ji`wFhS};09O8?r~mnzlG*0}&I(}FtK9YOT}_0D zK!V`a>X%2pS?0vAWqO8-t!ki$^v;NmYES zy@hk;pFr9FwvdDivmaE4!^%GeV4%poTVOWniPm9ftuM7tg=)(E5B#xqOAZT5jc$70j~!@11vGscg6qFQ4nOEvXVMZyvq+x?`%3M1zGiD8T^%%>SV9Cnx{I z;@<|XK4pZLWDt9R?JEW^BqsvBh{OYOm>7V5X9Pl!Oa24tGA4$SGB$?X{G`mBR5pg3 z)bzyUN)d+g#7w=i%#_r8z0ACl_}u)G%=|ob(W2C}%)C@yB(;fUh7fLMZen_>UO`?u H#90ggN)oYW literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-held-digest/package.receipt.json b/wai/pack-conformance/staged-mutations/still-held-digest/package.receipt.json new file mode 100644 index 000000000..8805d8696 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-held-digest/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"88d5ec96f371328b49efda1bb9fb940b3878eb2b67bf525da030990545ae9b1d","media_class":"video","stage_root":"8b80abc97ab16dc4f09f2113600feb1e1366a350897a256550fee7bb51c6ef44"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"c88caab5558890fcca9b1349a53a5ca0e3593434f6e4fa17964e678f8bd6a8d5","sig":"066303b7070b751aed70cf980cf32a31057dad17fc9111aa1863a204663020e475784d191c344fb806b5c8d41999d12e8605f7e23c9927199b503685e788fa03","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484464,"total_bytes":566398,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/LISTING b/wai/pack-conformance/staged-mutations/still-not-the-ladder/LISTING new file mode 100644 index 000000000..fadf1da03 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +1ea26eaea415f8b459f5994a950e14016a501c2464b9ed8d77db800e16da31d2 hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +35c672629c384c20029fdc16bc3cb4f7c03b8a057868f5116bb4646f04e39eb0 index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +767cc608341117aa7c83d39581a6eaa511fcab80d5ff646ad06702ba6c78ec88 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +8d245ca30f6284892c1d026f8d906feecba81d69ed80253f5bbcca6591530c0c policy.json +8ebd338a2e1caa82b8e7470f9926b0c07bb6f3ce7055560d315003118802fdc8 policy.toml +64c2be77a22f1f7ba46e72433d4023cf74237f83d2517d62d6419a04450aa462 stages/legacy.json +4e24738063718168c55f8af00766473c5214c772028ecfd26fa354741eb0c42d stages/mobile.json +a9ae80e6e6d3c9d2b8468964dd5977c18370941143d20282fcf9721207726de6 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/index-v.wsix b/wai/pack-conformance/staged-mutations/still-not-the-ladder/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..2a3c99b8dfc8699df04bb7d4b36e09b10c4dccfd GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeyjo|%;_FkNUHEsH>vNfIVyfG+E#l^Z@b2Fxj;4;hzD!~GD z6%&#om^jG&EMUw8@&+7uLs=R?8ssM?21Z6?1;tQ#kbW*8W??|p-vgDm0n&V6c?L$1 zJuoxQGlsG*&gL%L%H#Mk-ik>;%%Y9eH~4bAjIX2j$3wd6>r$ZxK&k&g0MZZh1HYs5 z1HVnSc?-6FJtP16%sgg&g^2%7El#U?imo2BHdw5G>_1rNHmLqLDNqKKs(Q8S&zyiu zom;;}l{BnqFZt>H^4-zxYzH$;m(Jc7x!}!V4wy?3`dEQs0J8rMFtTUcJqy)3oOr{) zZ@$A`?~6C=Ud?_i;xTE*?*lm{TarJ|`Ce}g*60j1ri2~Uyqlq0KPfA?HORKML^&9Hq^D-9C`<5deh^ijM1KQ67vp?=#`4QRf+#PQZHk6bLcCQxq{;}hh zox?;n4udR(hQE{BtJzWPKjV*To~e>x^q~lq9N%jdm5+8F$n8vu)c1*my22c+i37oFH47CS%kGgr=@C8-uRhp$v@bNADl_;gZbz7hB9 za#Z_4b|c4s(cvebY;tr?+Rl#HEqzE=Pc=YCCe6LG-1n~1O5Xm7Ul|%87Q*6xP8rN* z2>tcc*?dl2!&=Uw$2Z>k{rOU$-Nb!WGwStSRoxr6k8r-spNncgFv+3BzxjnYR@=b9 z@KWaoT~mFe(=I3QG9F*K%dz&ZMeXFeh`n}njGikf6QLr_JhiJaQX)Z2&nv{ zSNez9&keDk3FLo}{q#!zF#F;0&jxe|5dfqf1YqSKditNgDVc5l@2mh;y~sUFa%FF3>9hz@) zW!1NPAH7x2$-1st`}@5aIOW00za3Smj-IhQdhZsw&d-xNmd@LfxiRQzw0LCmlT^jG z+FLkh{t1-*ZwpDdF#ADuIIR3r00xTOy9H*Go@gC**7{QWRH&xh|G*z>x8$&}G~VqS zvS0o9c=(ww*g{zOv%v;dl0e+Cbk;xPj3?dAarQjoy!}VC+yA_j-Vs(`*EO3{cEeHW z_VwMU_VYvR2Pc1!{TJA*l?s&8JU6Qg$6Rbst&o^hl6Yw%x5@92Uuu>Y8B8yCK`Jnq z{Rv&D_D^=6s$iD&fA72lOJ&1_c==qHZAq1AdGqMi*Bw)RBpNKFK?x2BVEzY%KRNjy z7XLPA^(iB~B!k!kY+o^WAvqD~MI;`O!vxCuj6eu-$$ubS#>7xk#>SAFpOl%C%EpkB znx2?kDZ)^mn5kEmnUb2Xmzh@*pPOHjnV*L)T9lfWnU~6oq&Bh45W>yOO-xVKE67WS HIEw)QM)t3@ literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/package.receipt.json b/wai/pack-conformance/staged-mutations/still-not-the-ladder/package.receipt.json new file mode 100644 index 000000000..e65f18c54 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"354e187c770d3eb6787c40e13fe460f1f27852573caee30137afeacc92d4614e","media_class":"video","stage_root":"c10304943d547971b05fdb57231dcd6e097d7df38cc68ea92c376ddc957e022d"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"ab2c90a3d795e6d0fec30af3762d616546a6b41737ca62a884b44779db70b0e5","receipt_hash":"40cf6b24f9f2ff1a6a03f8748796d3669b9b2bc14d7544fdd23a1a627330f92d","sig":"6be47cdbd84173902b73449288cd0eedcd16e0f38baba0c51588218922191e9b10c20d69db39b59673062a817b6066dd0ed05dfa86e5349d610e1d57174d2f07","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484464,"total_bytes":566398,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.json b/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.json new file mode 100644 index 000000000..39ae28f54 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","class":[{"floor_only":false,"id":"tv","max_layer":2},{"floor_only":false,"id":"mobile","max_kbps":330,"max_layer":1},{"floor_only":true,"id":"legacy","max_layer":0}],"classify":{"motion_high_milli":9000,"static_mad_milli":300,"static_permille":5,"synthetic_bins":12},"component":[{"id":"v","media":"video","source":"video-staged-01.y4m","timebase":{"den":1,"num":25}}],"defaults":{"deadline_ms":1500},"floor":{"extra_keyframes":false,"supplied_listing":"f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150"},"ladder":[{"capability":"wai.video.int_motion","component":"v","id":"b0","params":{"block":16,"max_sse_px_milli":60000,"mv_precision":1,"q_candidates":[24,16],"search":4},"role":"base"},{"capability":"wai.video.int_refine","component":"v","id":"r1","params":{"q":12},"refines":"b0","role":"refinement"},{"capability":"wai.video.int_refine","component":"v","id":"r2","params":{"q":4},"refines":"r1","role":"refinement"},{"capability":"wai.image.png","component":"v","id":"still","params":{},"role":"base"},{"capability":"wai.video.av1","component":"v","id":"f0","params":{"source":"supplied"},"role":"floor"}],"lead":"v","live":{"layer_lag_ms":[0,200,400]},"moment":[{"from_ms":5600,"id":"m-event","tags":["event"],"to_ms":8000}],"royalty_free_only":true,"rule":[{"id":"static","then":{"ladder":["b0","r1","f0"]},"when":{"piece_kind":["static"]}},{"id":"motion","then":{"max_layer":1},"when":{"piece_kind":["motion_high"]}},{"id":"event","then":{"deadline_ms":800},"when":{"moment_tag":["event"]}}],"schema":"wai.pack.policy/1","segmentation":{"closed":true,"cut_abs_permille":300,"cut_rel_permille":3000,"cut_window":8,"lookahead_frames":6,"max_ms":4000,"min_ms":1000,"scene_cut":"wai.pack.scenecut/1","target_ms":2000}} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.toml b/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.toml new file mode 100644 index 000000000..f4aef7f68 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/policy.toml @@ -0,0 +1,94 @@ +schema = "wai.pack.policy/1" +asset_id = "video-staged-01" +royalty_free_only = true +lead = "v" + +[[component]] +id = "v" +media = "video" +source = "video-staged-01.y4m" + +[component.timebase] +num = 25 +den = 1 + +[segmentation] +target_ms = 2000 +min_ms = 1000 +max_ms = 4000 + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" +params = { block = 16, search = 4, mv_precision = 1, q_candidates = [24, 16], max_sse_px_milli = 60000 } + +[[ladder]] +id = "r1" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "b0" +params = { q = 12 } + +[[ladder]] +id = "r2" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "r1" +params = { q = 4 } + +[[ladder]] +id = "still" +component = "v" +capability = "wai.image.png" +role = "base" + +[[ladder]] +id = "f0" +component = "v" +capability = "wai.video.av1" +role = "floor" +params = { source = "supplied" } + +[floor] +supplied_listing = "f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150" + +[live] +layer_lag_ms = [0, 200, 400] + +[[class]] +id = "tv" +max_layer = 2 + +[[class]] +id = "mobile" +max_layer = 1 +max_kbps = 330 + +[[class]] +id = "legacy" +floor_only = true + +[[moment]] +id = "m-event" +from_ms = 5600 +to_ms = 8000 +tags = ["event"] + +[[rule]] +id = "static" +when = { piece_kind = ["static"] } +then = { ladder = ["b0", "r1", "f0"] } + +[[rule]] +id = "motion" +when = { piece_kind = ["motion_high"] } +then = { max_layer = 1 } + +[[rule]] +id = "event" +when = { moment_tag = ["event"] } +then = { deadline_ms = 800 } diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/legacy.json b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/legacy.json new file mode 100644 index 000000000..1d82dafec --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/legacy.json @@ -0,0 +1 @@ +{"class":"legacy","components":{"v":{"pieces":[{"dur":50,"floor_only":true,"p":0,"stages":[],"start":0},{"dur":25,"floor_only":true,"p":1,"stages":[],"start":50},{"dur":40,"floor_only":true,"p":2,"stages":[],"start":75},{"dur":25,"floor_only":true,"p":3,"stages":[],"start":115},{"dur":60,"floor_only":true,"p":4,"stages":[],"start":140}]}},"policy_digest":"ab2c90a3d795e6d0fec30af3762d616546a6b41737ca62a884b44779db70b0e5"} diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/mobile.json b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/mobile.json new file mode 100644 index 000000000..3af2303dc --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0}],"start":140}]}},"policy_digest":"ab2c90a3d795e6d0fec30af3762d616546a6b41737ca62a884b44779db70b0e5"} diff --git a/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/tv.json b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/tv.json new file mode 100644 index 000000000..5cf23cc1a --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-not-the-ladder/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101295,"deadline_ms":800,"h":"a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"ab2c90a3d795e6d0fec30af3762d616546a6b41737ca62a884b44779db70b0e5"} diff --git a/wai/pack-conformance/supplied_floor.py b/wai/pack-conformance/supplied_floor.py new file mode 100644 index 000000000..d01635825 --- /dev/null +++ b/wai/pack-conformance/supplied_floor.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Write a golden package's supplied floor (extensions/packaging.md §7.3) from its fixture. + +The floor of a golden is an AV1 coding of its generated fixture, so its every frame comes +from the fixture generator and nothing else. This script states how it was made: + +1. `wai-pack fixture gen ` writes the fixture the policy's component names; +2. `wai-pack plan` cuts it with the policy and writes the keyframe plan (§7.3); +3. each piece is coded alone by an AV1 encoder (`aomenc`, one pass, constant quality, no + lag and no alternate reference, so each temporal unit holds one shown frame, and no + key frame but the piece's first), in the low-overhead OBU format; +4. the pieces' streams, concatenated in order, are `floor/.obu`; +5. the policy's `floor.supplied_listing` is set to the SHA-256 of the floor directory's + listing (SPEC §3.1), which `wai-pack listing` prints. + +An encoder is not a pure function of its input across versions, so the stream is committed +and pinned by its listing: a packer checks the floor against the listing and the plan, and +never encodes it. This script is how the committed stream was written, not a step of +verification. + + python3 supplied_floor.py directory> [aomenc] +""" +import os +import re +import subprocess +import sys +import tempfile + + +def run(*args): + return subprocess.run(args, check=True, capture_output=True).stdout + + +def frames_of(y4m): + """The stream header line and each frame's bytes (its FRAME line included).""" + head_end = y4m.index(b"\n") + 1 + head = y4m[:head_end] + w = int(re.search(rb" W(\d+)", head).group(1)) + h = int(re.search(rb" H(\d+)", head).group(1)) + size = w * h + 2 * ((w + 1) // 2) * ((h + 1) // 2) + out, at = [], head_end + while at < len(y4m): + line = y4m.index(b"\n", at) + 1 + out.append(y4m[at:line + size]) + at = line + size + return head, out + + +def main(argv): + if len(argv) < 3: + print(__doc__) + return 2 + wai_pack, pack = argv[1], os.path.abspath(argv[2]) + aomenc = argv[3] if len(argv) > 3 else "aomenc" + policy_path = os.path.join(pack, "policy.toml") + policy = open(policy_path, encoding="utf-8").read() + comp = re.search(r'\[\[component\]\]\s*id = "([^"]+)"', policy).group(1) + source = re.search(r'source = "([^"]+)\.y4m"', policy).group(1) + with tempfile.TemporaryDirectory() as tmp: + src = os.path.join(tmp, "source.y4m") + run(wai_pack, "fixture", "gen", source, "-o", src) + plan = os.path.join(tmp, "plan.txt") + run(wai_pack, "plan", "--policy", policy_path, "--component", comp, "--source", src, "--out", plan) + starts = [int(x) for x in open(plan).read().split()] + head, frames = frames_of(open(src, "rb").read()) + bounds = starts + [len(frames)] + stream = b"" + for k, (a, b) in enumerate(zip(bounds, bounds[1:])): + piece = os.path.join(tmp, f"p{k}.y4m") + with open(piece, "wb") as f: + f.write(head + b"".join(frames[a:b])) + out = os.path.join(tmp, f"p{k}.obu") + run(aomenc, "--obu", f"--limit={b - a}", "--passes=1", "--lag-in-frames=0", "--auto-alt-ref=0", + "--kf-min-dist=0", f"--kf-max-dist={b - a}", "--end-usage=q", "--cq-level=40", "--cpu-used=6", + "--threads=1", "--row-mt=0", "-q", "-o", out, piece) + stream += open(out, "rb").read() + floor = os.path.join(pack, "floor") + os.makedirs(floor, exist_ok=True) + for name in os.listdir(floor): + os.remove(os.path.join(floor, name)) + with open(os.path.join(floor, f"{comp}.obu"), "wb") as f: + f.write(stream) + import hashlib + listing = hashlib.sha256(run(wai_pack, "listing", floor)).hexdigest() + policy = re.sub(r'supplied_listing = "[0-9a-f]{64}"', f'supplied_listing = "{listing}"', policy) + open(policy_path, "w", encoding="utf-8").write(policy) + print(f"{floor}/{comp}.obu: {len(stream)} bytes, {len(frames)} frames, key frames at {starts}; listing {listing}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/wai/registry/capabilities.json b/wai/registry/capabilities.json index 34cf28d92..ca6af967d 100644 --- a/wai/registry/capabilities.json +++ b/wai/registry/capabilities.json @@ -1735,13 +1735,6 @@ "section": "§4", "status": "defined" }, - { - "name": "wai_layer", - "kind": "catalog", - "document": "extensions/packaging.md", - "section": "§8", - "status": "defined" - }, { "name": "wai_receipt_alias", "kind": "catalog", diff --git a/wai/wai-pack/Cargo.lock b/wai/wai-pack/Cargo.lock index c2f9441f7..9046132f9 100644 --- a/wai/wai-pack/Cargo.lock +++ b/wai/wai-pack/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "arrayvec" version = "0.7.8" @@ -311,6 +317,15 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -674,6 +689,7 @@ version = "0.1.0" dependencies = [ "blake3", "ed25519-dalek", + "miniz_oxide", "serde_json", "sha2", "toml 0.8.23", diff --git a/wai/wai-pack/Cargo.toml b/wai/wai-pack/Cargo.toml index e9d59370f..d58d05e67 100644 --- a/wai/wai-pack/Cargo.toml +++ b/wai/wai-pack/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" rust-version = "1.97" license = "Apache-2.0" -description = "The reference packager of WAI's packaging extension: a declared policy and its digest, scene-cut segmentation with a bounded lookahead, piece classification, and the conformance corpus that pins them." +description = "The reference packager of WAI's packaging extension: a declared policy and its digest, scene-cut segmentation with a bounded lookahead, staged packages of refinement layers and stills, live windows, simulated delivery, and the conformance corpus that pins them." repository = "https://git.transaction.science/Transaction-Science/open-standards" publish = false @@ -31,6 +31,9 @@ toml = { version = "0.8", default-features = false, features = ["parse"] } serde_json = "1" blake3 = "1" sha2 = "0.10" +# Inflate, for the verifier's reader of a still (src/png.rs): an implementation apart from +# the packer's own deflate, so a still is decoded by code that did not write it. +miniz_oxide = "0.8" # Release builds check integer overflow as debug builds do, so the release tests in CI # catch a wrapped product (a field read as u16 and doubled) rather than run past it. diff --git a/wai/wai-pack/src/bin/wai_pack.rs b/wai/wai-pack/src/bin/wai_pack.rs index 6748a5b7c..6af6b8874 100644 --- a/wai/wai-pack/src/bin/wai_pack.rs +++ b/wai/wai-pack/src/bin/wai_pack.rs @@ -19,10 +19,33 @@ //! defaults to the key's did:key. A meter that gives no figure (a pack too short to //! reach a microjoule at , or a figure past 2^64 - 1) refuses the pack //! (`pack-meter`); it never writes an unmetered package in its place +//! wai-pack live --policy --out

--key [--signer-id ] [--window ] +//! pack a Y4M stream read from standard input as it arrives (packaging §9.2): each +//! piece coded as the segmenter closes it, its objects written to /objects/, and +//! each window of pieces (one window when 0 or absent) sealed as +//! windows//index-.wsix and windows//package.receipt.json, chained to the one +//! before; then plan-.txt and LISTING. No floor: a supplied floor is the on-demand +//! package's (§7.3). is new or empty //! wai-pack verify //! verify a package (packaging §9): every reason, or its counts and pack digest -//! wai-pack listing -//! the listing (SPEC §3.1) of every file of but LISTING +//! wai-pack listing [--primary] +//! the listing (SPEC §3.1) of every file of but LISTING; with --primary, of the +//! plan files and the objects the directory's stage indexes (index-*.wsix, and +//! windows/*/index-*.wsix) list as bases and refinements: what a live and an +//! on-demand pack of one source share (test 12) +//! wai-pack sim --policy --class --rate --startup +//! [--source ] [--rtt ] [--chunk ] [--enc ] [--cap ] [--fifo] +//! pack the source live and simulate its delivery to a subscriber of the class +//! (packaging §8.9, staged-measurement §10), printing the outcome per piece as JSON, +//! labelled simulated; --fifo sends without §8.5's priorities, to show what they +//! prevent +//! wai-pack stage-json +//! a conformance aid: reads JSON lines, each {"op": ...}, and writes one result per +//! line: "refine" {h,w,q,below,src} gives the WIR1 layer's hex and each reconstructed +//! frame's SHA-256 (§5.4); "still" {w,h,rgb} gives the still's hex (§5.7); "plan" +//! {policy, pieces:[{start,duration,kind,layer_bytes}]} gives each piece's rules, +//! moments, ladder rung ids, deadline and class plan (§5.7, §8.1); "deliver" {policy, +//! class_plan, published, plan} gives the simulated outcome (§8.9) //! wai-pack policy-verdict ... //! each policy's verdict (`ok`, or the reason a packager refuses it) and, when the //! policy itself parses, its digest (else `-`): `\t\t` @@ -55,8 +78,11 @@ fn usage() -> ExitCode { "usage:\n wai-pack policy [--royalty-free-only]\n \ wai-pack plan --policy --component [--source ] [--out ]\n \ wai-pack pack --policy --out --key [--source =]... [--floor ] [--signer-id ] [--royalty-free-only] [--meter none|model:]\n \ + wai-pack live --policy --out --key [--signer-id ] [--window ]\n \ wai-pack verify \n \ - wai-pack listing \n \ + wai-pack listing [--primary]\n \ + wai-pack sim --policy --class --rate --startup [--source ] [--rtt ] [--chunk ] [--enc ] [--cap ] [--fifo]\n \ + wai-pack stage-json\n \ wai-pack policy-verdict ...\n \ wai-pack source-verdict ...\n \ wai-pack fixture gen [-o ] | fixture list\n \ @@ -189,15 +215,111 @@ fn cmd_verify(args: &[String]) -> Result<(), String> { } fn cmd_listing(args: &[String]) -> Result<(), String> { - let dir = args.first().ok_or("listing: a directory")?; + let dir = args.iter().find(|a| !a.starts_with("--")).ok_or("listing: a directory")?; let mut files = wai::pin::read_dir_set(Path::new(dir)).map_err(|e| e.to_string())?; files.remove("LISTING"); + if args.iter().any(|a| a == "--primary") { + files = wai_pack::live::primary_files(&files)?; + } let view: std::collections::BTreeMap = files.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect(); let l = wai::pin::listing_from(&view).map_err(|e| e.to_string())?; use std::io::Write as _; std::io::stdout().write_all(&l).map_err(|e| e.to_string()) } +fn cmd_live(args: &[String]) -> Result<(), String> { + let path = opt(args, "--policy").ok_or("live: --policy")?; + let out = opt(args, "--out").ok_or("live: --out")?; + wai_pack::pack::out_dir_ready(Path::new(out)).map_err(|e| e.to_string())?; + let key = read_key(opt(args, "--key").ok_or("live: --key")?)?; + let bytes = std::fs::read(path).map_err(|e| format!("{path}: {e}"))?; + let signer_id = opt(args, "--signer-id").map(str::to_string).unwrap_or_else(|| wai::signer_id::did_key(&key.verifying_key().to_bytes())); + let mut o = wai_pack::live::LiveOpts::new(key, signer_id); + o.window_pieces = opt(args, "--window").map_or(Ok(0), |w| w.parse::().map_err(|e| format!("live: --window: {e}")))?; + let mut r = source::y4m::Y4mReader::new(std::io::BufReader::new(std::io::stdin().lock())).map_err(|e| e.to_string())?; + let mut lp = wai_pack::live::LivePacker::new(&bytes, r.header.w, r.header.h, o).map_err(|e| e.to_string())?; + let tb = lp.policy().components[0].timebase; + if let Some((n, d)) = r.header.rate + && (n, d) != (tb.num, tb.den) + { + return Err("live: pack-source-rate: the source's frame rate is not the component's timebase".into()); + } + let mut files: std::collections::BTreeMap> = std::collections::BTreeMap::new(); + let publish = |p: Vec, files: &mut std::collections::BTreeMap>| { + for x in p { + println!("piece {} layer {} at frame {}: {}", x.piece, x.layer, x.at_frame, hex(&x.hash)); + files.insert(wai_pack::objects::object_path(&x.hash), x.envelope); + } + }; + while let Some(f) = r.next_frame() { + let f = f.map_err(|e| e.to_string())?; + let p = lp.push(&f.rgb).map_err(|e| e.to_string())?; + publish(p, &mut files); + } + let comp = lp.policy().components[0].id.clone(); + let (p, windows, starts) = lp.finish().map_err(|e| e.to_string())?; + publish(p, &mut files); + for (n, w) in windows.iter().enumerate() { + files.insert(format!("windows/{n}/{}", wai::package::index_file(&comp)), w.index.clone()); + files.insert(format!("windows/{n}/package.receipt.json"), w.receipt.to_json().into_bytes()); + println!("window {n}: receipt {}", hex(&w.receipt.receipt_hash())); + } + files.insert(format!("plan-{comp}.txt"), wai_pack::pack::plan_file(&starts).into_bytes()); + let view: std::collections::BTreeMap = files.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect(); + let listing = wai::pin::listing_from(&view).map_err(|e| e.to_string())?; + files.insert("LISTING".into(), listing); + for (p, b) in &files { + let at = Path::new(out).join(p); + if let Some(d) = at.parent() { + std::fs::create_dir_all(d).map_err(|e| format!("{}: {e}", d.display()))?; + } + std::fs::write(&at, b).map_err(|e| format!("{}: {e}", at.display()))?; + } + Ok(()) +} + +fn cmd_sim(args: &[String]) -> Result<(), String> { + let path = opt(args, "--policy").ok_or("sim: --policy")?; + let class = opt(args, "--class").ok_or("sim: --class")?; + let num = |name: &str, default: Option| -> Result { + match opt(args, name) { + Some(v) => v.parse::().map_err(|e| format!("sim: {name}: {e}")), + None => default.ok_or_else(|| format!("sim: {name}")), + } + }; + let bytes = std::fs::read(path).map_err(|e| format!("{path}: {e}"))?; + let p = Policy::parse_bytes(&bytes).map_err(|e| e.to_string())?; + let c = p.components.first().ok_or("sim: no component")?; + let src = opt(args, "--source") + .map(str::to_string) + .or_else(|| c.source.as_ref().map(|s| Path::new(path).parent().unwrap_or(Path::new(".")).join(s).to_string_lossy().into_owned())) + .ok_or("sim: the component names no source; pass --source")?; + let y4m = std::fs::read(&src).map_err(|e| format!("{src}: {e}"))?; + let plan = wai_pack::delivery::DeliveryPlan { + rtt_us: num("--rtt", Some(0))?, + bytes_per_ms: num("--rate", None)?, + chunk_bytes: num("--chunk", Some(1200))?, + enc_us: num("--enc", Some(0))? * 1000, + startup_us: num("--startup", None)? * 1000, + layer_cap: opt(args, "--cap").map(|x| x.parse::().map_err(|e| format!("sim: --cap: {e}"))).transpose()?, + schedule: if args.iter().any(|a| a == "--fifo") { wai::stage_measure::sim::Schedule::Fifo } else { wai::stage_measure::sim::Schedule::StrictPriority }, + }; + let key = ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]); + let d = wai_pack::delivery::simulate_source(&bytes, &y4m, class, &plan, &key).map_err(|e| e.to_string())?; + println!("{}", serde_json::to_string_pretty(&d.to_json()).map_err(|e| e.to_string())?); + Ok(()) +} + +fn cmd_stage_json() -> Result<(), String> { + use std::io::BufRead as _; + for line in std::io::stdin().lock().lines() { + let line = line.map_err(|e| e.to_string())?; + let v: serde_json::Value = serde_json::from_str(&line).map_err(|e| e.to_string())?; + println!("{}", wai_pack::conformance::stage_json(&v)); + } + Ok(()) +} + fn cmd_policy_verdict(args: &[String]) -> Result<(), String> { for p in args { let b = std::fs::read(p).map_err(|e| format!("{p}: {e}"))?; @@ -312,6 +434,9 @@ fn main() -> ExitCode { "policy" => cmd_policy(rest), "plan" => cmd_plan(rest), "pack" => cmd_pack(rest), + "live" => cmd_live(rest), + "sim" => cmd_sim(rest), + "stage-json" => cmd_stage_json(), "verify" => cmd_verify(rest), "listing" => cmd_listing(rest), "policy-verdict" => cmd_policy_verdict(rest), diff --git a/wai/wai-pack/src/catalog.rs b/wai/wai-pack/src/catalog.rs index 3fdc025c3..47f3daa8c 100644 --- a/wai/wai-pack/src/catalog.rs +++ b/wai/wai-pack/src/catalog.rs @@ -89,6 +89,9 @@ pub struct CompView<'a> { /// Every object of the index by its content hash. pub objects: &'a BTreeMap<[u8; 32], Vec>, pub floor: Option, + /// The capability of the component's default base rung (§5.7): the base an HTTP view + /// carries (§8.3). + pub base_cap: String, } /// Why the catalogs cannot be written. @@ -156,11 +159,26 @@ impl CompView<'_> { self.index.piece_entries(p).iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement) && e.layer <= v).collect() } + /// What an HTTP view of class `c` lists for piece `p` (§8.3): what the class lists, + /// where the piece's base is of the component's default base capability; nothing + /// otherwise, so the floor plays there. http-delivery §4.2 lets no variant list bases + /// with no capability in common, so a piece coded by a rule's ladder in another base + /// capability (a still, §5.7) is carried over HTTP by the floor alone. + pub fn http_listed(&self, c: usize, p: usize) -> Vec<&EntryRec> { + let base_ok = self.index.piece_entries(p).iter().find(|e| e.role == Role::Base).is_some_and(|b| self.index.capability(b) == self.base_cap); + if base_ok { self.listed(c, p) } else { Vec::new() } + } + + /// Whether an HTTP view of class `c` lists any WAI object. + pub fn http_lists(&self, c: usize) -> bool { + (0..self.index.pieces.len()).any(|p| !self.http_listed(c, p).is_empty()) + } + fn seq_objects(&self, c: usize) -> Result, CatalogError> { let mut out = Vec::new(); let mut seen = std::collections::BTreeSet::new(); for p in 0..self.index.pieces.len() { - for e in self.listed(c, p) { + for e in self.http_listed(c, p) { if seen.insert(e.content_hash) { let env = self.objects.get(&e.content_hash).ok_or_else(|| CatalogError(format!("object {} is not held", crate::hex(&e.content_hash))))?; out.push(SeqObject::from_envelope(env).map_err(|r| CatalogError(r.to_string()))?); @@ -182,8 +200,9 @@ impl CompView<'_> { fn wai_rates(&self, c: usize) -> (u64, u64) { let items: Vec<(u64, u64)> = (0..self.index.pieces.len()) - .filter(|&p| self.index.plan[c][p] != FLOOR_ONLY) - .map(|p| (self.listed(c, p).iter().map(|e| e.byte_len).sum(), self.index.pieces[p].duration as u64)) + .map(|p| (self.http_listed(c, p), p)) + .filter(|(l, _)| !l.is_empty()) + .map(|(l, p)| (l.iter().map(|e| e.byte_len).sum(), self.index.pieces[p].duration as u64)) .collect(); rates(&items, self.tb) } @@ -298,8 +317,9 @@ pub fn render_wai_media(v: &CompView, c: usize) -> Result if seg.n != base.byte_len { return Err(CatalogError(format!("object {} is not {} bytes", crate::hex(&base.content_hash), base.byte_len))); } - seg.gap = v.index.plan[c][p] == FLOOR_ONLY; - for e in v.listed(c, p).into_iter().filter(|e| e.role == Role::Refinement) { + let listed = v.http_listed(c, p); + seg.gap = listed.is_empty(); + for e in listed.into_iter().filter(|e| e.role == Role::Refinement) { seg.layers.push(WaiLayer::of(&object(v, e)?).map_err(|r| CatalogError(r.to_string()))?); } segs.push(seg); @@ -355,11 +375,11 @@ pub fn render_dash_classic(v: &CompView) -> Result { pub fn render_dash_class(v: &CompView, c: usize) -> Result, CatalogError> { let Some((union, floor)) = v.wai_codecs(c)? else { return Ok(None) }; let class = v.class_id(c); - let top = (0..v.index.pieces.len()).filter(|&p| v.index.plan[c][p] != FLOOR_ONLY).map(|p| v.index.plan[c][p]).max().unwrap_or(0); + let top = (0..v.index.pieces.len()).filter(|&p| !v.http_listed(c, p).is_empty()).map(|p| v.index.plan[c][p]).max().unwrap_or(0); let mut reps = Vec::new(); for k in 0..=top { let ps: Vec<(usize, &EntryRec)> = (0..v.index.pieces.len()) - .filter_map(|p| v.listed(c, p).into_iter().find(|e| e.layer == k && (k > 0 || e.role == Role::Base)).map(|e| (p, e))) + .filter_map(|p| v.http_listed(c, p).into_iter().find(|e| e.layer == k && (k > 0 || e.role == Role::Base)).map(|e| (p, e))) .collect(); let (peak, _) = rates(&ps.iter().map(|(p, e)| (e.byte_len, v.index.pieces[*p].duration as u64)).collect::>(), v.tb); reps.push(Representation { @@ -394,27 +414,78 @@ fn tier_label(t: u8) -> Option<&'static str> { } } -/// `moq/catalog.json`: RFC 8785 canonical. `layer_lag_ms[k]` is the policy's lag of layer -/// `k`, and `default_deadline` its `defaults.deadline_ms`. -/// -/// One track per (layer, deadline), by layer, then by deadline ascending; a refinement -/// track `depends` on every track that carries an entry its entries refine, in track -/// order: the layer below at the same deadline in a package whose layers share their -/// piece's deadline, and never a track the catalog does not hold. A subscriber receives -/// every track a track depends on (§8.4), so a dependency is all of them. -pub fn render_moq_catalog(v: &CompView, layer_lag_ms: &[u64], default_deadline: u64) -> Result { +/// The domain of a track's receipt alias (§8.4). +pub const DOMAIN_RECEIPT_ALIAS: &[u8] = b"wai:pack-receipt-alias\x01"; + +/// A track's `wai_receipt_alias` (§8.4): the first eight bytes of `BLAKE3("wai:pack- +/// receipt-alias\x01" ‖ namespace ‖ 0x00 ‖ name)` as a little-endian integer, less than +/// `2^53`. Distinct for distinct tracks with overwhelming probability, so two packages +/// published under one key keep apart receipt chains. +pub fn receipt_alias(namespace: &str, name: &str) -> u64 { + let mut h = blake3::Hasher::new(); + h.update(DOMAIN_RECEIPT_ALIAS); + h.update(namespace.as_bytes()); + h.update(&[0]); + h.update(name.as_bytes()); + u64::from_le_bytes(h.finalize().as_bytes()[..8].try_into().expect("8 bytes")) & ((1 << 53) - 1) +} + +/// One MoQ track of a component's stage set (§8.4): its layer and deadline, its name, and +/// the entries it carries. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MoqTrack { + pub layer: u8, + pub deadline_ms: u32, + pub name: String, + pub alias: u64, + /// `deadline − layer_lag_ms[layer]` for a refinement track (§8.6); none for the base. + pub timeout_ms: Option, +} + +/// The media tracks of a component (§8.4): one per (layer, deadline) of its bases and +/// refinements, by layer, then deadline ascending; named `/l`, or `/l@` for +/// a refinement whose deadline is not `default_deadline`. +pub fn moq_tracks(v: &CompView, namespace: &str, layer_lag_ms: &[u64], default_deadline: u64) -> Vec { + let id = v.id(); + let mut keys: Vec<(u8, u32)> = v.index.entries.iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)).map(|e| (e.layer, e.deadline_ms)).collect(); + keys.sort_unstable(); + keys.dedup(); + keys.into_iter() + .map(|(k, d)| { + let name = if k == 0 || d as u64 == default_deadline { format!("{id}/l{k}") } else { format!("{id}/l{k}@{d}") }; + MoqTrack { + layer: k, + deadline_ms: d, + alias: receipt_alias(namespace, &name), + timeout_ms: (k > 0).then(|| (d as u64).saturating_sub(layer_lag_ms.get(k as usize).copied().unwrap_or(0))), + name, + } + }) + .collect() +} + +/// `moq/catalog.json` (§8.4): RFC 8785 canonical, `{"tracks": [...], "version": 1}`. The +/// component's bases and refinements are a stage set of moq-streaming-format §4.1, written +/// by `wai::staged::moq::SetTrack`: one track per (layer, deadline) of namespace +/// `namespace` (the asset id), each with `wai_staged` (set the component's id, its layer, +/// `lead_ms` null: the package is on demand), `isLive` false, `role` video, `renderGroup` +/// 1, `depends` every track carrying an entry below one of its own along its chain to the +/// base (so a track of each layer below, none at or above), `wai_capabilities`, +/// `wai_determinism`, receipts declared with an alias ([`receipt_alias`]) and a receipts +/// track of its own, `/receipts`; a refinement track carries +/// `wai_delivery_timeout_ms` (§8.6), and the base track `altGroup` 1, which it shares with +/// the floor, a `cmaf` track. The receipts tracks follow, in their media tracks' order. +/// The result passes §4.1's catalog check. +pub fn render_moq_catalog(v: &CompView, namespace: &str, layer_lag_ms: &[u64], default_deadline: u64) -> Result { + use wai::staged::moq::{receipts_track_json, SetTrack, StagedTrack}; let id = v.id(); let ix = v.index; let staged = |e: &EntryRec| matches!(e.role, Role::Base | Role::Refinement); - // The tracks, in order, and each staged entry's track. - let mut keys: Vec<(u8, u32)> = ix.entries.iter().filter(|e| staged(e)).map(|e| (e.layer, e.deadline_ms)).collect(); - keys.sort_unstable(); - keys.dedup(); - let name_of = |(k, d): (u8, u32)| if k == 0 || d as u64 == default_deadline { format!("{id}/l{k}") } else { format!("{id}/l{k}@{d}") }; - let track_of = |e: &EntryRec| keys.iter().position(|&x| x == (e.layer, e.deadline_ms)); - let mut tracks: Vec = Vec::new(); - for (ti, &(k, d)) in keys.iter().enumerate() { - let on: Vec<&EntryRec> = ix.entries.iter().filter(|e| staged(e) && (e.layer, e.deadline_ms) == (k, d)).collect(); + let tracks = moq_tracks(v, namespace, layer_lag_ms, default_deadline); + let track_of = |e: &EntryRec| tracks.iter().position(|t| (t.layer, t.deadline_ms) == (e.layer, e.deadline_ms)); + let mut out: Vec = Vec::new(); + for (ti, t) in tracks.iter().enumerate() { + let on: Vec<&EntryRec> = ix.entries.iter().filter(|e| staged(e) && (e.layer, e.deadline_ms) == (t.layer, t.deadline_ms)).collect(); let mut caps: Vec = Vec::new(); for e in &on { let c = ix.capability(e).to_string(); @@ -422,43 +493,61 @@ pub fn render_moq_catalog(v: &CompView, layer_lag_ms: &[u64], default_deadline: caps.push(c); } } - let weakest = on.iter().map(|e| e.prefix_tier).min().unwrap_or(0); - let mut t = json!({ - "name": name_of((k, d)), - "packaging": "wai", - "priority": PRIORITY_BASE + PRIORITY_STEP * k as u64, - "renderGroup": 1, - "wai_capabilities": caps, - "wai_layer": k, - "wai_receipts": false, - }); - if let Some(l) = tier_label(weakest) { - t["wai_determinism"] = json!(l); - } - if k == 0 { - if v.floor.is_some() { - t["altGroup"] = json!(1); - } - } else { - let mut deps: Vec = Vec::new(); - for e in &on { - let below = ix.entries.get(e.refines as usize).ok_or_else(|| CatalogError("a refinement refines no entry".into()))?; - let dt = track_of(below).filter(|&x| x < ti).ok_or_else(|| CatalogError("a refinement refines an entry on no earlier track".into()))?; + // Every track carrying an entry below one of this track's, down its chain. + let mut deps: Vec = Vec::new(); + for e in &on { + let mut cur = *e; + while cur.role == Role::Refinement { + cur = ix.entries.get(cur.refines as usize).ok_or_else(|| CatalogError("a refinement refines no entry".into()))?; + let dt = track_of(cur).filter(|&x| x < ti).ok_or_else(|| CatalogError("a refinement refines an entry on no earlier track".into()))?; if !deps.contains(&dt) { deps.push(dt); } } - deps.sort_unstable(); - t["depends"] = json!(deps.iter().map(|&x| name_of(keys[x])).collect::>()); - let lag = layer_lag_ms.get(k as usize).copied().unwrap_or(0); - t["wai_delivery_timeout_ms"] = json!((d as u64).saturating_sub(lag)); } - tracks.push(t); + deps.sort_unstable(); + let weakest = on.iter().map(|e| e.prefix_tier).min().unwrap_or(0); + let st = SetTrack { + namespace: namespace.to_string(), + name: t.name.clone(), + staged: StagedTrack { set: id.to_string(), layer: t.layer, lead_ms: None }, + is_live: false, + target_latency_ms: None, + role: Some("video".into()), + render_group: Some(1), + depends: deps.iter().map(|&x| tracks[x].name.clone()).collect(), + capabilities: caps, + determinism: tier_label(weakest).map(str::to_string), + receipts: true, + receipt_alias: Some(t.alias), + receipts_track: Some(format!("{}/receipts", t.name)), + }; + let mut j = st.to_catalog_json(); + if let Some(to) = t.timeout_ms { + j["wai_delivery_timeout_ms"] = json!(to); + } + if t.layer == 0 && v.floor.is_some() { + j["altGroup"] = json!(1); + } + out.push(j); } if let Some(f) = &v.floor { - tracks.push(json!({"altGroup": 1, "codec": f.codec, "height": f.h, "name": format!("{id}/floor"), "packaging": "cmaf", "renderGroup": 1, "width": f.w})); + out.push(json!({"altGroup": 1, "codec": f.codec, "height": f.h, "name": format!("{id}/floor"), "namespace": namespace, "packaging": "cmaf", "renderGroup": 1, "width": f.w})); } - wai::jcs::jcs(&json!({"tracks": tracks, "version": 1})).map(|s| s + "\n").map_err(|e| CatalogError(format!("moq/catalog.json: {e}"))) + for t in &tracks { + out.push(receipts_track_json(namespace, &format!("{}/receipts", t.name), false)); + } + let catalog = json!({"tracks": out, "version": 1}); + let aliases: std::collections::BTreeSet = tracks.iter().map(|t| t.alias).collect(); + if aliases.len() != tracks.len() { + return Err(CatalogError("two tracks' receipt aliases are equal".into())); + } + for (set, verdict) in wai::staged::moq::check_catalog_in(&catalog, Some(namespace)) { + if let Err(r) = verdict { + return Err(CatalogError(format!("moq/catalog.json: set {set} fails moq-streaming-format §4.1 ({})", r.code()))); + } + } + wai::jcs::jcs(&catalog).map(|s| s + "\n").map_err(|e| CatalogError(format!("moq/catalog.json: {e}"))) } /// `stages/.json`: RFC 8785 canonical. A start, a piece number or a length past @@ -487,7 +576,7 @@ pub fn render_stage_view(v: &CompView, c: usize, policy_digest: &[u8; 32]) -> Re } let class = v.class_id(c); let mut comp = json!({"pieces": pieces}); - if (0..v.index.pieces.len()).any(|p| !v.listed(c, p).is_empty()) { + if v.http_lists(c) { comp["media_playlist"] = json!(format!("../hls/{class}/wai-{}.m3u8", v.id())); } let mut components = serde_json::Map::new(); @@ -497,8 +586,8 @@ pub fn render_stage_view(v: &CompView, c: usize, policy_digest: &[u8; 32]) -> Re .map_err(|e| CatalogError(format!("stages/{class}.json: {e}"))) } -/// Every catalog file of a one-component package, by path. -pub fn render_all(v: &CompView, layer_lag_ms: &[u64], default_deadline: u64, policy_digest: &[u8; 32]) -> Result>, CatalogError> { +/// Every catalog file of a one-component package, by path, under `policy`. +pub fn render_all(v: &CompView, policy: &crate::policy::Policy, policy_digest: &[u8; 32]) -> Result>, CatalogError> { let mut out: BTreeMap> = BTreeMap::new(); let id = v.id().to_string(); if v.floor.is_none() { @@ -507,7 +596,7 @@ pub fn render_all(v: &CompView, layer_lag_ms: &[u64], default_deadline: u64, pol out.insert("hls/classic/master.m3u8".into(), render_classic_master(v).into_bytes()); out.insert(format!("hls/classic/floor-{id}.m3u8"), render_floor_media(v).into_bytes()); out.insert("dash/classic.mpd".into(), render_dash_classic(v)?.into_bytes()); - out.insert("moq/catalog.json".into(), render_moq_catalog(v, layer_lag_ms, default_deadline)?.into_bytes()); + out.insert("moq/catalog.json".into(), render_moq_catalog(v, &policy.asset_id, &policy.layer_lag_ms, policy.deadline_ms)?.into_bytes()); for c in 0..v.index.classes.len() { let class = v.class_id(c).to_string(); out.insert(format!("hls/{class}/master.m3u8"), render_class_master(v, c)?.into_bytes()); @@ -523,6 +612,12 @@ pub fn render_all(v: &CompView, layer_lag_ms: &[u64], default_deadline: u64, pol Ok(out) } +/// The capability of a component's default base rung: its first base rung in ladder order +/// (§5.7), which its HTTP views carry (§8.3). +pub fn default_base_cap(policy: &crate::policy::Policy, comp: &str) -> String { + policy.ladder.iter().find(|r| r.component == comp && r.role == crate::policy::Role::Base).map(|r| r.capability.clone()).unwrap_or_default() +} + #[cfg(test)] mod tests { use super::*; @@ -535,9 +630,19 @@ mod tests { h: 36, objects, floor: Some(FloorView { codec: "av01.0.00M.08".into(), w: 64, h: 36, init: "floor/v/init.mp4".into(), fragments: vec![("floor/v/a.m4s".into(), 2000), ("floor/v/b.m4s".into(), 1000)] }), + base_cap: "wai.video.int_motion".into(), } } + fn moq(v: &CompView) -> serde_json::Value { + serde_json::from_str(&render_moq_catalog(v, "asset", &[0, 200, 400], 1500).unwrap()).unwrap() + } + + /// The media tracks of a catalog, without the receipts tracks. + fn media_tracks(m: &serde_json::Value) -> Vec { + m["tracks"].as_array().unwrap().iter().filter(|t| t["packaging"] != "wai-receipts").cloned().collect() + } + /// A class listing layers: the codecs union carries the refinement after `+`; each /// listed layer is a `#WAI-LAYER` line http-delivery's parser reads, after its piece's /// segment; the WAI and floor media playlists are adjacent; and the DASH, MoQ and @@ -576,13 +681,20 @@ mod tests { let dash = render_dash_class(&v, 0).unwrap().unwrap(); assert!(dash.contains(" = moq["tracks"].as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); + let cat = moq(&v); + let tracks = media_tracks(&cat); + let names: Vec<&str> = tracks.iter().map(|t| t["name"].as_str().unwrap()).collect(); assert_eq!(names, vec!["v/l0", "v/l1", "v/l2", "v/floor"]); - assert_eq!(moq["tracks"][1]["depends"], serde_json::json!(["v/l0"])); - assert_eq!(moq["tracks"][1]["wai_delivery_timeout_ms"], 1300); - assert_eq!(moq["tracks"][2]["wai_delivery_timeout_ms"], 1100); - assert!(moq["tracks"][2]["priority"].as_u64() > moq["tracks"][1]["priority"].as_u64(), "a refinement after the track it depends on"); + assert_eq!(tracks[1]["depends"], serde_json::json!(["v/l0"])); + assert_eq!(tracks[2]["depends"], serde_json::json!(["v/l0", "v/l1"]), "a track of every layer below (moq-streaming-format §4.1)"); + assert_eq!(tracks[1]["wai_delivery_timeout_ms"], 1300); + assert_eq!(tracks[2]["wai_delivery_timeout_ms"], 1100); + assert_eq!(tracks[2]["wai_staged"], serde_json::json!({"set": "v", "layer": 2, "lead_ms": null})); + // Each media track has a receipts track of its own and an alias no other has. + let receipts: Vec<&str> = cat["tracks"].as_array().unwrap().iter().filter(|t| t["packaging"] == "wai-receipts").map(|t| t["name"].as_str().unwrap()).collect(); + assert_eq!(receipts, vec!["v/l0/receipts", "v/l1/receipts", "v/l2/receipts"]); + assert_eq!(tracks[0]["wai_receipt_alias"], receipt_alias("asset", "v/l0")); + assert!(wai::staged::moq::check_catalog_in(&cat, Some("asset")).iter().all(|(_, r)| r.is_ok())); let stages: serde_json::Value = serde_json::from_str(&render_stage_view(&v, 1, &[0; 32]).unwrap()).unwrap(); let p0 = &stages["components"]["v"]["pieces"][0]["stages"]; assert_eq!(p0.as_array().unwrap().len(), 2); @@ -601,13 +713,13 @@ mod tests { ix.check().unwrap(); let objects: BTreeMap<[u8; 32], Vec> = objs.iter().map(|o| (crate::objects::object_hash(o), o.clone())).collect(); let v = view(&ix, &objects); - let moq: serde_json::Value = serde_json::from_str(&render_moq_catalog(&v, &[0, 200, 400], 1500).unwrap()).unwrap(); - let tracks = moq["tracks"].as_array().unwrap(); + let cat = moq(&v); + let tracks = media_tracks(&cat); let names: Vec<&str> = tracks.iter().map(|t| t["name"].as_str().unwrap()).collect(); assert_eq!(names, vec!["v/l0", "v/l1@800", "v/l2", "v/floor"]); assert_eq!(tracks[1]["depends"], serde_json::json!(["v/l0"])); - assert_eq!(tracks[2]["depends"], serde_json::json!(["v/l1@800"])); - for t in tracks { + assert_eq!(tracks[2]["depends"], serde_json::json!(["v/l0", "v/l1@800"])); + for t in &tracks { for d in t["depends"].as_array().into_iter().flatten() { assert!(names.contains(&d.as_str().unwrap()), "{d} is a track of the catalog"); } @@ -627,7 +739,6 @@ mod tests { let objects: BTreeMap<[u8; 32], Vec> = objs.iter().map(|o| (crate::objects::object_hash(o), o.clone())).collect(); let v = view(&ix, &objects); assert!(render_stage_view(&v, 0, &[0; 32]).is_err()); - assert!(render_all(&v, &[0, 200, 400], 1500, &[0; 32]).is_err()); } #[test] diff --git a/wai/wai-pack/src/conformance.rs b/wai/wai-pack/src/conformance.rs index aee17c136..31408f50d 100644 --- a/wai/wai-pack/src/conformance.rs +++ b/wai/wai-pack/src/conformance.rs @@ -513,3 +513,113 @@ pub fn check(dir: &Path) -> std::io::Result> { out.extend(crate::golden::check(dir)?); Ok(out) } + +/// One line of `wai-pack stage-json` (the staged differential, `stage_diff.py`): the +/// result of the line's `op`, or `{"error": …}`. +/// +/// - `refine` `{h, w, q, below, src}`: the WIR1 layer that brings each frame of `below` +/// toward `src` (§5.4): `{"payload": hex, "recon": [SHA-256 of each frame]}`. +/// - `still` `{w, h, rgb}`: the still (§5.7), `{"png": hex}`, and its decode's SHA-256. +/// - `plan` `{policy, pieces: [{start, duration, kind}], layer_bytes: [[…]]}`: the policy's +/// verdict, and per piece its rules and moments, its ladder's rung ids, its deadline and +/// each class's plan (§5.7, §8.1). +/// - `deliver` `{policy, class_plan, published: [{piece, start, duration, at_frame, +/// layer, deadline_ms, bytes}], plan: {rtt_us, bytes_per_ms, chunk_bytes, enc_us, +/// startup_us, layer_cap, fifo}}`: the simulated delivery (§8.9). +pub fn stage_json(v: &Value) -> Value { + match stage_op(v) { + Ok(r) => r, + Err(e) => json!({"error": e}), + } +} + +fn u64_of(v: &Value, k: &str) -> Result { + v[k].as_u64().ok_or_else(|| format!("{k}: not an unsigned integer")) +} + +fn bytes_of(v: &Value) -> Result, String> { + v.as_array().ok_or("not an array")?.iter().map(|x| x.as_u64().filter(|b| *b < 256).map(|b| b as u8).ok_or_else(|| "not a byte".to_string())).collect() +} + +fn frames_of(v: &Value) -> Result>, String> { + v.as_array().ok_or("frames: not an array")?.iter().map(bytes_of).collect() +} + +fn stage_op(v: &Value) -> Result { + match v["op"].as_str() { + Some("refine") => { + let (h, w, q) = (u64_of(v, "h")? as u32, u64_of(v, "w")? as u32, u64_of(v, "q")? as u32); + let (payload, recon) = crate::encode::encode_refinement(&frames_of(&v["below"])?, &frames_of(&v["src"])?, h, w, q)?; + Ok(json!({"payload": hex(&payload), "recon": recon.iter().map(|f| sha256_hex(f)).collect::>()})) + } + Some("still") => { + let (w, h) = (u64_of(v, "w")? as u32, u64_of(v, "h")? as u32); + let rgb = bytes_of(&v["rgb"])?; + if rgb.len() as u64 != 3 * w as u64 * h as u64 { + return Err("rgb: not 3·w·h bytes".into()); + } + let png = crate::png::encode(&rgb, w, h); + let (_, _, back) = crate::png::decode(&png)?; + Ok(json!({"png": hex(&png), "decoded_sha256": sha256_hex(&back)})) + } + Some("plan") => { + let text = v["policy"].as_str().ok_or("policy: not a string")?; + let p = match Policy::parse(text) { + Ok(p) => p, + Err(e) => return Ok(json!({"verdict": e.code()})), + }; + let comp = &p.components[0]; + let mut out = Vec::new(); + for (pc, lb) in v["pieces"].as_array().ok_or("pieces")?.iter().zip(v["layer_bytes"].as_array().ok_or("layer_bytes")?) { + let kind = crate::classify::PieceKind::from_code(u64_of(pc, "kind")? as u8).ok_or("kind")?; + let (start, duration) = (u64_of(pc, "start")?, u64_of(pc, "duration")?); + let pp = crate::plan::plan_piece(&p, comp.timebase, start, duration, kind); + let ladder = crate::plan::piece_ladder(&p, &comp.id, &pp).ok_or("no base")?; + let mut ids = vec![ladder.base.id.clone()]; + ids.extend(ladder.refinements.iter().map(|r| r.id.clone())); + ids.extend(ladder.floors.iter().map(|r| r.id.clone())); + let lb: Vec = lb.as_array().ok_or("layer_bytes")?.iter().map(|x| x.as_u64().ok_or("a byte count")).collect::>()?; + let plan: Vec = p.classes.iter().map(|c| crate::plan::class_plan(c, &lb, pp.max_layer, duration, comp.timebase)).collect(); + out.push(json!({"rules": pp.rules, "moments": pp.moments, "ladder": ids, "deadline_ms": pp.deadline_ms, "class_plan": plan})); + } + Ok(json!({"verdict": "ok", "pieces": out})) + } + Some("deliver") => { + let text = v["policy"].as_str().ok_or("policy: not a string")?; + let p = Policy::parse(text).map_err(|e| e.code().to_string())?; + let row = bytes_of(&v["class_plan"])?; + let mut published = Vec::new(); + for x in v["published"].as_array().ok_or("published")? { + let env = vec![0u8; u64_of(x, "bytes")? as usize]; + published.push(crate::live::Published { + piece: u64_of(x, "piece")?, + start: u64_of(x, "start")?, + duration: u64_of(x, "duration")?, + at_frame: u64_of(x, "at_frame")?, + layer: u64_of(x, "layer")? as u8, + role: if u64_of(x, "layer")? == 0 { wai::stage_index::Role::Base } else { wai::stage_index::Role::Refinement }, + capability: String::new(), + deadline_ms: u64_of(x, "deadline_ms")? as u32, + hash: crate::objects::object_hash(&env), + envelope: env, + }); + } + let q = &v["plan"]; + let plan = crate::delivery::DeliveryPlan { + rtt_us: u64_of(q, "rtt_us")?, + bytes_per_ms: u64_of(q, "bytes_per_ms")?, + chunk_bytes: u64_of(q, "chunk_bytes")?, + enc_us: u64_of(q, "enc_us")?, + startup_us: u64_of(q, "startup_us")?, + layer_cap: q["layer_cap"].as_u64().map(|c| c as u8), + schedule: if q["fifo"].as_bool() == Some(true) { wai::stage_measure::sim::Schedule::Fifo } else { wai::stage_measure::sim::Schedule::StrictPriority }, + }; + let key = ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]); + let d = crate::delivery::simulate(&published, &p, &row, &plan, &key)?; + let mut j = d.to_json(); + j.as_object_mut().expect("an object").remove("plan"); + Ok(j) + } + _ => Err("op: refine, still, plan or deliver".into()), + } +} diff --git a/wai/wai-pack/src/delivery.rs b/wai/wai-pack/src/delivery.rs new file mode 100644 index 000000000..1b00738d2 --- /dev/null +++ b/wai/wai-pack/src/delivery.rs @@ -0,0 +1,247 @@ +//! A package's delivery over MoQ, simulated ([packaging] §8.9): the staged-delivery +//! simulator of staged-measurement §10 and Appendix B (`wai::stage_measure::sim`), run +//! over what a live packer publishes and what a class subscribes to. This module adds no +//! simulator of its own: it states the model and the plan, runs the one simulator, and +//! reads its presentations per piece. +//! +//! - **Model.** Each object a class lists of each piece (§8.1; a piece the class takes +//! as floor only contributes none), on the clip's frames as units: base `p` = the +//! piece, its layer, `units` the piece's frames, `bytes` the whole envelope, and its +//! capture time the instant it is published: the live packer publishes a piece's base +//! when the frame that closes the piece has arrived (frame `f` at `f · frame_us`), plus +//! `enc_us`, and layer `k` `layer_lag_ms[k]` after its base. Each refinement takes the +//! delivery timeout of its track, `(deadline_ms − layer_lag_ms[k]) · 1000` (§8.6). +//! - **Plan.** Live; the link's round trip, rate and chunk; strict priority, which is +//! §8.5's order (the base track's publisher priority below every layer's, a layer's +//! no greater than any above it); each (piece, layer) its own subgroup; no base +//! timeout; the subscriber's layer cap; one decode worker taking bases first, each +//! decode one microsecond (the simulation is of the link); playout from `startup_us`, +//! and a refinement that arrives after its piece's instant discarded (§8.7). +//! - **Outcome.** Per piece, at its first frame's presentation instant (`startup_us + +//! start · frame_us`): the highest layer presented by then, or none where even the base +//! arrived later (a stall, which §8.7's rule is there to prevent); the base's arrival; +//! and the layers dropped on the way. +//! +//! The publisher seals each track's group (a piece) with a jwp-receipts group receipt, +//! on the track's receipt chain (`wai_receipt_alias`, §8.4), and a subscriber accepts +//! every delivered object by its Merkle proof; an object dropped on the way is accounted +//! by inclusion alone (jwp-receipts §4.4), never as delivered. +//! +//! Every figure is simulated, labelled so, and never a measurement of a network. +//! +//! [packaging]: ../../../extensions/packaging.md + +use crate::catalog; +use crate::live::Published; +use crate::policy::Policy; +use ed25519_dalek::SigningKey; +use serde_json::{json, Value}; +use std::collections::BTreeMap; +use wai::stage_index::FLOOR_ONLY; +use wai::stage_measure::sim::{self, Carriage, DecodeOrder, LateRefinement, Mode, Model, ObjState, Plan, Schedule, SimObject}; + +/// The link, the encoder's delay and the subscriber of a simulated delivery. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeliveryPlan { + pub rtt_us: u64, + pub bytes_per_ms: u64, + pub chunk_bytes: u64, + /// From a piece's last needed frame to its base's publication. + pub enc_us: u64, + /// From the first frame's capture to its presentation. + pub startup_us: u64, + /// The highest layer the subscriber subscribes to (moq-streaming-format §4.1, + /// *Capping*); `None`: every layer track. + pub layer_cap: Option, + /// The sender's schedule: `StrictPriority` is §8.5's. `Fifo` is not a conforming + /// sender; it is here to show what §8.5 prevents. + pub schedule: Schedule, +} + +/// One piece's outcome. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PieceOutcome { + pub piece: u64, + /// The piece's first frame's presentation instant. + pub due_us: u64, + /// The highest layer presented at that instant; `None` where the base arrived later + /// (or the class takes the piece as floor only: `floor_only`). + pub presented: Option, + pub floor_only: bool, + pub base_arrival_us: Option, + /// The layers of the piece the link dropped or abandoned. + pub dropped: Vec, +} + +/// A simulated delivery. +#[derive(Debug, Clone)] +pub struct Delivery { + pub model: Model, + pub plan: Plan, + /// `None` where the class lists nothing of any piece: nothing is sent. + pub result: Option, + pub pieces: Vec, + /// Per object of the model, its track's group receipt and its own receipt. + pub receipts: Vec<(wai::jwp_delivery::GroupReceipt, wai::jwp_delivery::ObjectReceipt)>, +} + +impl Delivery { + /// The highest layer presented of each piece, `0xFF` for none: the vector a test + /// commits. + pub fn presented(&self) -> Vec { + self.pieces.iter().map(|p| p.presented.unwrap_or(FLOOR_ONLY)).collect() + } + + /// The outcome as JSON, labelled simulated, with its plan. + pub fn to_json(&self) -> Value { + json!({ + "label": "simulated", + "plan": self.plan.to_json(), + "pieces": self.pieces.iter().map(|p| json!({ + "piece": p.piece, "due_us": p.due_us, "presented": p.presented, "floor_only": p.floor_only, + "base_arrival_us": p.base_arrival_us, "dropped": p.dropped, + })).collect::>(), + "delivered": self.result.as_ref().map_or(0, |r| r.delivered), + "dropped": self.result.as_ref().map_or(0, |r| r.dropped), + "abandoned": self.result.as_ref().map_or(0, |r| r.abandoned), + "never_sent_capped": self.result.as_ref().map_or(0, |r| r.never_sent_capped), + }) + } +} + +/// `us` microseconds after 1970-01-01T00:00:00Z, as RFC 3339 to the microsecond: a group +/// receipt's start and end on the simulation's clock. +fn rfc3339(us: u64) -> String { + let (s, frac) = (us / 1_000_000, us % 1_000_000); + let (days, rem) = (s / 86_400, s % 86_400); + format!("1970-01-{:02}T{:02}:{:02}:{:02}.{frac:06}Z", 1 + days.min(30), rem / 3600, rem % 3600 / 60, rem % 60) +} + +/// Simulate the delivery to a subscriber of class `class` of what a live packer +/// published (`published`, in publication order) under `policy`, whose class plan per +/// piece is `class_plan` (the class's row of the windows' class plans, by absolute piece +/// number). `key` signs the group receipts. +pub fn simulate(published: &[Published], policy: &Policy, class_plan: &[u8], plan: &DeliveryPlan, key: &SigningKey) -> Result { + let comp = policy.components.first().ok_or("no component")?; + let tb = comp.timebase; + let frame_us = 1_000_000u64.checked_mul(tb.den as u64).filter(|x| x % tb.num as u64 == 0).map(|x| x / tb.num as u64).ok_or("a frame that is not a whole number of microseconds")?; + // §8.5: the base track's priority below every layer track's, each layer's no greater + // than any above it; strict priority by layer is that order. + let layers: Vec = (1..policy.layer_count()).map(|k| (catalog::PRIORITY_BASE + catalog::PRIORITY_STEP * k) as u8).collect(); + if !wai::staged::moq::priorities_ok(catalog::PRIORITY_BASE as u8, &layers) { + return Err("§8.5's priorities are not MoQ's order".into()); + } + let mut objects = Vec::new(); + let mut idx: Vec<&Published> = Vec::new(); + for p in published { + let listed = class_plan.get(p.piece as usize).copied().ok_or("a piece past the class plan")?; + if listed == FLOOR_ONLY || p.layer > listed { + continue; + } + let lag = policy.layer_lag_ms.get(p.layer as usize).copied().unwrap_or(0); + let at = p + .at_frame + .checked_mul(frame_us) + .and_then(|x| x.checked_add(plan.enc_us)) + .and_then(|x| x.checked_add(lag.checked_mul(1000)?)) + .ok_or("a time past 2^64 - 1")?; + objects.push(SimObject { + id: format!("p{}/l{}", p.piece, p.layer), + base: u32::try_from(p.piece).map_err(|_| "a piece past 2^32")?, + layer: p.layer, + from: p.start, + count: p.duration, + bytes: p.envelope.len() as u64, + capture_us: Some(at), + timeout_us: (p.layer > 0).then(|| (p.deadline_ms as u64).saturating_sub(lag) * 1000), + }); + idx.push(p); + } + let model = Model { unit_us: frame_us, objects }; + let sp = Plan { + mode: Mode::Live, + rtt_us: plan.rtt_us, + bytes_per_ms: plan.bytes_per_ms, + chunk_bytes: plan.chunk_bytes, + schedule: plan.schedule, + carriage: Carriage::Subgroup, + layer_cap: plan.layer_cap, + timeout_base_us: None, + timeout_refinement_us: None, + vod_window_us: None, + workers: 1, + decode_order: DecodeOrder::BaseFirst, + decode_base_us: 1, + decode_refinement_us: 1, + playout_start_us: Some(plan.startup_us), + late_refinement: LateRefinement::Discard, + }; + let result = if model.objects.is_empty() { None } else { Some(sim::simulate(&model, &sp).map_err(|e| e.to_string())?) }; + // Per piece, at its first frame's instant. + let mut pieces: BTreeMap = BTreeMap::new(); + for p in published { + let due = plan.startup_us + p.start * frame_us; + let floor_only = class_plan[p.piece as usize] == FLOOR_ONLY; + pieces.entry(p.piece).or_insert(PieceOutcome { piece: p.piece, due_us: due, presented: None, floor_only, base_arrival_us: None, dropped: Vec::new() }); + } + let events = result.as_ref().map_or(&[][..], |r| &r.objects[..]); + let presentations = result.as_ref().map_or(&[][..], |r| &r.presentations[..]); + for (o, ev) in model.objects.iter().zip(events) { + let po = pieces.get_mut(&(o.base as u64)).expect("a piece"); + if o.layer == 0 { + po.base_arrival_us = ev.arrived_us; + } + if matches!(ev.state, ObjState::Dropped | ObjState::Abandoned) { + po.dropped.push(o.layer); + } + } + for po in pieces.values_mut() { + let start = published.iter().find(|p| p.piece == po.piece).expect("a piece").start; + let at: Vec<&(u64, u16, u64)> = presentations.iter().filter(|(u, _, t)| *u == start && *t <= po.due_us).collect(); + if at.iter().any(|(_, l, _)| *l == 1) { + po.presented = at.iter().map(|(_, l, _)| (*l - 1) as u8).max(); + } + } + // The receipts: per track, per group, on the track's chain. + let tracks = |p: &Published| { + let name = if p.layer == 0 || p.deadline_ms as u64 == policy.deadline_ms { format!("{}/l{}", comp.id, p.layer) } else { format!("{}/l{}@{}", comp.id, p.layer, p.deadline_ms) }; + catalog::receipt_alias(&policy.asset_id, &name) + }; + let signer_id = wai::signer_id::did_key(&key.verifying_key().to_bytes()); + let mut heads: BTreeMap = BTreeMap::new(); + let mut receipts = Vec::with_capacity(idx.len()); + for (o, p) in model.objects.iter().zip(&idx) { + let alias = tracks(p); + let mut b = wai::jwp_delivery::GroupBuilder::default(); + b.add(p.hash, 0, "", None); + let at = o.capture_us.expect("live"); + let (g, mut objs) = b + .finish(key, &signer_id, alias, p.piece, &rfc3339(at), &rfc3339(at), heads.get(&alias).copied(), false) + .ok_or("a group receipt")?; + heads.insert(alias, g.link_hash()); + receipts.push((g, objs.remove(0))); + } + Ok(Delivery { model, plan: sp, result, pieces: pieces.into_values().collect(), receipts }) +} + +/// Pack the source `y4m` live under `policy_bytes` (one window) and simulate its delivery +/// to a subscriber of class `class` ([`simulate`]). +pub fn simulate_source(policy_bytes: &[u8], y4m: &[u8], class: &str, plan: &DeliveryPlan, key: &SigningKey) -> Result { + let (hd, frames) = crate::source::y4m::read_all(y4m).map_err(|e| e.to_string())?; + let signer_id = wai::signer_id::did_key(&key.verifying_key().to_bytes()); + let mut lp = crate::live::LivePacker::new(policy_bytes, hd.w, hd.h, crate::live::LiveOpts::new(key.clone(), signer_id)).map_err(|e| e.to_string())?; + let mut published = Vec::new(); + for f in &frames { + published.extend(lp.push(&f.rgb).map_err(|e| e.to_string())?); + } + let policy = lp.policy().clone(); + let (rest, windows, _) = lp.finish().map_err(|e| e.to_string())?; + published.extend(rest); + let ci = policy.classes.iter().position(|c| c.id == class).ok_or_else(|| format!("no class {class}"))?; + let mut row = Vec::new(); + for w in &windows { + let ix = wai::stage_index::StageIndex::parse(&w.index).map_err(|e| e.to_string())?; + row.extend_from_slice(&ix.plan[ci]); + } + simulate(&published, &policy, &row, plan, key) +} diff --git a/wai/wai-pack/src/encode.rs b/wai/wai-pack/src/encode.rs index 0c572bb62..a7dd4e099 100644 --- a/wai/wai-pack/src/encode.rs +++ b/wai/wai-pack/src/encode.rs @@ -69,10 +69,52 @@ pub fn choose_q(frames: &[Vec], h: u32, w: u32, p: &MotionParams) -> Result< unreachable!("the smallest step is always chosen when it is reached") } +/// The sum, over every sample, of the squared difference between two clips of RGB8 +/// frames (§6.6). +pub fn sse(a: &[Vec], b: &[Vec]) -> u64 { + a.iter().zip(b).flat_map(|(x, y)| x.iter().zip(y)).map(|(&x, &y)| (x as i64 - y as i64).unsigned_abs().pow(2)).sum() +} + +/// One refinement layer of a piece (§5.4): a `WIR1` of unit kind RGB8, one unit per frame, +/// the identity predictor, one table, at most 4095 table symbols, that brings each frame +/// of `below` (the stage the layer refines) toward its source frame in `src`, at step +/// `q`. This is `wai-rs`'s refinement encoder (`staged::wir1::encode`), the one `wai stage +/// refine` runs: the residual `rdiv(source − below, q)`, its table, its streams. Returns +/// the payload and the frames a sink reconstructs, which the encoder has decoded back. +pub fn encode_refinement(below: &[Vec], src: &[Vec], h: u32, w: u32, q: u32) -> Result<(Vec, Vec>), String> { + use wai::staged::wir1::{encode, Below, Next, Predictor, RefineParams, UnitKind}; + let belows: Vec = below.iter().map(|f| Below::U8(f)).collect(); + let targets: Vec = src.iter().map(|f| Below::U8(f)).collect(); + let p = RefineParams { q, predictor: Predictor::Identity, rate: (0, 0), table_per_unit: false, max_symbols: 4095 }; + let (payload, next) = encode(UnitKind::Rgb8, &belows, (h, w, 3), &targets, (h, w, 3), &p).map_err(|e| e.0)?; + let frames = next + .into_iter() + .map(|n| match n { + Next::U8(v) => Ok(v), + Next::I16(_) => Err("an RGB8 layer decoded to PCM".to_string()), + }) + .collect::, _>>()?; + Ok((payload, frames)) +} + #[cfg(test)] mod tests { use super::*; + /// A layer brings every frame closer to its source, and a finer step closer still. + #[test] + fn a_refinement_reduces_the_error() { + let src = clip(); + let base = encode(&src, 10, 12, &EncParams { block: 4, search: 1, mv_precision: 1, q: 24 }).unwrap(); + let (p1, r1) = encode_refinement(&base.recon, &src, 10, 12, 8).unwrap(); + let (_, r2) = encode_refinement(&r1, &src, 10, 12, 2).unwrap(); + assert_eq!(&p1[..4], b"WIR1"); + assert_eq!(sse(&base.recon, &src), base.sse); + assert!(sse(&r1, &src) < base.sse && sse(&r2, &src) < sse(&r1, &src), "{} {} {}", base.sse, sse(&r1, &src), sse(&r2, &src)); + // Step 1 reconstructs the source exactly. + assert_eq!(encode_refinement(&r1, &src, 10, 12, 1).unwrap().1, src); + } + fn clip() -> Vec> { (0..4).map(|t| (0..12 * 10 * 3).map(|i| ((i * 37 + t * 11) % 251) as u8).collect()).collect() } diff --git a/wai/wai-pack/src/fixture.rs b/wai/wai-pack/src/fixture.rs index 931aef49b..2fb1c1acb 100644 --- a/wai/wai-pack/src/fixture.rs +++ b/wai/wai-pack/src/fixture.rs @@ -21,7 +21,7 @@ //! v ^= v >> 16` (wrapping). Chroma is constant per shot. /// The fixtures this module generates. -pub const NAMES: &[&str] = &["cuts-01", "kinds-01", "video-base-01"]; +pub const NAMES: &[&str] = &["cuts-01", "kinds-01", "video-base-01", "video-staged-01"]; /// The fixtures the scene-cut, encode and step-choice corpora cover. pub const SCENECUT_NAMES: &[&str] = &["cuts-01", "kinds-01"]; @@ -119,6 +119,19 @@ fn spans(name: &str) -> Option<(u32, u32, Vec)> { s(40, ramp(16, 160, 1, 2, 1, true, 13, 128, 128)), ]) } + // The staged golden: a moving shot, the bumper, a still shot (no motion, no + // noise), the bumper again (the same frames both times), and a second moving + // shot. + "video-staged-01" => { + let bumper = Shot::Blocks { levels: [40, 120, 200], bw: 8, bh: 6, speed: 1, cb: 100, cr: 160 }; + (64, 36, vec![ + s(50, ramp(20, 200, 1, 1, 1, true, 21, 110, 140)), + local(25, bumper), + s(40, ramp(60, 180, 2, 1, 0, false, 0, 140, 116)), + local(25, bumper), + s(60, ramp(30, 120, 1, 2, 1, true, 22, 150, 120)), + ]) + } _ => return None, }) } @@ -173,7 +186,7 @@ mod tests { for n in NAMES { let b = generate(n).unwrap(); let (hd, frames) = crate::source::y4m::read_all(&b).unwrap(); - let dims = if *n == "video-base-01" { (64, 36) } else { (32, 18) }; + let dims = if n.starts_with("video-") { (64, 36) } else { (32, 18) }; assert_eq!((hd.w, hd.h, hd.rate), (dims.0, dims.1, Some((25, 1))), "{n}"); assert!(frames.len() >= 200, "{n}"); } @@ -181,6 +194,9 @@ mod tests { let (_, f) = crate::source::y4m::read_all(&generate("video-base-01").unwrap()).unwrap(); assert_eq!(f[60..85], f[135..160]); assert_ne!(f[60..85], f[85..110]); + let (_, f) = crate::source::y4m::read_all(&generate("video-staged-01").unwrap()).unwrap(); + assert_eq!(f[50..75], f[115..140]); + assert!(f[75..115].iter().all(|x| *x == f[75]), "the still shot does not change"); assert!(generate("nope").is_none()); } } diff --git a/wai/wai-pack/src/golden.rs b/wai/wai-pack/src/golden.rs index 9a916f8c1..72986c601 100644 --- a/wai/wai-pack/src/golden.rs +++ b/wai/wai-pack/src/golden.rs @@ -31,7 +31,7 @@ use wai::stage_index::{self as wsix, ClassRec, EntryRec, Header, MediaClass, Pie use wai::wai_video_manifest::EnergyProvenance; /// The golden packs. -pub const PACKS: &[&str] = &["video-base-01"]; +pub const PACKS: &[&str] = &["video-base-01", "video-staged-01"]; type Files = BTreeMap>; @@ -573,8 +573,8 @@ pub fn reseal_as(files: &mut Files, key: &SigningKey, policy_json: Option<&[u8]> files.extend(frags); } let (w, h) = dims.unwrap_or((0, 0)); - let view = CompView { index: &ix, tb, w, h, objects: &objects, floor: floor_view }; - if let Ok(cats) = crate::catalog::render_all(&view, &policy.layer_lag_ms, policy.deadline_ms, &policy_digest) { + let view = CompView { index: &ix, tb, w, h, objects: &objects, floor: floor_view, base_cap: crate::catalog::default_base_cap(&policy, &comp.id) }; + if let Ok(cats) = crate::catalog::render_all(&view, &policy, &policy_digest) { files.extend(cats); } let parent = if ix.header.prev_index_digest != [0u8; 32] { Parent::LiveWindow([7u8; 32]) } else { Parent::None }; @@ -595,6 +595,9 @@ pub fn relist(files: &mut Files) { // ─────────────────────────────── catalogs ─────────────────────────────── +/// The namespace the catalog corpus's MoQ catalogs are written in. +pub const CATALOG_NAMESPACE: &str = "catalogs"; + /// MoQ catalogs (§8.4) of indexes no golden package has: layers on more than one /// deadline, so each refinement track's `depends` is read from what its entries refine. pub fn catalogs() -> (Files, Value) { @@ -619,11 +622,11 @@ pub fn catalogs() -> (Files, Value) { m(&mut ix); let objects: BTreeMap<[u8; 32], Vec> = objs.iter().map(|o| (objects::object_hash(o), o.clone())).collect(); let floor = FloorView { codec: "av01.0.00M.08".into(), w: 64, h: 36, init: "floor/v/init.mp4".into(), fragments: Vec::new() }; - let v = CompView { index: &ix, tb: crate::policy::Timebase { num: 25, den: 1 }, w: 8, h: 8, objects: &objects, floor: Some(floor) }; - let moq = render_moq_catalog(&v, &lag, deadline).expect("a catalog"); + let v = CompView { index: &ix, tb: crate::policy::Timebase { num: 25, den: 1 }, w: 8, h: 8, objects: &objects, floor: Some(floor), base_cap: "wai.video.int_motion".into() }; + let moq = render_moq_catalog(&v, CATALOG_NAMESPACE, &lag, deadline).expect("a catalog"); let path = format!("catalogs/{name}.wsix"); files.insert(path.clone(), ix.to_bytes()); - cases.push(json!({"index": path, "layer_lag_ms": lag, "default_deadline": deadline, "floor": {"codec": "av01.0.00M.08", "w": 64, "h": 36}, "moq": moq})); + cases.push(json!({"index": path, "namespace": CATALOG_NAMESPACE, "layer_lag_ms": lag, "default_deadline": deadline, "floor": {"codec": "av01.0.00M.08", "w": 64, "h": 36}, "moq": moq})); } (files, json!({"cases": cases})) } @@ -936,6 +939,104 @@ pub fn package_mutations(g: &Files, key: &SigningKey) -> Vec { out } +/// The staged golden, which the staged mutations alter. +pub const STAGED_PACK: &str = "video-staged-01"; + +/// Entry `k`'s prefix as the reference sink gives it: its piece's base decoded, and each +/// refinement up to `k` applied. +fn prefix_frames(f: &Files, ix: &StageIndex, k: usize) -> Vec> { + let p = ix.pieces.iter().position(|p| (p.first_entry as usize..(p.first_entry + p.n_entries as u32) as usize).contains(&k)).expect("a piece"); + let first = ix.pieces[p].first_entry as usize; + let obj = |i: usize| f[&objects::object_path(&ix.entries[i].content_hash)].clone(); + let (info, units) = wai::staged::decode_base(&obj(first), &BTreeMap::new(), None, None).expect("the golden's base decodes"); + let mut c = wai::staged::StageChain::new(info, units, wai::staged::ChainConfig { + bound: wai::staged::HoldBound { max_envelopes: 0, max_age_ms: 0 }, + cost_limit: None, + budget: u64::MAX, + record_budget: 0, + live: None, + }); + for i in first + 1..=k { + c.offer(&obj(i), 0, wai::staged::ReceiptEvidence::None).expect("the golden's layer applies"); + } + c.units().iter().map(|u| match &u.buf.samples { + wai::staged::Samples::U8(v) => v.clone(), + wai::staged::Samples::I16(_) => unreachable!("frames"), + }).collect() +} + +/// Staged packages a verifier must refuse (§11.3), each the staged golden altered and +/// re-sealed with its key: a refinement on another deadline than its plan's (§8.6); a +/// piece missing its top layer, and the static piece's still where the policy's rule now +/// gives it the default ladder (§5.7); a top layer coded again at another step, its +/// prefix digest left as it was; and the still's prefix digest of its one frame, not of +/// the frame held for the piece (§5.7, §6.5). +pub fn staged_mutations(g: &Files, key: &SigningKey) -> Vec { + let sealed = |mut f: Files| { + reseal(&mut f, key).expect("the alteration re-seals"); + f + }; + let mut out: Vec = Vec::new(); + let mut add = |name: &'static str, reason: &str, files: Files| out.push(PackageMutation { name, reason: reason.into(), files, empty_dirs: Vec::new(), unreadable_dirs: Vec::new() }); + let ix = golden_index(g); + let last = ix.pieces.len() - 1; + let top = |ix: &StageIndex, p: usize| ix.pieces[p].first_entry as usize + ix.piece_entries(p).iter().rposition(|e| e.role == Role::Refinement).expect("a layer"); + // The last piece's layers ride the event's deadline (800 ms): one on the default. + let mut f = g.clone(); + let mut x = golden_index(&f); + let k = top(&x, last); + assert_eq!(x.entries[k].deadline_ms, 800, "the event's deadline"); + x.entries[k].deadline_ms = 1500; + set_golden_index(&mut f, &x); + add("refinement-deadline", "index-policy-mismatch", sealed(f)); + // The last piece without its top layer: its entry and object gone, each class plan + // capped at the layer below. + let mut f = g.clone(); + let mut x = golden_index(&f); + let k = top(&x, last); + f.remove(&objects::object_path(&x.entries[k].content_hash)); + x.entries.remove(k); + x.pieces[last].n_entries -= 1; + let below = x.entries[k - 1].layer; + for row in x.plan.iter_mut() { + if row[last] != wsix::FLOOR_ONLY && row[last] > below { + row[last] = below; + } + } + set_golden_index(&mut f, &x); + add("layer-dropped", "index-policy-mismatch", sealed(f)); + // The static piece's rule giving it the default ladder: its still is no longer its + // ladder's base. + let mut f = g.clone(); + policy_edit(&mut f, "then = { ladder = [\"still\", \"f0\"] }", "then = { ladder = [\"b0\", \"r1\", \"f0\"] }"); + add("still-not-the-ladder", "index-policy-mismatch", sealed(f)); + // The last piece's top layer coded again at step 5 over the layer below: a well-formed + // layer whose output is not the prefix digest its entry states. + let mut f = g.clone(); + let x = golden_index(&f); + let k = top(&x, last); + let below = prefix_frames(&f, &x, k - 1); + let (_, frames) = crate::source::y4m::read_all(&crate::fixture::generate(STAGED_PACK).expect("the fixture")).expect("a Y4M"); + let pc = &x.pieces[last]; + let src: Vec> = frames[pc.start as usize..(pc.start + pc.duration as u64) as usize].iter().map(|fr| fr.rgb.clone()).collect(); + let (wd, ht) = (frames[0].w, frames[0].h); + let (payload, _) = crate::encode::encode_refinement(&below, &src, ht, wd, 5).expect("a layer encodes"); + let base = x.entries[pc.first_entry as usize].content_hash; + let env = objects::refinement_envelope("video", x.capability(&x.entries[k]), payload, &base, &x.entries[k - 1].content_hash, x.entries[k].layer); + replace_object(&mut f, k, env); + add("layer-replaced", "prefix-digest", sealed(f)); + // The still's prefix digest of its one frame, not of the frame held for its piece. + let mut f = g.clone(); + let mut x = golden_index(&f); + let k = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").expect("the still"); + let w = Wai::from_bytes(&f[&objects::object_path(&x.entries[k].content_hash)]).expect("an envelope"); + let (_, _, rgb) = crate::png::decode(&w.payload).expect("a still"); + x.entries[k].prefix_digest = crate::pack::clip_digest(&[rgb]); + set_golden_index(&mut f, &x); + add("still-held-digest", "prefix-digest", sealed(f)); + out +} + static CASE_DIRS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); fn set_unreadable(path: &Path, unreadable: bool) -> std::io::Result<()> { @@ -981,12 +1082,22 @@ pub fn package_case_verdict(files: &Files, empty_dirs: &[String], unreadable_dir /// removes, the directories it adds that hold nothing (`empty_dirs`) or cannot be read /// (`unreadable_dirs`), and its reason. pub fn package_overlays(g: &Files, key: &SigningKey) -> (Files, Value) { + overlays("package-mutations", MUTATED_PACK, g, package_mutations(g, key)) +} + +/// The staged mutations as overlays on the staged golden (`staged-mutations/`), in the +/// layout of [`package_overlays`]. +pub fn staged_overlays(g: &Files, key: &SigningKey) -> (Files, Value) { + overlays("staged-mutations", STAGED_PACK, g, staged_mutations(g, key)) +} + +fn overlays(dir: &str, pack: &str, g: &Files, mutations: Vec) -> (Files, Value) { let mut files = BTreeMap::new(); let mut cases = Vec::new(); - for m in package_mutations(g, key) { + for m in mutations { for (p, b) in &m.files { if g.get(p) != Some(b) { - files.insert(format!("package-mutations/{}/{p}", m.name), b.clone()); + files.insert(format!("{dir}/{}/{p}", m.name), b.clone()); } } let remove: Vec<&String> = g.keys().filter(|p| !m.files.contains_key(*p)).collect(); @@ -999,7 +1110,7 @@ pub fn package_overlays(g: &Files, key: &SigningKey) -> (Files, Value) { } cases.push(c); } - (files, json!({"pack": MUTATED_PACK, "cases": cases})) + (files, json!({"pack": pack, "cases": cases})) } /// A package case's files: the golden, less what it removes, with its overlay. @@ -1052,6 +1163,46 @@ pub fn v1_receipts() -> (Files, Value) { (files, json!({"cases": cases})) } +// ─────────────────────────────── delivery ─────────────────────────────── + +/// The delivery corpus's plans (§8.9, test 13): the staged golden packed live and sent to +/// a `tv` subscriber over a 40 bytes/ms link with chunks of 1200 bytes and 3.2 s of +/// start-up: under §8.5's priorities; without them (fifo); and capped at layer 1. +pub fn delivery_plans() -> Vec<(&'static str, crate::delivery::DeliveryPlan)> { + use wai::stage_measure::sim::Schedule; + let strict = crate::delivery::DeliveryPlan { rtt_us: 0, bytes_per_ms: 40, chunk_bytes: 1200, enc_us: 0, startup_us: 3_200_000, layer_cap: None, schedule: Schedule::StrictPriority }; + vec![ + ("strict", strict.clone()), + ("fifo", crate::delivery::DeliveryPlan { schedule: Schedule::Fifo, ..strict.clone() }), + ("capped", crate::delivery::DeliveryPlan { layer_cap: Some(1), ..strict }), + ] +} + +/// The class the delivery corpus's subscriber takes. +pub const DELIVERY_CLASS: &str = "tv"; + +/// `delivery/expected.json`: each plan of [`delivery_plans`] and its simulated outcome. +pub fn delivery(dir: &Path) -> std::io::Result { + let policy = std::fs::read(dir.join("packs").join(STAGED_PACK).join("policy.toml"))?; + let src = crate::fixture::generate(STAGED_PACK).ok_or_else(|| io("no fixture"))?; + let key = SigningKey::from_bytes(&[0x5a; 32]); + let mut cases = Vec::new(); + for (name, plan) in delivery_plans() { + let d = crate::delivery::simulate_source(&policy, &src, DELIVERY_CLASS, &plan, &key).map_err(io)?; + let mut outcome = d.to_json(); + let plan_json = outcome.as_object_mut().expect("an object").remove("plan").expect("a plan"); + cases.push(json!({ + "name": name, + "plan": {"rtt_us": plan.rtt_us, "bytes_per_ms": plan.bytes_per_ms, "chunk_bytes": plan.chunk_bytes, "enc_us": plan.enc_us, + "startup_us": plan.startup_us, "layer_cap": plan.layer_cap, "fifo": plan.schedule == wai::stage_measure::sim::Schedule::Fifo}, + "sim_plan": plan_json, + "presented": d.presented(), + "outcome": outcome, + })); + } + Ok(json!({"pack": STAGED_PACK, "class": DELIVERY_CLASS, "label": "simulated", "cases": cases})) +} + // ─────────────────────────────── regen and check ─────────────────────────────── fn write_files(dir: &Path, files: &Files) -> std::io::Result<()> { @@ -1071,6 +1222,10 @@ fn write_json(p: &Path, v: &Value) -> std::io::Result<()> { std::fs::write(p, s) } +fn tree_names(dir: &Path) -> std::io::Result> { + Ok(read_tree(dir)?.into_keys().collect()) +} + fn read_tree(dir: &Path) -> std::io::Result { wai::pin::read_dir_set(dir).map_err(io) } @@ -1111,6 +1266,18 @@ pub fn regen(dir: &Path) -> std::io::Result<()> { } write_files(dir, &files)?; write_json(&pm.join("expected.json"), &expected)?; + let dl = dir.join("delivery"); + std::fs::create_dir_all(&dl)?; + write_json(&dl.join("expected.json"), &delivery(dir)?)?; + let golden = read_tree(&dir.join("packs").join(STAGED_PACK).join("package"))?; + let key = read_key(&dir.join("packs").join(STAGED_PACK).join("key.hex"))?; + let (files, expected) = staged_overlays(&golden, &key); + let sm = dir.join("staged-mutations"); + if sm.exists() { + std::fs::remove_dir_all(&sm)?; + } + write_files(dir, &files)?; + write_json(&sm.join("expected.json"), &expected)?; Ok(()) } @@ -1184,25 +1351,39 @@ pub fn check(dir: &Path) -> std::io::Result> { if expected_held != want { out.push("catalogs/expected.json: the corpus and this implementation differ".into()); } - // The package mutations, read strictly (their dot-names and `*.sha256` files are - // cases), each refused with its reason. - let golden = read_tree(&dir.join("packs").join(MUTATED_PACK).join("package"))?; - let key = read_key(&dir.join("packs").join(MUTATED_PACK).join("key.hex"))?; - let mut held = crate::verify::read_strict(&dir.join("package-mutations")).map_err(io)?; - let expected_held: Value = serde_json::from_slice(&held.remove("expected.json").unwrap_or_default()).map_err(io)?; - let (files, want) = package_overlays(&golden, &key); - let files: Files = files.into_iter().map(|(k, v)| (k.trim_start_matches("package-mutations/").to_string(), v)).collect(); - same_tree("package-mutations", &files, &held, &mut out); - if expected_held != want { - out.push("package-mutations/expected.json: the corpus and this implementation differ".into()); + // The delivery cases (§8.9). + let held: Value = serde_json::from_slice(&std::fs::read(dir.join("delivery/expected.json"))?).map_err(io)?; + if held != delivery(dir)? { + out.push("delivery/expected.json: the corpus and this implementation differ".into()); } - let strings = |v: &Value| -> Vec { v.as_array().into_iter().flatten().filter_map(|p| p.as_str().map(String::from)).collect() }; - for c in want["cases"].as_array().into_iter().flatten() { - let name = c["name"].as_str().unwrap_or(""); - let files = apply_overlay(&golden, &held, name, &strings(&c["remove"])); - let v = package_case_verdict(&files, &strings(&c["empty_dirs"]), &strings(&c["unreadable_dirs"]))?; - if v != c["reason"] { - out.push(format!("package-mutations {name}: {v}, the corpus says {}", c["reason"])); + if tree_names(&dir.join("delivery"))? != ["expected.json"] { + out.push("delivery/: a file that is no case".into()); + } + // The package mutations, read strictly (their dot-names and `*.sha256` files are + // cases), each refused with its reason; then the staged ones. + for (sub, pack, f) in [ + ("package-mutations", MUTATED_PACK, package_overlays as fn(&Files, &SigningKey) -> (Files, Value)), + ("staged-mutations", STAGED_PACK, staged_overlays), + ] { + let golden = read_tree(&dir.join("packs").join(pack).join("package"))?; + let key = read_key(&dir.join("packs").join(pack).join("key.hex"))?; + let mut held = crate::verify::read_strict(&dir.join(sub)).map_err(io)?; + let expected_held: Value = serde_json::from_slice(&held.remove("expected.json").unwrap_or_default()).map_err(io)?; + let (files, want) = f(&golden, &key); + let prefix = format!("{sub}/"); + let files: Files = files.into_iter().map(|(k, v)| (k.trim_start_matches(prefix.as_str()).to_string(), v)).collect(); + same_tree(sub, &files, &held, &mut out); + if expected_held != want { + out.push(format!("{sub}/expected.json: the corpus and this implementation differ")); + } + let strings = |v: &Value| -> Vec { v.as_array().into_iter().flatten().filter_map(|p| p.as_str().map(String::from)).collect() }; + for c in want["cases"].as_array().into_iter().flatten() { + let name = c["name"].as_str().unwrap_or(""); + let files = apply_overlay(&golden, &held, name, &strings(&c["remove"])); + let v = package_case_verdict(&files, &strings(&c["empty_dirs"]), &strings(&c["unreadable_dirs"]))?; + if v != c["reason"] { + out.push(format!("{sub} {name}: {v}, the corpus says {}", c["reason"])); + } } } Ok(out) diff --git a/wai/wai-pack/src/lib.rs b/wai/wai-pack/src/lib.rs index f868e5bcc..5556fc9aa 100644 --- a/wai/wai-pack/src/lib.rs +++ b/wai/wai-pack/src/lib.rs @@ -3,10 +3,13 @@ //! //! What this revision builds: the policy and its digest ([`policy`]), what a packager //! may emit ([`capcheck`]), the source readers ([`source`]), `wai.pack.scenecut/1` -//! ([`scenecut`]), piece classification ([`classify`]), the synthetic fixtures -//! ([`fixture`]), and the conformance corpus's regeneration and check -//! ([`conformance`]), whose encode vectors run the integer video encoder -//! (`wai::int_video_enc`); and the base encode's choice of step ([`encode`]). +//! ([`scenecut`]), piece classification ([`classify`]), a piece's plan, ladder and class +//! plan ([`plan`]), the base encode's choice of step and the refinement layers +//! ([`encode`]), stills ([`png`]), the floor ([`floor`], [`av1`], [`cmaf`]), the objects +//! ([`objects`]), the catalogs ([`catalog`]), the packer ([`pack`]) and its verifier +//! ([`verify`]), the live packer ([`live`]), delivery over a simulated link +//! ([`delivery`]), the synthetic fixtures ([`fixture`]), and the conformance corpus's +//! regeneration and check ([`conformance`], [`golden`]). //! //! No float decides anything here: every computation is integer, and float arithmetic //! is denied for the whole crate. @@ -19,13 +22,16 @@ pub mod catalog; pub mod classify; pub mod cmaf; pub mod conformance; +pub mod delivery; pub mod encode; pub mod fixture; pub mod floor; pub mod golden; +pub mod live; pub mod objects; pub mod pack; pub mod plan; +pub mod png; pub mod policy; pub mod scenecut; pub mod source; diff --git a/wai/wai-pack/src/live.rs b/wai/wai-pack/src/live.rs new file mode 100644 index 000000000..382156889 --- /dev/null +++ b/wai/wai-pack/src/live.rs @@ -0,0 +1,255 @@ +//! The live packer ([packaging] §9.2, §4.3): frames pushed one at a time, each piece +//! coded as the segmenter closes it, by the coder the on-demand packer uses +//! ([`crate::pack::Coder`]), so a piece is the same objects either way, and the pieces +//! gathered into windows, each a stage index (§6.1) sealed by a version-2 receipt that +//! names the previous window's (parent kind `live-window`, §9.2). +//! +//! `wai.pack.scenecut/1` decides a frame's cut once `lookahead_frames` more have arrived +//! (§4.3), so a live and an on-demand packer under one policy cut the same pieces and +//! write the same objects. A live packer writes no floor: a supplied floor is made from +//! the whole source after the fact (§7.3), so it is the on-demand package's alone, and a +//! window lists the primary objects, its bases and refinements. +//! +//! A piece's objects are published when the frame that closes it has been pushed: its +//! base then, and layer `k` `layer_lag_ms[k]` after it (§8.6), which [`Published`] states +//! for the delivery simulator (`crate::delivery`). +//! +//! [packaging]: ../../../extensions/packaging.md + +use crate::pack::{CodedPiece, Coder, PackError}; +use crate::policy::{Media, Policy}; +use crate::scenecut::{SegParams, Segmenter}; +use ed25519_dalek::SigningKey; +use wai::package::{PackageReceiptV2, Parent}; +use wai::quantum_ops::GrantRef; +use wai::stage_index::Role; + +/// What a live packer seals windows with. +pub struct LiveOpts { + pub signer: SigningKey, + pub signer_id: String, + /// Pieces per window; 0 for one window over the whole session. + pub window_pieces: u64, + pub grant: GrantRef, +} + +impl LiveOpts { + /// One window, unbounded grant `wai.video.package`. + pub fn new(signer: SigningKey, signer_id: impl Into) -> LiveOpts { + LiveOpts { signer, signer_id: signer_id.into(), window_pieces: 0, grant: GrantRef::unbounded("wai.video.package") } + } +} + +/// One object published. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Published { + /// The absolute piece number. + pub piece: u64, + /// The piece's first frame and its frames. + pub start: u64, + pub duration: u64, + /// The frame whose push closed the piece: the object is published once it has + /// arrived (and, for layer `k`, `layer_lag_ms[k]` after that). + pub at_frame: u64, + pub layer: u8, + pub role: Role, + pub capability: String, + /// A refinement's deadline (§8.6); 0 for a base. + pub deadline_ms: u32, + pub hash: [u8; 32], + pub envelope: Vec, +} + +/// What [`LivePacker::finish`] gives: the objects it published last, every window, and +/// the plan (each piece's first frame). +pub type Finished = (Vec, Vec, Vec); + +/// A window: its stage index and the receipt that seals it. +#[derive(Debug, Clone)] +pub struct LiveWindow { + pub index: Vec, + pub receipt: PackageReceiptV2, +} + +/// The live packer of a one-component video policy. +pub struct LivePacker { + policy: Policy, + w: u32, + h: u32, + opts: LiveOpts, + seg: Option, + /// The frames from the first of the piece being cut, the first at `held_from`. + held: Vec>, + held_from: u64, + pushed: u64, + starts: Vec, + open: Vec, + first_piece: u64, + first_entry: u64, + prev_index: [u8; 32], + prev_receipt: Option<[u8; 32]>, + windows: Vec, +} + +impl LivePacker { + /// A live packer of frames of `w × h` under `policy_bytes`, refusing as the on-demand + /// packer does before any frame: the policy's own refusals, another medium, a rung it + /// does not code. + pub fn new(policy_bytes: &[u8], w: u32, h: u32, opts: LiveOpts) -> Result { + let policy = Policy::parse_bytes(policy_bytes).map_err(|e| PackError::new(e.code(), e.to_string()))?; + if let Some(r) = crate::verify::policy_refusal(&policy) { + return Err(PackError::new(r.code, r.detail)); + } + let [comp] = policy.components.as_slice() else { + return Err(PackError::new("pack-media-unsupported", "this revision packs one component")); + }; + if comp.media != Media::Video { + return Err(PackError::new("pack-media-unsupported", format!("component {} is {}", comp.id, comp.media.label()))); + } + Coder::new(&policy, comp, w, h)?; + let seg = Segmenter::new(SegParams::from_policy(&policy.segmentation, comp.timebase), w, h); + Ok(LivePacker { + policy, + w, + h, + opts, + seg: Some(seg), + held: Vec::new(), + held_from: 0, + pushed: 0, + starts: Vec::new(), + open: Vec::new(), + first_piece: 0, + first_entry: 0, + prev_index: [0u8; 32], + prev_receipt: None, + windows: Vec::new(), + }) + } + + /// The policy, every default written in. + pub fn policy(&self) -> &Policy { + &self.policy + } + + fn code(&mut self, pieces: Vec) -> Result, PackError> { + let mut out = Vec::new(); + let at_frame = self.pushed.saturating_sub(1); + for piece in pieces { + let comp = &self.policy.components[0]; + let coder = Coder { policy: &self.policy, comp, w: self.w, h: self.h }; + let from = (piece.start - self.held_from) as usize; + let cp = coder.code(&piece, &self.held[from..from + piece.len as usize])?; + let number = self.first_piece + self.open.len() as u64; + for s in &cp.stages { + out.push(Published { + piece: number, + start: piece.start, + duration: piece.len, + at_frame, + layer: s.layer, + role: s.role, + capability: s.capability.clone(), + deadline_ms: s.deadline_ms, + hash: s.hash, + envelope: s.envelope.clone(), + }); + } + // The frames before the next piece are no longer needed. + let end = (piece.start + piece.len - self.held_from) as usize; + self.held.drain(..end); + self.held_from = piece.start + piece.len; + self.starts.push(piece.start); + self.open.push(cp); + if self.opts.window_pieces > 0 && self.open.len() as u64 == self.opts.window_pieces { + self.close_window()?; + } + } + Ok(out) + } + + /// Push the next frame (RGB8, `3·w·h` bytes); the objects of every piece it closes. + pub fn push(&mut self, rgb: &[u8]) -> Result, PackError> { + if rgb.len() as u64 != 3 * self.w as u64 * self.h as u64 { + return Err(PackError::new("y4m-size-change", "a frame of another size")); + } + self.held.push(rgb.to_vec()); + self.pushed += 1; + let pieces = self.seg.as_mut().expect("open until finish").push(rgb); + self.code(pieces) + } + + /// Seal the open pieces as a window (a no-op with none open). + pub fn close_window(&mut self) -> Result<(), PackError> { + if self.open.is_empty() { + return Ok(()); + } + let comp = &self.policy.components[0]; + let w = crate::pack::window_index(&self.policy, comp, &self.open, self.first_piece, self.first_entry, self.prev_index)?; + let bytes = w.index.to_bytes(); + let parent = match self.prev_receipt { + None => Parent::None, + Some(h) => Parent::LiveWindow(h), + }; + let receipt = PackageReceiptV2::seal( + &self.opts.signer, + &self.opts.signer_id, + &self.policy.asset_id, + self.policy.digest(), + &[(comp.id.clone(), bytes.clone())], + &crate::pack::packager_id(), + wai::energy_meter::EnergyReading::unmetered(), + self.opts.grant.clone(), + parent, + ) + .map_err(|r| PackError::new(r.code, r.detail))?; + self.first_piece += self.open.len() as u64; + self.first_entry += w.index.entries.len() as u64; + self.prev_index = w.index.digest(); + self.prev_receipt = Some(receipt.receipt_hash()); + self.open.clear(); + self.windows.push(LiveWindow { index: bytes, receipt }); + Ok(()) + } + + /// The end of the source: the last pieces coded and published, the last window + /// sealed. Returns the published objects, every window, and the plan (each piece's + /// first frame). + pub fn finish(mut self) -> Result { + let pieces = self.seg.take().expect("open until finish").finish(); + let out = self.code(pieces)?; + self.close_window()?; + Ok((out, self.windows, self.starts)) + } + + /// The windows sealed so far. + pub fn windows(&self) -> &[LiveWindow] { + &self.windows + } +} + +/// What a live and an on-demand pack of one source share (test 12): of `files`, the plan +/// files (`plan-.txt`) and the objects the stage indexes (`index-.wsix` at the top, +/// and `windows//index-.wsix`) list as bases and refinements. A floor object, which +/// a live pack does not write, is left out. +pub fn primary_files(files: &std::collections::BTreeMap>) -> Result>, String> { + let is_index = |p: &str| { + let name = p.strip_prefix("windows/").and_then(|r| r.split_once('/')).map_or(p, |(_, n)| n); + name.starts_with("index-") && name.ends_with(".wsix") && (p == name || p.starts_with("windows/")) + }; + let mut out = std::collections::BTreeMap::new(); + for (p, b) in files { + if p.starts_with("plan-") && p.ends_with(".txt") && !p.contains('/') { + out.insert(p.clone(), b.clone()); + } + if is_index(p) { + let ix = wai::stage_index::StageIndex::parse(b).map_err(|e| format!("{p}: {e}"))?; + for e in ix.entries.iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)) { + let o = crate::objects::object_path(&e.content_hash); + let bytes = files.get(&o).ok_or_else(|| format!("{o}: listed by {p}, not held"))?; + out.insert(o, bytes.clone()); + } + } + } + Ok(out) +} diff --git a/wai/wai-pack/src/objects.rs b/wai/wai-pack/src/objects.rs index a7226d212..5e801dd63 100644 --- a/wai/wai-pack/src/objects.rs +++ b/wai/wai-pack/src/objects.rs @@ -4,8 +4,10 @@ //! content are one object. //! //! - a base: `media` the component's class, `intent` `replicate`, the rung's capability, -//! `conditioning.kind` its short codec id, and `target` the frames, height and width; -//! - a floor: the same for its capability, with `derived_from` naming the base it +//! `conditioning.kind` its short codec id, and `target` the frames, height and width +//! (a still's frames are those it is held for, §5.7); +//! - a refinement: the manifest `wai stage refine` writes, with `refines` (§5.4); +//! - a floor: a base's manifest for its capability, with `derived_from` naming the base it //! presents (SPEC §7.1, *Derived renditions*). //! //! [packaging]: ../../../extensions/packaging.md @@ -46,6 +48,19 @@ pub fn base_envelope(media: &str, cap: &str, payload: Vec, frames: u64, h: u Wai::new(m, payload).to_bytes().expect("a manifest serialises") } +/// A refinement object of `cap` (§5.4): `refines` names the piece's base `base`, the +/// object of the layer below as the producer of every unit (`prev`, the base itself for +/// layer 1) and its `layer`. Its manifest is the one `wai stage refine` writes: `media` +/// the component's, `intent` `replicate`, the capability with no fallback, +/// `conditioning.kind` `wir1` (the payload's form), `refines`, and no `target` (a +/// layer's shape is the stage below it). +pub fn refinement_envelope(media: &str, cap: &str, payload: Vec, base: &[u8; 32], prev: &[u8; 32], layer: u8) -> Vec { + use wai::container::{Prev, Refines}; + let refines = Refines { base: crate::hex(base), prev: Prev::One(crate::hex(prev)), layer, span: None }; + let m = Manifest::new(media, "replicate", ModelRequirement::new(cap), Conditioning::new("wir1")).with_refines(&refines); + Wai::new(m, payload).to_bytes().expect("a manifest serialises") +} + /// A floor object of `cap` presenting `derived_from.motif`. pub fn floor_envelope(media: &str, cap: &str, payload: Vec, frames: u64, h: u32, w: u32, derived_from: &DerivedFrom) -> Vec { let m = Manifest::new(media, "replicate", ModelRequirement::new(cap), Conditioning::new(kind_of(cap))) diff --git a/wai/wai-pack/src/pack.rs b/wai/wai-pack/src/pack.rs index b0ec71339..1285c797e 100644 --- a/wai/wai-pack/src/pack.rs +++ b/wai/wai-pack/src/pack.rs @@ -3,12 +3,15 @@ //! signing key and the packager's identity, except the energy figure and the measurement //! claim of a metered pack. //! -//! This revision packs one video component with a `wai.video.int_motion` base (its step -//! chosen per piece by [`crate::encode::choose_q`]) and a supplied AV1 floor. What it -//! does not pack yet it refuses by name rather than leaving out: another medium -//! (`pack-media-unsupported`), refinement rungs (`pack-refinement-unsupported`), a rule's -//! ladder subset (`pack-rule-ladder-unsupported`), a floor to encode -//! (`pack-floor-encode-unsupported`), a rung of a capability it does not encode +//! This revision packs one video component, each piece by its ladder ([packaging] §5.7, +//! [`crate::plan::piece_ladder`]): a `wai.video.int_motion` base (its step chosen per +//! piece by [`crate::encode::choose_q`]) or a still of `wai.image.png` ([`crate::png`]), +//! the refinements of `wai.video.int_refine` over an `int_motion` base +//! ([`crate::encode::encode_refinement`], §5.4), and a supplied AV1 floor. Every piece is +//! coded by [`Coder`], which the live packer (`crate::live`) shares. What it does not pack +//! yet it refuses by name rather than leaving out: another medium +//! (`pack-media-unsupported`), a floor to encode (`pack-floor-encode-unsupported`), a rung +//! of a capability it does not encode or a refinement over a base it does not refine //! (`pack-capability-unsupported:`). Its own failures are refused by name too //! (`pack-encode`, `pack-piece-length`, `pack-catalog`, `pack-meter`, //! `pack-measurement-claim`, `pack-listing`; packaging §11.4), and a package is written @@ -54,7 +57,7 @@ pub struct PackError { } impl PackError { - fn new(code: impl Into, detail: impl Into) -> PackError { + pub fn new(code: impl Into, detail: impl Into) -> PackError { PackError { code: code.into(), detail: detail.into() } } } @@ -178,28 +181,6 @@ fn rungs_of<'a>(p: &'a Policy, comp: &str, role: RungRole) -> Vec<&'a Rung> { p.ladder.iter().filter(|r| r.component == comp && r.role == role).collect() } -/// An entry describing `env`. -#[allow(clippy::too_many_arguments)] -fn entry(env: &[u8], piece: u64, capability: u16, role: Role, prefix_tier: u8, prefix_digest: [u8; 32], sse: u64, flags: u8) -> Result { - let (man, payload_len) = object_lengths(env).map_err(|e| PackError::new("pack-encode", format!("an object the packer wrote is not countable: {e}")))?; - Ok(EntryRec { - content_hash: objects::object_hash(env), - byte_len: env.len() as u64, - piece, - refines: wsix::NO_REFINES, - capability, - role, - layer: 0, - prefix_tier, - flags, - deadline_ms: 0, - prefix_digest, - sse, - manifest_len: man, - payload_len, - }) -} - /// A component's CMAF files: the initialization segment's path and bytes, and each /// piece's fragment's. pub type CmafFiles = (String, Vec, Vec<(String, Vec)>); @@ -228,6 +209,280 @@ pub fn plan_file(starts: &[u64]) -> String { starts.iter().map(|s| format!("{s}\n")).collect() } +/// The capabilities this revision encodes, by role: a base of `wai.video.int_motion` +/// (§5.5, §5.6) or `wai.image.png` (§5.7), a refinement of `wai.video.int_refine` over an +/// `int_motion` base (§5.4), and a supplied AV1 floor (§7.3). +fn encodes(rung: &Rung, base_of: impl Fn(&Rung) -> Option) -> bool { + match rung.role { + RungRole::Base => matches!(rung.capability.as_str(), CAP_INT_MOTION | CAP_PNG), + RungRole::Refinement => rung.capability == CAP_INT_REFINE && base_of(rung).is_some_and(|b| b == CAP_INT_MOTION), + RungRole::Floor => matches!(rung.capability.as_str(), "wai.video.av1" | "wai.video.av1.lossless"), + } +} + +const CAP_INT_MOTION: &str = "wai.video.int_motion"; +const CAP_INT_REFINE: &str = "wai.video.int_refine"; +const CAP_PNG: &str = "wai.image.png"; + +/// One object of a piece, with what its entry states (§6.2). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Stage { + pub envelope: Vec, + pub hash: [u8; 32], + pub capability: String, + pub role: Role, + pub layer: u8, + pub prefix_tier: u8, + pub prefix_digest: [u8; 32], + pub sse: u64, + pub deadline_ms: u32, + /// `ENTRY_DERIVED_FROM` for a floor. + pub flags: u8, +} + +/// A piece coded: where it lies, its kind and plan, and its objects in entry order. +#[derive(Debug, Clone)] +pub struct CodedPiece { + pub piece: crate::scenecut::Piece, + pub kind: PieceKind, + pub plan: crate::plan::PiecePlan, + pub stages: Vec, +} + +impl CodedPiece { + /// The bytes of each layer's object: what the class plan reads (§8.1). + pub fn layer_bytes(&self) -> Vec { + self.stages.iter().filter(|s| matches!(s.role, Role::Base | Role::Refinement)).map(|s| s.envelope.len() as u64).collect() + } +} + +/// What codes a video component's pieces: its policy, its frame size, and the rungs it +/// codes, checked once. The on-demand packer ([`pack`]) and the live packer +/// (`crate::live`) code every piece with it, so a piece is the same objects either way. +pub struct Coder<'a> { + pub policy: &'a Policy, + pub comp: &'a crate::policy::Component, + pub w: u32, + pub h: u32, +} + +impl<'a> Coder<'a> { + /// A coder of `comp` under `policy`, refusing a rung of a capability it does not code + /// (`pack-capability-unsupported:`, the first in ladder order) before any piece. + pub fn new(policy: &'a Policy, comp: &'a crate::policy::Component, w: u32, h: u32) -> Result, PackError> { + let by_id: BTreeMap<&str, &Rung> = policy.ladder.iter().map(|r| (r.id.as_str(), r)).collect(); + let base_of = |r: &Rung| { + let mut cur = r; + while let Some(up) = cur.refines.as_deref().and_then(|id| by_id.get(id)) { + cur = up; + } + (cur.role == RungRole::Base).then(|| cur.capability.clone()) + }; + for r in policy.ladder.iter().filter(|r| r.component == comp.id) { + if !encodes(r, base_of) { + return Err(PackError::new(format!("pack-capability-unsupported:{}", r.capability), format!("ladder.{}", r.id))); + } + if r.capability == CAP_INT_MOTION && MotionParams::of(r).is_none() { + return Err(PackError::new(format!("pack-capability-unsupported:{}", r.capability), format!("ladder.{}: its parameters", r.id))); + } + } + Ok(Coder { policy, comp, w, h }) + } + + /// Code `piece`, whose source frames are `src`: its base, then each refinement of its + /// ladder (§5.4–§5.7). Each refinement is applied by the reference sink + /// (`wai::staged::StageChain`) to the decoded base before it is kept, and must give the + /// encoder's reconstruction. + pub fn code(&self, piece: &crate::scenecut::Piece, src: &[Vec]) -> Result { + let (w, h, tb) = (self.w, self.h, self.comp.timebase); + let kind: PieceKind = classify::classify(&piece.stats, &self.policy.classify); + let pp = plan_piece(self.policy, tb, piece.start, piece.len, kind); + let ladder = crate::plan::piece_ladder(self.policy, &self.comp.id, &pp) + .ok_or_else(|| PackError::new("policy-no-base", format!("component {}", self.comp.id)))?; + let n = src.len() as u64; + let enc = |e: String| PackError::new("pack-encode", e); + // The base and the frames a sink presents from it. + let (payload, recon): (Vec, Vec>) = match ladder.base.capability.as_str() { + CAP_INT_MOTION => { + let mp = MotionParams::of(ladder.base).expect("checked by Coder::new"); + let q = choose_q(src, h, w, &mp).map_err(|e| enc(e.to_string()))?; + (q.encoded.wiv1, q.encoded.recon) + } + // A still: the piece's first frame, held for the piece (§5.7). + _ => (crate::png::encode(&src[0], w, h), vec![src[0].clone(); src.len()]), + }; + let base_env = objects::base_envelope(self.comp.media.label(), &ladder.base.capability, payload, n, h, w); + let base_hash = objects::object_hash(&base_env); + let tier = wsix::capability_prefix_tier(&ladder.base.capability); + let mut stages = vec![Stage { + hash: base_hash, + capability: ladder.base.capability.clone(), + role: Role::Base, + layer: 0, + prefix_tier: tier, + prefix_digest: if tier == wsix::TIER_DECODE { clip_digest(&recon) } else { [0u8; 32] }, + sse: crate::encode::sse(&recon, src), + deadline_ms: 0, + flags: 0, + envelope: base_env, + }]; + let deadline = u32::try_from(pp.deadline_ms).map_err(|_| enc("a deadline past 2^32 - 1".into()))?; + let mut below = recon; + let mut chain: Option = None; + for (k, rung) in ladder.refinements.iter().enumerate() { + let layer = u8::try_from(k + 1).map_err(|_| enc("a layer past 255".into()))?; + let q = rung.param_u64("q").and_then(|q| u32::try_from(q).ok()).ok_or_else(|| enc(format!("ladder.{}: its step", rung.id)))?; + let (payload, next) = crate::encode::encode_refinement(&below, src, h, w, q).map_err(enc)?; + let prev = stages.last().expect("the base").hash; + let env = objects::refinement_envelope(self.comp.media.label(), &rung.capability, payload, &base_hash, &prev, layer); + // The sink's own application of the layer to the decoded base. + if chain.is_none() { + let (info, units) = wai::staged::decode_base(&stages[0].envelope, &BTreeMap::new(), None, None).map_err(|e| enc(e.to_string()))?; + chain = Some(wai::staged::StageChain::new(info, units, sink_config())); + } + let c = chain.as_mut().expect("set"); + let reports = c.offer(&env, 0, wai::staged::ReceiptEvidence::None).map_err(|e| enc(e.to_string()))?; + if reports.iter().any(|r| r.envelope.is_some()) || c.clip_sha256() != Some(clip_digest(&next)) { + return Err(enc(format!("piece at {}: layer {layer} does not apply to its base as the encoder reconstructs it", piece.start))); + } + let below_tier = stages.last().expect("the base").prefix_tier; + let tier = if below_tier == wsix::TIER_DECODE && wsix::capability_prefix_tier(&rung.capability) == wsix::TIER_DECODE { wsix::TIER_DECODE } else { wsix::TIER_NONE }; + stages.push(Stage { + hash: objects::object_hash(&env), + capability: rung.capability.clone(), + role: Role::Refinement, + layer, + prefix_tier: tier, + prefix_digest: if tier == wsix::TIER_DECODE { clip_digest(&next) } else { [0u8; 32] }, + sse: crate::encode::sse(&next, src), + deadline_ms: deadline, + flags: 0, + envelope: env, + }); + below = next; + } + Ok(CodedPiece { piece: piece.clone(), kind, plan: pp, stages }) + } +} + +/// The sink a coder applies its layers with: no hold, no cost limit, a budget no piece of +/// this revision reaches, no live schedule. +fn sink_config() -> wai::staged::ChainConfig { + wai::staged::ChainConfig { + bound: wai::staged::HoldBound { max_envelopes: 0, max_age_ms: 0 }, + cost_limit: None, + budget: u64::MAX, + record_budget: 0, + live: None, + } +} + +/// A window's stage index (§6) over coded pieces, and its objects by content hash. +pub struct Window { + pub index: StageIndex, + pub objects: BTreeMap<[u8; 32], Vec>, +} + +/// The stage index of `pieces` (each with its floor stage appended where it has one) as +/// one window: its first piece and entry are `first_piece` and `first_entry` of the +/// chain, `prev` the previous window's index digest (zero for on-demand content and a +/// first window). The string table is the component's id, the class ids, then each +/// capability at its first use; a content hash an earlier entry of the window has is +/// flagged a duplicate. +pub fn window_index(policy: &Policy, comp: &crate::policy::Component, pieces: &[CodedPiece], first_piece: u64, first_entry: u64, prev: [u8; 32]) -> Result { + let tb = comp.timebase; + let mut strings: Vec = vec![comp.id.clone()]; + strings.extend(policy.classes.iter().map(|c| c.id.clone())); + let fixed = strings.len(); + let mut objects: BTreeMap<[u8; 32], Vec> = BTreeMap::new(); + let mut entries: Vec = Vec::new(); + let mut piece_recs: Vec = Vec::new(); + for (p, cp) in pieces.iter().enumerate() { + let number = first_piece.checked_add(p as u64).ok_or_else(|| PackError::new("pack-piece-length", "a piece number past 2^64 - 1"))?; + let first = entries.len() as u32; + for s in &cp.stages { + let cap = match strings[fixed..].iter().position(|x| *x == s.capability) { + Some(i) => (fixed + i) as u16, + None => { + strings.push(s.capability.clone()); + (strings.len() - 1) as u16 + } + }; + let (man, payload_len) = object_lengths(&s.envelope).map_err(|e| PackError::new("pack-encode", format!("an object the packer wrote is not countable: {e}")))?; + let mut flags = s.flags; + if entries.iter().any(|x| x.content_hash == s.hash) { + flags |= wsix::ENTRY_DUP; + } + // A refinement refines the entry of the layer below it in its piece. + let refines = if s.role == Role::Refinement { entries.len() as u32 - 1 } else { wsix::NO_REFINES }; + entries.push(EntryRec { + content_hash: s.hash, + byte_len: s.envelope.len() as u64, + piece: number, + refines, + capability: cap, + role: s.role, + layer: s.layer, + prefix_tier: s.prefix_tier, + flags, + deadline_ms: s.deadline_ms, + prefix_digest: s.prefix_digest, + sse: s.sse, + manifest_len: man, + payload_len, + }); + objects.insert(s.hash, s.envelope.clone()); + } + piece_recs.push(PieceRec { + start: cp.piece.start, + duration: u32::try_from(cp.piece.len).map_err(|_| PackError::new("pack-piece-length", "a piece past 2^32 - 1 units"))?, + first_entry: first, + n_entries: u16::try_from(entries.len() as u32 - first).map_err(|_| PackError::new("pack-piece-length", "a piece of more than 65535 objects"))?, + kind: cp.kind as u8, + flags: cp.piece.flags, + rules: cp.plan.rules, + moments: cp.plan.moments, + }); + } + let plan: Vec> = policy + .classes + .iter() + .map(|c| pieces.iter().map(|cp| class_plan(c, &cp.layer_bytes(), cp.plan.max_layer, cp.piece.len, tb)).collect()) + .collect(); + let classes: Vec = policy + .classes + .iter() + .enumerate() + .map(|(i, c)| ClassRec { + name: (1 + i) as u16, + max_layer: c.max_layer.min(255) as u8, + flags: if c.floor_only { wsix::CLASS_FLOOR_ONLY } else { 0 }, + max_kbps: c.max_kbps.map_or(0, |k| k.min(u32::MAX as u64) as u32), + }) + .collect(); + let starts: Vec = pieces.iter().map(|cp| cp.piece.start).collect(); + let index = StageIndex { + header: Header { + media_class: MediaClass::Video, + tb_num: tb.num, + tb_den: tb.den, + policy_digest: policy.digest(), + prev_index_digest: prev, + first_piece, + first_entry, + plan_digest: wsix::plan_digest(&starts), + component: 0, + }, + pieces: piece_recs, + entries, + classes, + plan, + strings, + }; + index.check().map_err(|r| PackError::new(r.code, format!("the packer wrote an index that does not check: {}", r.detail)))?; + Ok(Window { index, objects }) +} + /// Pack `policy_bytes` over `sources` (each component's source bytes, by id). pub fn pack(policy_bytes: &[u8], sources: &BTreeMap>, opts: &mut PackOpts) -> Result { let policy = Policy::parse_bytes(policy_bytes).map_err(|e| PackError::new(e.code(), e.to_string()))?.effective(&opts.overrides); @@ -241,25 +496,12 @@ pub fn pack(policy_bytes: &[u8], sources: &BTreeMap>, opts: &mut if comp.media != Media::Video { return Err(PackError::new("pack-media-unsupported", format!("component {} is {}", comp.id, comp.media.label()))); } - if !rungs_of(&policy, &comp.id, RungRole::Refinement).is_empty() { - return Err(PackError::new("pack-refinement-unsupported", "refinement rungs are packed by a later revision")); - } - if policy.rules.iter().any(|r| r.then.ladder.is_some()) { - return Err(PackError::new("pack-rule-ladder-unsupported", "a rule's ladder subset is packed by a later revision")); - } - let bases = rungs_of(&policy, &comp.id, RungRole::Base); - let [base] = bases.as_slice() else { - let extra = bases.get(1).map_or("", |r| r.capability.as_str()); - return Err(PackError::new(format!("pack-capability-unsupported:{extra}"), "this revision packs one base rung")); - }; - let mp = MotionParams::of(base).ok_or_else(|| PackError::new(format!("pack-capability-unsupported:{}", base.capability), "the base rung"))?; + // The rungs it codes, before the source is read. + Coder::new(&policy, comp, 0, 0)?; let floors = rungs_of(&policy, &comp.id, RungRole::Floor); let [floor_rung] = floors.as_slice() else { return Err(PackError::new("floor-missing", format!("component {} needs one floor rung (§7.1)", comp.id))); }; - if !matches!(floor_rung.capability.as_str(), "wai.video.av1" | "wai.video.av1.lossless") { - return Err(PackError::new(format!("pack-capability-unsupported:{}", floor_rung.capability), "the floor rung")); - } if floor_rung.params.get("source").and_then(|v| v.as_str()) != Some("supplied") { return Err(PackError::new("pack-floor-encode-unsupported", "this revision packs a supplied floor")); } @@ -287,101 +529,33 @@ pub fn pack(policy_bytes: &[u8], sources: &BTreeMap>, opts: &mut if let Some(m) = opts.meter.as_mut() { m.start(); } - - // The string table: the component, the classes, then capabilities by first use. - let mut strings: Vec = vec![comp.id.clone()]; - strings.extend(policy.classes.iter().map(|c| c.id.clone())); - let mut intern = |s: &str| -> u16 { - match strings.iter().position(|x| x == s) { - Some(i) => i as u16, - None => { - strings.push(s.to_string()); - (strings.len() - 1) as u16 - } - } - }; - let base_cap = intern(&base.capability); - let floor_cap = intern(&floor_rung.capability); - let mut objects: BTreeMap<[u8; 32], Vec> = BTreeMap::new(); - let mut entries: Vec = Vec::new(); - let mut piece_recs: Vec = Vec::new(); - let mut layer_bytes: Vec<(Vec, Option)> = Vec::new(); + let coder = Coder::new(&policy, comp, w, h)?; let (fw, fh) = (fl.seq.max_frame_width, fl.seq.max_frame_height); - let mut add = |env: Vec, entries: &mut Vec, e: EntryRec| { - let mut e = e; - if entries.iter().any(|x| x.content_hash == e.content_hash) { - e.flags |= wsix::ENTRY_DUP; - } - objects.insert(e.content_hash, env); - entries.push(e); - }; + let mut coded = Vec::with_capacity(pieces.len()); for (p, piece) in pieces.iter().enumerate() { - let kind: PieceKind = classify::classify(&piece.stats, &policy.classify); - let pp = plan_piece(&policy, tb, piece.start, piece.len, kind); let src_frames: Vec> = frames[piece.start as usize..(piece.start + piece.len) as usize].iter().map(|f| f.rgb.clone()).collect(); - let q = choose_q(&src_frames, h, w, &mp).map_err(|e| PackError::new("pack-encode", e.to_string()))?; - let n = src_frames.len() as u64; - let base_env = objects::base_envelope("video", &base.capability, q.encoded.wiv1, n, h, w); - let base_hash = objects::object_hash(&base_env); - let first = entries.len() as u32; - let tier = wsix::capability_prefix_tier(&base.capability); - let digest = if tier == wsix::TIER_DECODE { clip_digest(&q.encoded.recon) } else { [0u8; 32] }; - let be = entry(&base_env, p as u64, base_cap, Role::Base, tier, digest, q.encoded.sse, 0)?; - let base_len = be.byte_len; - add(base_env, &mut entries, be); - let df = objects::supplied_derived_from(&base_hash, &base.capability, &listing); + let mut cp = coder.code(piece, &src_frames)?; + // The floor, presenting the piece's base (§7.2). + let base = &cp.stages[0]; + let df = objects::supplied_derived_from(&base.hash, &base.capability, &listing); let payload = floor::av1_payload(&fl.pieces[p].units, fh, fw, tb.num, tb.den); - let floor_env = objects::floor_envelope("video", &floor_rung.capability, payload, n, fh, fw, &df); - let fe = entry(&floor_env, p as u64, floor_cap, Role::Floor, wsix::TIER_NONE, [0u8; 32], wsix::SSE_UNKNOWN, wsix::ENTRY_DERIVED_FROM)?; - add(floor_env, &mut entries, fe); - piece_recs.push(PieceRec { - start: piece.start, - duration: u32::try_from(piece.len).map_err(|_| PackError::new("pack-piece-length", "a piece past 2^32 - 1 units"))?, - first_entry: first, - n_entries: (entries.len() as u32 - first) as u16, - kind: kind as u8, - flags: piece.flags, - rules: pp.rules, - moments: pp.moments, + let env = objects::floor_envelope(comp.media.label(), &floor_rung.capability, payload, piece.len, fh, fw, &df); + cp.stages.push(Stage { + hash: objects::object_hash(&env), + capability: floor_rung.capability.clone(), + role: Role::Floor, + layer: 0, + prefix_tier: wsix::TIER_NONE, + prefix_digest: [0u8; 32], + sse: wsix::SSE_UNKNOWN, + deadline_ms: 0, + flags: wsix::ENTRY_DERIVED_FROM, + envelope: env, }); - layer_bytes.push((vec![base_len], pp.max_layer)); + coded.push(cp); } - let plan: Vec> = policy - .classes - .iter() - .map(|c| piece_recs.iter().zip(&layer_bytes).map(|(pr, (lb, rm))| class_plan(c, lb, *rm, pr.duration as u64, tb)).collect()) - .collect(); - let classes: Vec = policy - .classes - .iter() - .enumerate() - .map(|(i, c)| ClassRec { - name: (1 + i) as u16, - max_layer: c.max_layer.min(255) as u8, - flags: if c.floor_only { wsix::CLASS_FLOOR_ONLY } else { 0 }, - max_kbps: c.max_kbps.map_or(0, |k| k.min(u32::MAX as u64) as u32), - }) - .collect(); + let Window { index, objects } = window_index(&policy, comp, &coded, 0, 0, [0u8; 32])?; let policy_digest = policy.digest(); - let index = StageIndex { - header: Header { - media_class: MediaClass::Video, - tb_num: tb.num, - tb_den: tb.den, - policy_digest, - prev_index_digest: [0u8; 32], - first_piece: 0, - first_entry: 0, - plan_digest: wsix::plan_digest(&starts), - component: 0, - }, - pieces: piece_recs, - entries, - classes, - plan, - strings, - }; - index.check().map_err(|r| PackError::new(r.code, format!("the packer wrote an index that does not check: {}", r.detail)))?; let (init_path, init, frags) = cmaf_files(&comp.id, &fl, &starts, tb.den, tb.num).map_err(|e| PackError::new("floor-av1-malformed", e))?; let view = CompView { index: &index, @@ -390,8 +564,9 @@ pub fn pack(policy_bytes: &[u8], sources: &BTreeMap>, opts: &mut h, objects: &objects, floor: Some(FloorView { codec: fl.seq.codec_string(), w: fw, h: fh, init: init_path.clone(), fragments: frags.iter().map(|(p, b)| (p.clone(), b.len() as u64)).collect() }), + base_cap: catalog::default_base_cap(&policy, &comp.id), }; - let catalogs = catalog::render_all(&view, &policy.layer_lag_ms, policy.deadline_ms, &policy_digest).map_err(|e| PackError::new("pack-catalog", e.0))?; + let catalogs = catalog::render_all(&view, &policy, &policy_digest).map_err(|e| PackError::new("pack-catalog", e.0))?; let index_bytes = index.to_bytes(); // A meter that was asked for and gives no figure (a span too short, a product past // its range) refuses the pack: it never turns into an unmetered package. diff --git a/wai/wai-pack/src/plan.rs b/wai/wai-pack/src/plan.rs index 65418177a..a22a402b2 100644 --- a/wai/wai-pack/src/plan.rs +++ b/wai/wai-pack/src/plan.rs @@ -17,7 +17,7 @@ //! [packaging]: ../../../extensions/packaging.md use crate::classify::PieceKind; -use crate::policy::{Class, Policy, Timebase}; +use crate::policy::{Class, Policy, Role, Rung, Timebase}; use wai::stage_index::FLOOR_ONLY; /// A span in timebase units as milliseconds, rounded down. @@ -67,6 +67,41 @@ pub fn plan_piece(p: &Policy, tb: Timebase, start: u64, duration: u64, kind: Pie out } +/// The rungs a piece is coded in (§5.7): one base, the refinements over it in layer +/// order, and the component's floors. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PieceLadder<'a> { + pub base: &'a Rung, + /// `refinements[k − 1]` codes layer `k`. + pub refinements: Vec<&'a Rung>, + pub floors: Vec<&'a Rung>, +} + +/// The ladder of a piece of component `comp` whose plan is `pp`: the rungs the matching +/// rules' `then.ladder` names, where it names rungs of `comp` (§3.4 has checked that they +/// are one base, refinements over rungs it names, and every floor of the component); +/// otherwise the component's default ladder, its first base rung in ladder order, the +/// chain of refinements over it, and its floor rungs. `None` for a component with no base +/// rung (§3.4 refuses such a policy). +pub fn piece_ladder<'a>(p: &'a Policy, comp: &str, pp: &PiecePlan) -> Option> { + let mine = |r: &&Rung| r.component == comp; + let rungs: Vec<&Rung> = match &pp.ladder { + Some(ids) if ids.iter().all(|id| p.ladder.iter().any(|r| r.id == *id && r.component == comp)) => { + p.ladder.iter().filter(mine).filter(|r| ids.contains(&r.id)).collect() + } + _ => p.ladder.iter().filter(mine).collect(), + }; + let base = *rungs.iter().find(|r| r.role == Role::Base)?; + let mut refinements = Vec::new(); + let mut cur = base; + while let Some(next) = rungs.iter().find(|r| r.role == Role::Refinement && r.refines.as_deref() == Some(cur.id.as_str())) { + refinements.push(*next); + cur = next; + } + let floors = rungs.iter().filter(|r| r.role == Role::Floor).copied().collect(); + Some(PieceLadder { base, refinements, floors }) +} + /// The layer class `c` lists for a piece: `layer_bytes[k]` is the bytes of the piece's /// layer-`k` object(s), `rule_max` the rules' `max_layer`, and the piece lasts `duration` /// units of `tb`. diff --git a/wai/wai-pack/src/png.rs b/wai/wai-pack/src/png.rs new file mode 100644 index 000000000..d18d872e3 --- /dev/null +++ b/wai/wai-pack/src/png.rs @@ -0,0 +1,342 @@ +//! A piece's still ([packaging] §5.7): one RGB8 frame as a `wai.image.png` payload, every +//! byte a pure function of the frame, so an independent implementation writes the same +//! file, and the reader a verifier decodes any such payload with. +//! +//! The writer's choices: +//! +//! - the signature, then `IHDR` (width, height, bit depth 8, colour type 2, compression 0, +//! filter 0, interlace 0), one `IDAT` holding the whole compressed stream, and `IEND`; +//! each chunk a big-endian length, its type, its data and the CRC-32 of type and data; +//! - every row filtered `Sub` (filter byte 1): each byte less the byte three before it in +//! the row, modulo 256, the first pixel's bytes less 0; +//! - a zlib stream (RFC 1950): `0x78 0x01`, one final deflate block of the fixed Huffman +//! codes (RFC 1951 §3.2.6), and the Adler-32 of the filtered rows, big-endian; +//! - the block's symbols, from position 0 of the filtered rows: where at least three bytes +//! remain and an earlier position starts with the same three bytes, the latest such +//! position `j` within 32768 bytes is the match; its length is the bytes the two runs +//! share from there, at most 258, and it is written as that length and the distance to +//! `j`; otherwise one literal. Every position a symbol covers that has three bytes left +//! becomes the latest position of its three bytes. The block ends with symbol 256, and +//! the last byte is padded with zero bits. +//! +//! The reader takes any PNG of 8-bit RGB without interlace: the chunks, each CRC checked, +//! the first `IHDR`, the `IDAT`s in order, `IEND` last and nothing after it; the zlib +//! stream inflated (any block type) with its Adler-32 checked; and the five filters. +//! +//! [packaging]: ../../../extensions/packaging.md + +use std::collections::BTreeMap; + +const SIGNATURE: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; + +/// The CRC-32 of ISO 3309 (PNG's chunk check), reflected, polynomial `0xEDB88320`. +pub fn crc32(parts: &[&[u8]]) -> u32 { + let mut table = [0u32; 256]; + for (n, t) in table.iter_mut().enumerate() { + let mut c = n as u32; + for _ in 0..8 { + c = if c & 1 != 0 { 0xedb8_8320 ^ (c >> 1) } else { c >> 1 }; + } + *t = c; + } + let mut c = 0xffff_ffffu32; + for p in parts { + for &b in *p { + c = table[((c ^ b as u32) & 0xff) as usize] ^ (c >> 8); + } + } + c ^ 0xffff_ffff +} + +/// Adler-32 (RFC 1950). +pub fn adler32(b: &[u8]) -> u32 { + let (mut a, mut s) = (1u32, 0u32); + for &x in b { + a = (a + x as u32) % 65521; + s = (s + a) % 65521; + } + (s << 16) | a +} + +/// Bits written least significant first, as deflate packs them. +struct Bits { + out: Vec, + acc: u64, + n: u32, +} + +impl Bits { + fn put(&mut self, v: u32, n: u32) { + self.acc |= (v as u64) << self.n; + self.n += n; + while self.n >= 8 { + self.out.push(self.acc as u8); + self.acc >>= 8; + self.n -= 8; + } + } + + /// A Huffman code, most significant bit first. + fn code(&mut self, code: u32, len: u32) { + let mut rev = 0u32; + for i in 0..len { + rev |= ((code >> i) & 1) << (len - 1 - i); + } + self.put(rev, len); + } + + fn finish(mut self) -> Vec { + if self.n > 0 { + self.out.push(self.acc as u8); + } + self.out + } +} + +/// The fixed literal/length code of symbol `s` (RFC 1951 §3.2.6). +fn lit_code(b: &mut Bits, s: u32) { + match s { + 0..=143 => b.code(0x30 + s, 8), + 144..=255 => b.code(0x190 + s - 144, 9), + 256..=279 => b.code(s - 256, 7), + _ => b.code(0xc0 + s - 280, 8), + } +} + +/// RFC 1951 §3.2.5: the length codes' base lengths and extra bits. +const LEN_BASE: [u32; 29] = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258]; +const LEN_EXTRA: [u32; 29] = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]; +const DIST_BASE: [u32; 30] = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577]; +const DIST_EXTRA: [u32; 30] = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]; + +fn put_match(b: &mut Bits, len: u32, dist: u32) { + let li = LEN_BASE.iter().rposition(|&x| x <= len).expect("a length of at least 3"); + lit_code(b, 257 + li as u32); + b.put(len - LEN_BASE[li], LEN_EXTRA[li]); + let di = DIST_BASE.iter().rposition(|&x| x <= dist).expect("a distance of at least 1"); + b.code(di as u32, 5); + b.put(dist - DIST_BASE[di], DIST_EXTRA[di]); +} + +fn key(d: &[u8], i: usize) -> u32 { + (d[i] as u32) << 16 | (d[i + 1] as u32) << 8 | d[i + 2] as u32 +} + +/// The module's deflate: one final block of fixed codes, the matches as the module states. +pub fn deflate_fixed(d: &[u8]) -> Vec { + let mut b = Bits { out: Vec::with_capacity(d.len() / 2 + 16), acc: 0, n: 0 }; + b.put(1, 1); // BFINAL + b.put(1, 2); // BTYPE 01, fixed codes + let mut last: BTreeMap = BTreeMap::new(); + let n = d.len(); + let mut i = 0usize; + while i < n { + let found = if i + 3 <= n { last.get(&key(d, i)).copied().filter(|&j| i - j <= 32768) } else { None }; + let len = match found { + Some(j) => { + let max = (n - i).min(258); + let mut l = 0; + while l < max && d[j + l] == d[i + l] { + l += 1; + } + put_match(&mut b, l as u32, (i - j) as u32); + l + } + None => { + lit_code(&mut b, d[i] as u32); + 1 + } + }; + for p in i..i + len { + if p + 3 <= n { + last.insert(key(d, p), p); + } + } + i += len; + } + lit_code(&mut b, 256); + b.finish() +} + +fn chunk(out: &mut Vec, kind: &[u8; 4], data: &[u8]) { + out.extend_from_slice(&(data.len() as u32).to_be_bytes()); + out.extend_from_slice(kind); + out.extend_from_slice(data); + out.extend_from_slice(&crc32(&[kind, data]).to_be_bytes()); +} + +/// The rows of an RGB8 frame of `w × h`, each filtered `Sub`. +fn filtered(rgb: &[u8], w: u32, h: u32) -> Vec { + let stride = 3 * w as usize; + let mut out = Vec::with_capacity((stride + 1) * h as usize); + for row in rgb.chunks_exact(stride).take(h as usize) { + out.push(1); + for i in 0..stride { + out.push(row[i].wrapping_sub(if i >= 3 { row[i - 3] } else { 0 })); + } + } + out +} + +/// The still of an RGB8 frame of `w × h` (`rgb` holds `3·w·h` bytes). +pub fn encode(rgb: &[u8], w: u32, h: u32) -> Vec { + let raw = filtered(rgb, w, h); + let mut z = vec![0x78, 0x01]; + z.extend_from_slice(&deflate_fixed(&raw)); + z.extend_from_slice(&adler32(&raw).to_be_bytes()); + let mut ihdr = Vec::with_capacity(13); + ihdr.extend_from_slice(&w.to_be_bytes()); + ihdr.extend_from_slice(&h.to_be_bytes()); + ihdr.extend_from_slice(&[8, 2, 0, 0, 0]); + let mut out = SIGNATURE.to_vec(); + chunk(&mut out, b"IHDR", &ihdr); + chunk(&mut out, b"IDAT", &z); + chunk(&mut out, b"IEND", &[]); + out +} + +fn paeth(a: u8, b: u8, c: u8) -> u8 { + let p = a as i16 + b as i16 - c as i16; + let (pa, pb, pc) = ((p - a as i16).abs(), (p - b as i16).abs(), (p - c as i16).abs()); + if pa <= pb && pa <= pc { + a + } else if pb <= pc { + b + } else { + c + } +} + +/// The largest picture the reader decodes: the integer payloads' cap on one unit +/// (`2^26` samples). +pub const MAX_SAMPLES: u64 = 1 << 26; + +/// Decode a PNG of 8-bit RGB without interlace to its width, height and RGB8 samples. +pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { + if b.len() < 8 || b[..8] != SIGNATURE { + return Err("not a PNG signature".into()); + } + let mut at = 8usize; + let mut ihdr: Option<(u32, u32)> = None; + let mut z = Vec::new(); + let mut ended = false; + while at < b.len() { + if ended { + return Err("bytes after IEND".into()); + } + let len = u32::from_be_bytes(b.get(at..at + 4).ok_or("a chunk length past the end")?.try_into().expect("4")) as usize; + let kind: [u8; 4] = b.get(at + 4..at + 8).ok_or("a chunk type past the end")?.try_into().expect("4"); + let data = b.get(at + 8..at + 8 + len).ok_or("a chunk past the end")?; + let crc = u32::from_be_bytes(b.get(at + 8 + len..at + 12 + len).ok_or("a chunk CRC past the end")?.try_into().expect("4")); + if crc != crc32(&[&kind, data]) { + return Err(format!("{}: its CRC", String::from_utf8_lossy(&kind))); + } + match &kind { + b"IHDR" => { + if ihdr.is_some() || at != 8 || data.len() != 13 { + return Err("IHDR is not one 13-byte chunk first".into()); + } + let (w, h) = (u32::from_be_bytes(data[0..4].try_into().expect("4")), u32::from_be_bytes(data[4..8].try_into().expect("4"))); + if data[8..] != [8, 2, 0, 0, 0] { + return Err("only 8-bit RGB, deflate, adaptive filtering, no interlace".into()); + } + if w == 0 || h == 0 || (w as u64) * (h as u64) * 3 > MAX_SAMPLES { + return Err("a size outside 1 to 2^26 samples".into()); + } + ihdr = Some((w, h)); + } + b"IDAT" => z.extend_from_slice(data), + b"IEND" => ended = true, + _ if kind[0] & 0x20 == 0 => return Err(format!("an unknown critical chunk {}", String::from_utf8_lossy(&kind))), + _ => {} + } + at += 12 + len; + } + let (w, h) = ihdr.ok_or("no IHDR")?; + if !ended { + return Err("no IEND".into()); + } + let stride = 3 * w as usize; + let want = (stride + 1) * h as usize; + let raw = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(&z, want).map_err(|e| format!("the zlib stream: {e:?}"))?; + if raw.len() != want || z.len() < 4 || u32::from_be_bytes(z[z.len() - 4..].try_into().expect("4")) != adler32(&raw) { + return Err("the rows are not the picture's size, or their Adler-32".into()); + } + let mut out = vec![0u8; stride * h as usize]; + for y in 0..h as usize { + let f = raw[y * (stride + 1)]; + let row = &raw[y * (stride + 1) + 1..(y + 1) * (stride + 1)]; + for i in 0..stride { + let a = if i >= 3 { out[y * stride + i - 3] } else { 0 }; + let up = if y > 0 { out[(y - 1) * stride + i] } else { 0 }; + let c = if y > 0 && i >= 3 { out[(y - 1) * stride + i - 3] } else { 0 }; + let p = match f { + 0 => 0, + 1 => a, + 2 => up, + 3 => ((a as u16 + up as u16) / 2) as u8, + 4 => paeth(a, up, c), + _ => return Err(format!("row {y}: filter {f}")), + }; + out[y * stride + i] = row[i].wrapping_add(p); + } + } + Ok((w, h, out)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn frame(w: u32, h: u32, f: impl Fn(u32, u32, u32) -> u8) -> Vec { + (0..h).flat_map(|y| (0..w).flat_map(move |x| (0..3).map(move |c| (x, y, c)))).map(|(x, y, c)| f(x, y, c)).collect() + } + + /// Known answers computed independently (RFC 1950's and ISO 3309's examples). + #[test] + fn checksums() { + assert_eq!(crc32(&[b"IEND"]), 0xae42_6082); + assert_eq!(crc32(&[b"123456789"]), 0xcbf4_3926); + assert_eq!(adler32(b"Wikipedia"), 0x11e6_0398); + } + + /// The writer's output is a PNG another inflater reads back to the frame, for + /// pictures that compress well and pictures that do not. + #[test] + fn a_still_round_trips() { + for (w, h) in [(1, 1), (3, 2), (64, 36), (33, 17)] { + for f in [ + frame(w, h, |x, y, c| ((x * 7 + y * 3 + c * 50) % 256) as u8), + frame(w, h, |_, _, _| 77), + frame(w, h, |x, y, c| (crate::fixture::noise(5, x, y, c) % 256) as u8), + ] { + let png = encode(&f, w, h); + assert_eq!(decode(&png).unwrap(), (w, h, f.clone()), "{w}x{h}"); + let raw = miniz_oxide::inflate::decompress_to_vec_zlib(&png[8 + 25 + 8..png.len() - 12 - 4]).unwrap(); + assert_eq!(raw.len(), (3 * w as usize + 1) * h as usize); + } + } + } + + /// A flat picture compresses to a fraction of its samples; matches reach back across + /// rows and run to 258 bytes. + #[test] + fn matches_compress() { + let f = frame(64, 36, |_, _, _| 9); + assert!(encode(&f, 64, 36).len() < 300, "{}", encode(&f, 64, 36).len()); + } + + #[test] + fn the_reader_refuses_what_it_does_not_read() { + let f = frame(4, 3, |x, y, c| (x + y + c) as u8); + let good = encode(&f, 4, 3); + let mut bad = good.clone(); + bad[30] ^= 1; + assert!(decode(&bad).is_err(), "a CRC"); + let mut bad = good.clone(); + bad.push(0); + assert!(decode(&bad).is_err(), "bytes after IEND"); + assert!(decode(&good[..good.len() - 12]).is_err(), "no IEND"); + assert!(decode(b"not a png").is_err()); + } +} diff --git a/wai/wai-pack/src/policy.rs b/wai/wai-pack/src/policy.rs index f12d52c10..fa2185993 100644 --- a/wai/wai-pack/src/policy.rs +++ b/wai/wai-pack/src/policy.rs @@ -985,10 +985,17 @@ impl Policy { for r in &self.rules { if let Some(ids) = &r.then.ladder { let rungs: Option> = ids.iter().map(|id| by_id.get(id.as_str()).copied()).collect(); + // One component; each rung once; exactly one base; every refinement + // over a rung it names, so the layers it codes are a chain from that base; + // and every floor rung of the component (§7.1: every piece has a floor). + let named = |id: &str| ids.iter().any(|i| i == id); let ok = rungs.is_some_and(|rs| { !rs.is_empty() && rs.iter().all(|x| x.component == rs[0].component) - && rs.iter().any(|x| x.role == Role::Base) + && first_duplicate(ids.iter().map(String::as_str)).is_none() + && rs.iter().filter(|x| x.role == Role::Base).count() == 1 + && rs.iter().all(|x| x.refines.as_deref().is_none_or(named)) + && self.ladder.iter().filter(|x| x.component == rs[0].component && x.role == Role::Floor).all(|f| named(&f.id)) }); if !ok { return e(P::RuleLadder, &format!("rule.{}", r.id)); diff --git a/wai/wai-pack/src/verify.rs b/wai/wai-pack/src/verify.rs index 037d71dd5..65b743bea 100644 --- a/wai/wai-pack/src/verify.rs +++ b/wai/wai-pack/src/verify.rs @@ -273,30 +273,28 @@ pub fn index_against_policy(policy: &Policy, comp: &Component, ix: &StageIndex) if !classes_ok { return bad("the class records are not the policy's classes".into()); } - // Each entry's role and capability are a rung's of its component. - let layers = policy.layers(); - for (k, e) in ix.entries.iter().enumerate() { - let want = match e.role { - Role::Base => Some(RungRole::Base), - Role::Refinement => Some(RungRole::Refinement), - Role::Floor => Some(RungRole::Floor), - _ => None, - }; - let cap = ix.capability(e); - let found = want.is_some_and(|w| { - policy.ladder.iter().any(|r| r.component == comp.id && r.role == w && r.capability == cap && layers.get(r.id.as_str()).copied() == Some(e.layer as u64)) - }); - if !found { - return bad(format!("entry {k}: {cap} as {:?} at layer {} is no rung of the component", e.role, e.layer)); - } - } - // Each piece's rules, moments and class plan (§8.1). + // Each piece: a kind a video piece has; its rules and moments (§8.1); its entries the + // rungs of its ladder (§5.7), a base, each refinement in layer order, each floor, with + // the plan's deadline on every refinement (§8.6); its class plan (§8.1). for (p, piece) in ix.pieces.iter().enumerate() { let Some(kind) = PieceKind::from_code(piece.kind) else { return bad(format!("piece {p}: a kind a video piece does not have")) }; let pp = plan_piece(policy, tb, piece.start, piece.duration as u64, kind); if (pp.rules, pp.moments) != (piece.rules, piece.moments) { return bad(format!("piece {p}: its rules and moments are not §8.1's")); } + let Some(ladder) = crate::plan::piece_ladder(policy, &comp.id, &pp) else { return bad("no base rung".into()) }; + let mut want: Vec<(Role, &str, u8)> = vec![(Role::Base, ladder.base.capability.as_str(), 0)]; + for (k, r) in ladder.refinements.iter().enumerate() { + want.push((Role::Refinement, r.capability.as_str(), (k + 1).min(255) as u8)); + } + want.extend(ladder.floors.iter().map(|r| (Role::Floor, r.capability.as_str(), 0))); + let got: Vec<(Role, &str, u8)> = ix.piece_entries(p).iter().map(|e| (e.role, ix.capability(e), e.layer)).collect(); + if got != want { + return bad(format!("piece {p}: its entries are not the rungs of its ladder (§5.7)")); + } + if ix.piece_entries(p).iter().any(|e| e.role == Role::Refinement && e.deadline_ms as u64 != pp.deadline_ms) { + return bad(format!("piece {p}: a refinement's deadline is not its plan's (§8.6)")); + } let mut layer_bytes: Vec = Vec::new(); for e in ix.piece_entries(p).iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)) { if layer_bytes.len() <= e.layer as usize { @@ -355,18 +353,11 @@ fn verify_component( continue; } objects.insert(e.content_hash, b.clone()); - if e.prefix_tier == wsix::TIER_DECODE && e.role == Role::Base { - match decode_frames(ix.capability(e), b) { - Ok((frames, w, hgt)) => { - dims.get_or_insert((w, hgt)); - if crate::pack::clip_digest(&frames) != e.prefix_digest { - errs.push(reason("prefix-digest", format!("entry {k}: the decode is not its prefix digest"))); - } - } - Err(why) => errs.push(reason("prefix-digest", format!("entry {k}: {why}"))), - } - } else if e.prefix_tier == wsix::TIER_DECODE { - errs.push(reason("prefix-digest", format!("entry {k}: a refinement's prefix is checked by a later revision"))); + } + if errs.is_empty() { + match prefix_digests(ix, &objects) { + Ok(d) => dims = d, + Err(mut v) => errs.append(&mut v), } } // The floor and the catalogs are read only from objects that hold. @@ -384,19 +375,82 @@ fn verify_component( return Err(errs); } let (w, hgt) = dims.unwrap_or((0, 0)); - let view = CompView { index: ix, tb: Timebase { num: tb.num, den: tb.den }, w, h: hgt, objects: &objects, floor }; - catalog::render_all(&view, &policy.layer_lag_ms, policy.deadline_ms, policy_digest).map_err(|e| vec![reason("catalog-mismatch", format!("no catalog can be written: {}", e.0))]) + let view = CompView { index: ix, tb: Timebase { num: tb.num, den: tb.den }, w, h: hgt, objects: &objects, floor, base_cap: catalog::default_base_cap(policy, &comp.id) }; + catalog::render_all(&view, policy, policy_digest).map_err(|e| vec![reason("catalog-mismatch", format!("no catalog can be written: {}", e.0))]) } -/// A base object decoded to its frames and dimensions. -fn decode_frames(cap: &str, env: &[u8]) -> Result<(Vec>, u32, u32), String> { +/// A base object decoded to the frames a sink presents for a piece of `n` frames, and +/// their dimensions: an integer payload's own frames, or a still's one frame held for the +/// piece (§5.7). +fn decode_frames(cap: &str, env: &[u8], n: u32) -> Result<(Vec>, u32, u32), String> { let w = wai::container::Wai::from_bytes(env).map_err(|e| e.to_string())?; + if cap == "wai.image.png" { + let (width, height, rgb) = crate::png::decode(&w.payload)?; + return Ok((vec![rgb; n as usize], width, height)); + } match wai::int_payload::decode_int(cap, &w.payload, &wai::int_payload::IntParams::none()).map_err(|e| e.to_string())? { wai::int_payload::IntDecoded::Video { frames, width, height } => Ok((frames, width, height)), _ => Err(format!("{cap} does not decode to frames here")), } } +/// Every decode-equivalent prefix decoded and compared with its `prefix_digest` (§6.5): +/// per piece, its base by its capability's decode, then its refinements applied in layer +/// order by the reference sink (`wai::staged`), under the reference tools' cost limit and +/// chain budget, each checked after it applies. The frame size of the first base. +fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Result, Vec> { + let mut errs = Vec::new(); + let mut dims = None; + for p in 0..ix.pieces.len() { + let first = ix.pieces[p].first_entry as usize; + let es = ix.piece_entries(p); + let Some(bi) = es.iter().position(|e| e.role == Role::Base) else { continue }; + let base = &es[bi]; + let bytes = &objects[&base.content_hash]; + let at = |k: usize| format!("entry {}", first + k); + match decode_frames(ix.capability(base), bytes, ix.pieces[p].duration) { + Ok((frames, w, h)) => { + dims.get_or_insert((w, h)); + if base.prefix_tier == wsix::TIER_DECODE && crate::pack::clip_digest(&frames) != base.prefix_digest { + errs.push(reason("prefix-digest", format!("{}: the decode is not its prefix digest", at(bi)))); + } + } + Err(why) if base.prefix_tier == wsix::TIER_DECODE => errs.push(reason("prefix-digest", format!("{}: {why}", at(bi)))), + Err(_) => {} + } + let layers: Vec<(usize, &wsix::EntryRec)> = es.iter().enumerate().filter(|(_, e)| e.role == Role::Refinement).collect(); + if !layers.iter().any(|(_, e)| e.prefix_tier == wsix::TIER_DECODE) { + continue; + } + let limit = wai::staged::TOOL_DEFAULT_LIMIT; + let mut chain = match wai::staged::decode_base(bytes, &BTreeMap::new(), Some(&limit), None) { + Ok((info, units)) => wai::staged::StageChain::new(info, units, wai::staged::ChainConfig { + bound: wai::staged::HoldBound { max_envelopes: 0, max_age_ms: 0 }, + cost_limit: Some(wai::staged::layer_limit(&limit)), + budget: wai::staged::TOOL_DEFAULT_CHAIN_BUDGET, + record_budget: 0, + live: None, + }), + Err(e) => { + errs.push(reason("prefix-digest", format!("{}: the base its layers refine does not decode for the sink: {e}", at(bi)))); + continue; + } + }; + for (k, e) in layers { + let applied = chain.offer(&objects[&e.content_hash], 0, wai::staged::ReceiptEvidence::None); + let ok = applied.as_ref().is_ok_and(|r| r.iter().all(|x| x.envelope.is_none() && x.applied.len() as u32 == chain.n_units())); + if !ok { + errs.push(reason("prefix-digest", format!("{}: the layer does not apply to every frame of the stage below it", at(k)))); + break; + } + if e.prefix_tier == wsix::TIER_DECODE && chain.clip_sha256() != Some(e.prefix_digest) { + errs.push(reason("prefix-digest", format!("{}: the prefix does not decode to its prefix digest", at(k)))); + } + } + } + if errs.is_empty() { Ok(dims) } else { Err(errs) } +} + type FloorFiles = (Option, Vec<(String, Vec)>); /// The floor of a component as its objects give it (§7.2, §7.3, §7.6), and the CMAF files diff --git a/wai/wai-pack/tests/refusals.rs b/wai/wai-pack/tests/refusals.rs index f7c68978b..275891504 100644 --- a/wai/wai-pack/tests/refusals.rs +++ b/wai/wai-pack/tests/refusals.rs @@ -97,7 +97,8 @@ fn every_floor_reason() { } /// §11.4: what this revision does not pack yet is refused by name, every -/// `pack-capability-unsupported` naming its capability. +/// `pack-capability-unsupported` naming its capability; what it packs now (refinement +/// rungs, a rule's ladder subset, a still) packs. #[test] fn what_this_revision_does_not_pack() { let fl = floor_of(golden_floor()); @@ -108,26 +109,34 @@ fn what_this_revision_does_not_pack() { run(&|p: String| p.replace("[segmentation]", "[[component]]\nid = \"a\"\nmedia = \"audio\"\n\n[component.timebase]\nnum = 48000\nden = 1\n\n[[ladder]]\nid = \"ab\"\ncomponent = \"a\"\ncapability = \"wai.audio.flac\"\nrole = \"base\"\n\n[segmentation]")), "pack-media-unsupported" ); - // A refinement rung. - assert_eq!( - run(&|p: String| p.replace("[floor]", "[[ladder]]\nid = \"r1\"\ncomponent = \"v\"\ncapability = \"wai.video.int_refine\"\nrole = \"refinement\"\nrefines = \"b0\"\nparams = { q = 6 }\n\n[live]\nlayer_lag_ms = [0, 200]\n\n[floor]")), - "pack-refinement-unsupported" - ); - // A rule's ladder subset. - assert_eq!( - run(&|p: String| format!("{p}\n[[rule]]\nid = \"s\"\nwhen = {{ piece_kind = [\"static\"] }}\nthen = {{ ladder = [\"b0\", \"f0\"] }}\n")), - "pack-rule-ladder-unsupported" - ); - // A floor to encode. - assert_eq!(run(&|p: String| p.replace("params = { source = \"supplied\" }", "params = { source = \"encode\" }")), "pack-floor-encode-unsupported"); - // A base of another capability; two bases; a floor of another capability. + // A refinement rung, and a rule's ladder subset, pack. + let refinement = |cap: &str, over: &str| { + let (cap, over) = (cap.to_string(), over.to_string()); + move |p: String| p.replace("[floor]", &format!("[[ladder]]\nid = \"r1\"\ncomponent = \"v\"\ncapability = \"{cap}\"\nrole = \"refinement\"\nrefines = \"{over}\"\nparams = {{ q = 6 }}\n\n[live]\nlayer_lag_ms = [0, 200]\n\n[floor]")) + }; + assert_eq!(run(&refinement("wai.video.int_refine", "b0")), "ok"); + assert_eq!(run(&|p: String| format!("{p}\n[[rule]]\nid = \"s\"\nwhen = {{ piece_kind = [\"static\"] }}\nthen = {{ ladder = [\"b0\", \"f0\"] }}\n")), "ok"); + // A still as the base, alone or as a second base for a rule to choose: it packs. assert_eq!( run(&|p: String| p.replace("capability = \"wai.video.int_motion\"\nrole = \"base\"\nparams = { block = 16, search = 4, mv_precision = 1, q_candidates = [16, 12, 8], max_sse_px_milli = 30000 }", "capability = \"wai.image.png\"\nrole = \"base\"")), - "pack-capability-unsupported:wai.image.png" + "ok" + ); + assert_eq!(run(&|p: String| p.replace("[floor]", "[[ladder]]\nid = \"b1\"\ncomponent = \"v\"\ncapability = \"wai.image.png\"\nrole = \"base\"\n\n[floor]")), "ok"); + // A floor to encode. + assert_eq!(run(&|p: String| p.replace("params = { source = \"supplied\" }", "params = { source = \"encode\" }")), "pack-floor-encode-unsupported"); + // A base of a capability it does not code; a refinement of one, or over a base it does + // not refine; a floor of another capability. + assert_eq!( + run(&|p: String| p.replace("[floor]", "[[ladder]]\nid = \"b1\"\ncomponent = \"v\"\ncapability = \"wai.video.av1\"\nrole = \"base\"\n\n[floor]")), + "pack-capability-unsupported:wai.video.av1" ); assert_eq!( - run(&|p: String| p.replace("[floor]", "[[ladder]]\nid = \"b1\"\ncomponent = \"v\"\ncapability = \"wai.image.png\"\nrole = \"base\"\n\n[floor]")), - "pack-capability-unsupported:wai.image.png" + run(&|p: String| p.replace("[floor]", "[[ladder]]\nid = \"r1\"\ncomponent = \"v\"\ncapability = \"wai.image.int_refine\"\nrole = \"refinement\"\nrefines = \"b0\"\n\n[live]\nlayer_lag_ms = [0, 200]\n\n[floor]")), + "pack-capability-unsupported:wai.image.int_refine" + ); + assert_eq!( + run(&|p: String| refinement("wai.video.int_refine", "b1")(p.replace("[floor]", "[[ladder]]\nid = \"b1\"\ncomponent = \"v\"\ncapability = \"wai.image.png\"\nrole = \"base\"\n\n[floor]"))), + "pack-capability-unsupported:wai.video.int_refine" ); assert_eq!( run(&|p: String| p.replace("capability = \"wai.video.av1\"\nrole = \"floor\"\nparams = { source = \"supplied\" }", "capability = \"wai.image.png\"\nrole = \"floor\"")), diff --git a/wai/wai-pack/tests/staged.rs b/wai/wai-pack/tests/staged.rs new file mode 100644 index 000000000..8e2d84937 --- /dev/null +++ b/wai/wai-pack/tests/staged.rs @@ -0,0 +1,307 @@ +//! Staged packages, end to end, over the golden `video-staged-01` of +//! `wai/pack-conformance`: byte-identical packages with refinements and a still (test 5), +//! deduplication of a repeated piece and its layers (test 7), every prefix decoded by the +//! reference sink, the still over HTTP and MoQ, live packing equal to on-demand packing +//! (test 12), and delivery over a simulated link (test 13). + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use wai::stage_index::{Role, StageIndex}; +use wai_pack::golden; + +const PACK: &str = "video-staged-01"; + +fn corpus() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../pack-conformance") +} + +fn held(p: &Path) -> BTreeMap> { + wai::pin::read_dir_set(p).unwrap() +} + +fn golden_package() -> BTreeMap> { + held(&corpus().join("packs").join(PACK).join("package")) +} + +fn policy() -> Vec { + std::fs::read(corpus().join("packs").join(PACK).join("policy.toml")).unwrap() +} + +fn key() -> ed25519_dalek::SigningKey { + let t = std::fs::read_to_string(corpus().join("packs").join(PACK).join("key.hex")).unwrap(); + let b: Vec = (0..32).map(|i| u8::from_str_radix(&t.trim()[2 * i..2 * i + 2], 16).unwrap()).collect(); + ed25519_dalek::SigningKey::from_bytes(&b.try_into().unwrap()) +} + +fn index(files: &BTreeMap>) -> StageIndex { + StageIndex::parse(&files["index-v.wsix"]).unwrap() +} + +/// Test 5: the staged golden's inputs pack to the committed package byte for byte, and +/// again to the same bytes; the package verifies; its pieces carry two refinement layers, +/// a still, and layers on the event's deadline. +#[test] +fn the_staged_golden_is_byte_identical() { + let a = golden::pack_golden(&corpus(), PACK).unwrap(); + let want = golden_package(); + assert_eq!(a.files.keys().collect::>(), want.keys().collect::>()); + for (p, b) in &want { + assert!(a.files[p] == *b, "{p} differs from the committed golden"); + } + let b = golden::pack_golden(&corpus(), PACK).unwrap(); + assert_eq!(a.files, b.files, "a second pack writes the same bytes"); + wai_pack::verify::verify_files(&want).unwrap(); + let ix = index(&want); + let shape: Vec> = (0..ix.pieces.len()).map(|p| ix.piece_entries(p).iter().map(|e| (e.role, e.layer, ix.capability(e))).collect()).collect(); + let (b0, r, still, f) = ("wai.video.int_motion", "wai.video.int_refine", "wai.image.png", "wai.video.av1"); + let staged = vec![(Role::Base, 0, b0), (Role::Refinement, 1, r), (Role::Refinement, 2, r), (Role::Floor, 0, f)]; + assert_eq!(shape, vec![staged.clone(), staged.clone(), vec![(Role::Base, 0, still), (Role::Floor, 0, f)], staged.clone(), staged]); + let deadlines: Vec = ix.entries.iter().filter(|e| e.role == Role::Refinement).map(|e| e.deadline_ms).collect(); + assert_eq!(deadlines, vec![1500, 1500, 1500, 1500, 1500, 1500, 800, 800], "the event's moment puts the last piece's layers on 800 ms"); +} + +/// Test 7: the bumper played twice is one base and one object per layer, so the package +/// stores fewer bytes than it references; the counts are the index's. +#[test] +fn a_repeated_piece_is_one_object_with_its_layers() { + let pkg = golden_package(); + let ix = index(&pkg); + let hashes = |p: usize| ix.piece_entries(p).iter().map(|e| e.content_hash).collect::>(); + assert_eq!(hashes(1), hashes(3), "the bumper's base, layers and floor are the same objects"); + assert!(ix.piece_entries(3).iter().all(|e| e.flags & wai::stage_index::ENTRY_DUP != 0)); + let r = wai::package::PackageReceiptV2::from_json(std::str::from_utf8(&pkg["package.receipt.json"]).unwrap()).unwrap(); + let mut first: BTreeMap<[u8; 32], u64> = BTreeMap::new(); + for e in &ix.entries { + first.entry(e.content_hash).or_insert(e.byte_len); + } + assert_eq!((r.unique_objects, r.stored_bytes), (first.len() as u64, first.values().sum::())); + assert_eq!(r.total_entries - r.unique_objects, 4, "the second bumper's four objects"); +} + +/// Every decode-equivalent prefix decodes to its digest through the reference sink: each +/// base by its capability, each layer applied by `wai::staged::StageChain`; the still's +/// prefix is its frame held for the piece, and every layer brings its piece closer to the +/// source (its `sse` falls). +#[test] +fn every_prefix_decodes_to_its_digest() { + let pkg = golden_package(); + let ix = index(&pkg); + let obj = |e: &wai::stage_index::EntryRec| pkg[&wai_pack::objects::object_path(&e.content_hash)].clone(); + for p in 0..ix.pieces.len() { + let es = ix.piece_entries(p); + if ix.capability(&es[0]) == "wai.image.png" { + let w = wai::container::Wai::from_bytes(&obj(&es[0])).unwrap(); + let (_, _, rgb) = wai_pack::png::decode(&w.payload).unwrap(); + let held = vec![rgb; ix.pieces[p].duration as usize]; + assert_eq!(wai_pack::pack::clip_digest(&held), es[0].prefix_digest, "piece {p}: the still held for its piece"); + assert_eq!(es[0].sse, 0, "a still shot is its first frame"); + continue; + } + let (info, units) = wai::staged::decode_base(&obj(&es[0]), &BTreeMap::new(), None, None).unwrap(); + let mut c = wai::staged::StageChain::new(info, units, wai::staged::ChainConfig { + bound: wai::staged::HoldBound { max_envelopes: 0, max_age_ms: 0 }, + cost_limit: None, + budget: u64::MAX, + record_budget: 0, + live: None, + }); + assert_eq!(c.clip_sha256(), Some(es[0].prefix_digest)); + let mut sse = es[0].sse; + for e in es.iter().filter(|e| e.role == Role::Refinement) { + let r = c.offer(&obj(e), 0, wai::staged::ReceiptEvidence::None).unwrap(); + assert!(r.iter().all(|x| x.envelope.is_none() && x.applied.len() as u32 == c.n_units()), "piece {p} layer {}", e.layer); + assert_eq!(c.clip_sha256(), Some(e.prefix_digest), "piece {p} layer {}", e.layer); + assert!(e.sse < sse, "piece {p} layer {}: {} after {sse}", e.layer, e.sse); + sse = e.sse; + } + } +} + +/// §8.3, §8.4: the still rides the MoQ base track, and HTTP, whose variants cannot mix a +/// still with motion bases (http-delivery §4.2), plays the floor for its piece; the stage +/// view still lists it. The MoQ catalog is a stage set moq-streaming-format §4.1 accepts, +/// a receipts track per media track. +#[test] +fn a_still_rides_moq_and_the_floor_over_http() { + let pkg = golden_package(); + let ix = index(&pkg); + let still = (0..ix.pieces.len()).find(|&p| ix.capability(&ix.piece_entries(p)[0]) == "wai.image.png").unwrap(); + let media = String::from_utf8(pkg["hls/tv/wai-v.m3u8"].clone()).unwrap(); + let read = wai::http_binding::hls::parse_media_playlist(&media).unwrap(); + let gaps: Vec = read.segments.iter().enumerate().filter(|(_, s)| s.extra.gap).map(|(i, _)| i).collect(); + assert_eq!(gaps, vec![still], "tv plays the floor for the still's piece only"); + let master = String::from_utf8(pkg["hls/tv/wai.m3u8"].clone()).unwrap(); + assert!(master.contains("CODECS=\"wai1.video.int_motion+video.int_refine\""), "{master}"); + assert!(!master.contains("image.png")); + let view: serde_json::Value = serde_json::from_slice(&pkg["stages/tv.json"]).unwrap(); + assert_eq!(view["components"]["v"]["pieces"][still]["stages"].as_array().unwrap().len(), 1, "the stage view lists the still"); + let cat: serde_json::Value = serde_json::from_slice(&pkg["moq/catalog.json"]).unwrap(); + let l0 = cat["tracks"].as_array().unwrap().iter().find(|t| t["name"] == "v/l0").unwrap(); + assert_eq!(l0["wai_capabilities"], serde_json::json!(["wai.video.int_motion", "wai.image.png"])); + let verdicts = wai::staged::moq::check_catalog_in(&cat, Some(PACK)); + assert_eq!(verdicts, vec![("v".to_string(), Ok(()))]); + let media_tracks = cat["tracks"].as_array().unwrap().iter().filter(|t| t["packaging"] == "wai").count(); + let receipts = cat["tracks"].as_array().unwrap().iter().filter(|t| t["packaging"] == "wai-receipts").count(); + assert_eq!((media_tracks, receipts), (5, 5), "l0, l1@800, l1, l2@800, l2, each with its receipts track"); +} + +/// The frames of a fixture, pushed one at a time into a live packer. +fn live(policy: &[u8], fixture: &str, window_pieces: u64) -> (Vec, Vec, Vec) { + let (hd, frames) = wai_pack::source::y4m::read_all(&wai_pack::fixture::generate(fixture).unwrap()).unwrap(); + let k = key(); + let id = wai::signer_id::did_key(&k.verifying_key().to_bytes()); + let mut o = wai_pack::live::LiveOpts::new(k, id); + o.window_pieces = window_pieces; + let mut lp = wai_pack::live::LivePacker::new(policy, hd.w, hd.h, o).unwrap(); + let mut out = Vec::new(); + for f in &frames { + out.extend(lp.push(&f.rgb).unwrap()); + } + let (rest, windows, starts) = lp.finish().unwrap(); + out.extend(rest); + (out, windows, starts) +} + +fn listing_of(files: &BTreeMap>) -> Vec { + let view: BTreeMap = files.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect(); + wai::pin::listing_from(&view).unwrap() +} + +/// Test 12: a live pack of the staged golden's source, a frame at a time, writes the +/// on-demand package's plan and every one of its bases and refinements, byte for byte +/// (the primary listing; a live pack has no floor), in windows of two pieces that each +/// verify against their receipt and chain to the window before. +#[test] +fn live_is_on_demand() { + let (published, windows, starts) = live(&policy(), PACK, 2); + let mut files: BTreeMap> = published.iter().map(|p| (wai_pack::objects::object_path(&p.hash), p.envelope.clone())).collect(); + files.insert("plan-v.txt".into(), wai_pack::pack::plan_file(&starts).into_bytes()); + let vod = wai_pack::live::primary_files(&golden_package()).unwrap(); + assert_eq!(listing_of(&files), listing_of(&vod), "the primary listing"); + assert_eq!(windows.len(), 3, "five pieces in windows of two"); + let mut prev: Option<(&wai_pack::live::LiveWindow, StageIndex)> = None; + let mut piece = 0; + for w in &windows { + let ix = StageIndex::parse(&w.index).unwrap(); + ix.check().unwrap(); + w.receipt.verify_detailed(&[&w.index]).unwrap(); + assert_eq!(ix.header.first_piece, piece); + match &prev { + None => { + assert_eq!(w.receipt.parent, wai::package::Parent::None); + assert_eq!(ix.header.prev_index_digest, [0; 32]); + } + Some((pw, pix)) => { + assert_eq!(w.receipt.parent, wai::package::Parent::LiveWindow(pw.receipt.receipt_hash())); + assert_eq!(ix.header.prev_index_digest, pix.digest()); + assert_eq!(ix.header.first_entry, pix.header.first_entry + pix.entries.len() as u64); + } + } + piece += ix.pieces.len() as u64; + prev = Some((w, ix)); + } + // A piece is published once the frame that decides its end has arrived: the next + // piece's start plus the lookahead (6 frames), or the source's last frame. + let at: Vec<(u64, u64)> = published.iter().filter(|p| p.layer == 0).map(|p| (p.piece, p.at_frame)).collect(); + assert_eq!(at, vec![(0, 56), (1, 81), (2, 121), (3, 146), (4, 199)]); +} + +/// Test 12 over the command line: `wai-pack live` fed the source on standard input +/// writes the on-demand package's primary listing. +#[test] +fn live_over_a_pipe_is_on_demand() { + use std::io::Write as _; + let dir = std::env::temp_dir().join(format!("wai-pack-live-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let g = corpus().join("packs").join(PACK); + let mut child = std::process::Command::new(env!("CARGO_BIN_EXE_wai-pack")) + .args(["live", "--policy", g.join("policy.toml").to_str().unwrap(), "--out", dir.to_str().unwrap(), "--key", g.join("key.hex").to_str().unwrap()]) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::null()) + .spawn() + .unwrap(); + child.stdin.take().unwrap().write_all(&wai_pack::fixture::generate(PACK).unwrap()).unwrap(); + assert!(child.wait().unwrap().success()); + let listing = |d: &Path| std::process::Command::new(env!("CARGO_BIN_EXE_wai-pack")).args(["listing", d.to_str().unwrap(), "--primary"]).output().unwrap().stdout; + let got = listing(&dir); + assert!(!got.is_empty()); + assert_eq!(got, listing(&g.join("package"))); + let _ = std::fs::remove_dir_all(&dir); +} + +/// Test 12's counter-case: with one frame less of lookahead than the on-demand cut, the +/// live packer cuts `cuts-01`'s flash, so the two plans part. +#[test] +fn a_shorter_lookahead_cuts_the_flash() { + let p = std::fs::read(corpus().join("scenecut/policy.toml")).unwrap(); + let (_, _, six) = live(&p, "cuts-01", 0); + let five = String::from_utf8(p.clone()).unwrap().replace("lookahead_frames = 6", "lookahead_frames = 5"); + let (_, _, short) = live(five.as_bytes(), "cuts-01", 0); + let want: Vec = serde_json::from_slice::(&std::fs::read(corpus().join("scenecut/expected.json")).unwrap()).unwrap()["fixtures"] + .as_array() + .unwrap() + .iter() + .find(|f| f["name"] == "cuts-01") + .unwrap()["pieces"] + .as_array() + .unwrap() + .iter() + .map(|p| p["start"].as_u64().unwrap()) + .collect(); + assert_eq!(six, want, "the live cut is the on-demand one"); + assert!(short.contains(&90), "the flash (frames 90 to 95) cuts at L = 5: {short:?}"); + assert_ne!(short, six); +} + +/// Test 13: the staged golden packed live and delivered to a `tv` subscriber over a +/// simulated 40 bytes/ms link, as `delivery/expected.json` states it: every base arrives +/// by its piece's instant under §8.5's priorities, and refinements are dropped on their +/// tracks' timeouts; every stall is within staged-measurement §10.4's bound; every object +/// delivered is accepted by its group receipt's Merkle proof, and a dropped one is held +/// by inclusion alone. Without §8.5's priorities (fifo), the second piece loses its layer; +/// capped at layer 1, no layer-2 object is sent. +#[test] +fn delivery_over_a_simulated_link() { + let expected: serde_json::Value = serde_json::from_slice(&std::fs::read(corpus().join("delivery/expected.json")).unwrap()).unwrap(); + let src = wai_pack::fixture::generate(PACK).unwrap(); + let k = ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]); + let mut seen = Vec::new(); + for (name, plan) in golden::delivery_plans() { + let d = wai_pack::delivery::simulate_source(&policy(), &src, golden::DELIVERY_CLASS, &plan, &k).unwrap(); + let case = expected["cases"].as_array().unwrap().iter().find(|c| c["name"] == name).unwrap(); + assert_eq!(serde_json::json!(d.presented()), case["presented"], "{name}"); + let mut got = d.to_json(); + got.as_object_mut().unwrap().remove("plan"); + assert_eq!(got, case["outcome"], "{name}"); + wai::stage_measure::sim::check_stall(&d.model, &d.plan).unwrap_or_else(|v| panic!("{name}: {v:?}")); + if name != "fifo" { + assert!(d.pieces.iter().all(|p| p.base_arrival_us.is_some_and(|a| a <= p.due_us)), "{name}: every base by its instant"); + } + // The receipts: a chain per track, every delivered object accepted, a dropped + // one by inclusion only. + let published = { + let (out, _, _) = live(&policy(), PACK, 0); + out + }; + let by_id: BTreeMap = published.iter().map(|p| (format!("p{}/l{}", p.piece, p.layer), p)).collect(); + let mut heads: BTreeMap = BTreeMap::new(); + for ((o, ev), (g, r)) in d.model.objects.iter().zip(&d.result.as_ref().unwrap().objects).zip(&d.receipts) { + g.check_parent(heads.get(&g.track_alias).copied()).unwrap(); + heads.insert(g.track_alias, g); + let env = &by_id[&o.id].envelope; + match ev.state { + wai::stage_measure::sim::ObjState::Delivered => { + r.accept(g, env).unwrap(); + let mut bad = env.clone(); + *bad.last_mut().unwrap() ^= 1; + assert!(r.accept(g, &bad).is_err()); + } + _ => assert_eq!(r.verify_against(g, None).unwrap(), wai::jwp_delivery::Evidence::InclusionVerified), + } + } + seen.push((name, d.presented(), d.result.as_ref().unwrap().never_sent_capped)); + } + assert_eq!(seen[0].1, vec![0, 1, 0, 1, 0]); + assert_eq!(seen[1].1, vec![0, 0, 0, 1, 0], "fifo: the second piece's layer waits behind the first's and is dropped"); + assert!(seen[2].2 > 0 && seen[2].1.iter().all(|&l| l <= 1), "capped at layer 1"); +} diff --git a/wai/wai-rs/src/codecs/registry.rs b/wai/wai-rs/src/codecs/registry.rs index e7a0133ec..7df42bbb3 100644 --- a/wai/wai-rs/src/codecs/registry.rs +++ b/wai/wai-rs/src/codecs/registry.rs @@ -513,7 +513,6 @@ pub const WIRE_IDENTIFIERS: &[WireIdentifier] = &[ wire("science.transaction.wai.stages", WireKind::DataId, PACKAGING, "§8", WireStatus::Defined), wire("tag:transaction.science,2026:wai/class", WireKind::Scheme, PACKAGING, "§8", WireStatus::Defined), wire("tag:transaction.science,2026:wai/stages", WireKind::Scheme, PACKAGING, "§8", WireStatus::Defined), - wire("wai_layer", WireKind::Catalog, PACKAGING, "§8", WireStatus::Defined), wire("wai_delivery_timeout_ms", WireKind::Catalog, PACKAGING, "§8", WireStatus::Defined), ]; -- 2.54.0 (Apple Git-157) From 103ef76156f8d7c55ce6254c1238434ba634b69b Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 14:21:54 -0400 Subject: [PATCH 03/21] ci, changelog: wai-pack's staged packages - wai-pack.yml: the push filter lists the staged-delivery and staged-measurement verifiers pack_verify.py now imports; the test-count floor covers tests/staged.rs and the library's new tests; stage_diff.py runs against the debug and the release build; the media job reads both goldens' floors. - CHANGELOG: the staged packages, the MoQ stage set, live windows, simulated delivery and the simulator's per-object timeout. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-pack.yml | 58 ++++++++++++++++++++++++---------- wai/CHANGELOG.md | 43 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/.github/workflows/wai-pack.yml b/.github/workflows/wai-pack.yml index 2fc67964a..31cfe2a71 100644 --- a/.github/workflows/wai-pack.yml +++ b/.github/workflows/wai-pack.yml @@ -9,15 +9,19 @@ name: wai-pack # packed again byte for byte and verified, every mutation's reason. # - independent: the corpus recomputed by its second implementation, written in Python # from the extension text and sharing no code with wai-pack, the golden packages -# verified and re-derived from their sources among it. +# verified and re-derived from their sources among it (every refinement layer and +# still byte for byte, every delivery case through the staged-measurement simulator's +# Python twin); and the differentials: source readers, policies, scene cuts, and the +# staged packer (layers, stills, piece plans, deliveries). # - media: the golden's floor, its CMAF initialization segment and fragments, read and # decoded by a media prober (test 14). # - licences: every crate in wai-pack's dependency graph is under a licence deny.toml # allows; a source-available licence fails it. # # Push filters match the push's delta, so every path a build or a verifier reads is -# listed: the crate, the wai crate it builds on, the corpus, the BLAKE3 and integer -# payload verifiers and the registry export pack_verify.py reads, and this file. +# listed: the crate, the wai crate it builds on, the corpus, the BLAKE3, integer payload, +# staged-delivery and staged-measurement verifiers and the registry export pack_verify.py +# reads, and this file. concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -32,6 +36,9 @@ on: - 'wai/claim-conformance/_blake3.py' - 'wai/claim-conformance/_ed25519.py' - 'wai/int-payload-conformance/int_payload_verify.py' + - 'wai/staged-conformance/staged_verify.py' + - 'wai/staged-measure-conformance/sim_verify.py' + - 'wai/staged-measure-conformance/measure_verify.py' - 'wai/registry/capabilities.json' - '.github/workflows/wai-pack.yml' push: @@ -43,6 +50,9 @@ on: - 'wai/claim-conformance/_blake3.py' - 'wai/claim-conformance/_ed25519.py' - 'wai/int-payload-conformance/int_payload_verify.py' + - 'wai/staged-conformance/staged_verify.py' + - 'wai/staged-measure-conformance/sim_verify.py' + - 'wai/staged-measure-conformance/measure_verify.py' - 'wai/registry/capabilities.json' - '.github/workflows/wai-pack.yml' @@ -96,13 +106,14 @@ jobs: echo "$target: $n tests (floor $floor)" [ "$n" -ge "$floor" ] || { echo "::error::$target lists $n tests, under its floor of $floor"; fail=1; } done <<'EOF' - --lib 28 + --lib 33 --test=conformance 1 --test=packs 7 --test=review 9 --test=refusals 5 --test=scenecut 3 --test=source_alloc 1 + --test=staged 8 EOF exit $fail @@ -164,6 +175,16 @@ jobs: shell: bash working-directory: wai/wai-pack run: python3 ../pack-conformance/segment_diff.py target/release/wai-pack 1 2000 + # The staged packer differentially: refinement layers, stills, piece plans and + # deliveries on seeded random inputs, every result of wai-pack equal to + # pack_verify.py's (debug and release, both with overflow checks). + - name: stage_diff.py, wai-pack against pack_verify.py + shell: bash + working-directory: wai/wai-pack + run: | + set -euo pipefail + python3 ../pack-conformance/stage_diff.py target/debug/wai-pack 1 1000 + python3 ../pack-conformance/stage_diff.py target/release/wai-pack 2 3000 media: name: wai-pack · the floor through a media prober @@ -179,22 +200,25 @@ jobs: # Test 14: the golden's init.mp4 and its fragments, in its floor playlist's order, # read as one AV1 track of every frame of the source, with a key frame at each piece # start of the plan and nowhere else, and decode without an error. - - name: The golden's floor reads and decodes + - name: The goldens' floors read and decode shell: bash - working-directory: wai/pack-conformance/packs/video-base-01/package + working-directory: wai/pack-conformance/packs run: | set -euo pipefail - tmp=$(mktemp -d) - cat floor/v/init.mp4 $(grep -v '^#' hls/classic/floor-v.m3u8 | sed 's#^\.\./\.\./##') > "$tmp/floor.mp4" - ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=codec_name,width,height,nb_read_frames -of default=nw=1 "$tmp/floor.mp4" | tee "$tmp/probe.txt" - grep -qx 'codec_name=av1' "$tmp/probe.txt" - grep -qx 'nb_read_frames=200' "$tmp/probe.txt" - keys=$(ffprobe -v error -select_streams v:0 -show_entries packet=pts,flags -of csv=p=0 "$tmp/floor.mp4" | awk -F, '$2 ~ /K/ {print $1}' | tr '\n' ' ') - want=$(tr '\n' ' ' < plan-v.txt) - echo "key frames: $keys" - [ "$keys" = "$want" ] || { echo "::error::key frames at $keys; the plan says $want"; exit 1; } - ffmpeg -v error -xerror -i "$tmp/floor.mp4" -f null - - echo "the floor reads as one AV1 track of 200 frames, a key frame at each piece start" + for g in video-base-01 video-staged-01; do + ( cd "$g/package" + tmp=$(mktemp -d) + cat floor/v/init.mp4 $(grep -v '^#' hls/classic/floor-v.m3u8 | sed 's#^\.\./\.\./##') > "$tmp/floor.mp4" + ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=codec_name,width,height,nb_read_frames -of default=nw=1 "$tmp/floor.mp4" | tee "$tmp/probe.txt" + grep -qx 'codec_name=av1' "$tmp/probe.txt" + grep -qx 'nb_read_frames=200' "$tmp/probe.txt" + keys=$(ffprobe -v error -select_streams v:0 -show_entries packet=pts,flags -of csv=p=0 "$tmp/floor.mp4" | awk -F, '$2 ~ /K/ {print $1}' | tr '\n' ' ') + want=$(tr '\n' ' ' < plan-v.txt) + echo "$g key frames: $keys" + [ "$keys" = "$want" ] || { echo "::error::$g: key frames at $keys; the plan says $want"; exit 1; } + ffmpeg -v error -xerror -i "$tmp/floor.mp4" -f null - + echo "$g: the floor reads as one AV1 track of 200 frames, a key frame at each piece start" ) + done licences: name: wai-pack · dependency licences diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index 112c348c5..667baebbb 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -6,6 +6,49 @@ wire format may change and such changes are recorded here. ## v0.4.0 — current +### Packaging: refinement layers, stills, a piece's ladder, live windows and delivery + +- **Refinement layers in packages** ([packaging](extensions/packaging.md) §5.4). For + each refinement rung of a piece's ladder the packer writes a `wai.video.int_refine` + object over the piece's `wai.video.int_motion` base: the manifest `wai stage refine` + writes, and a `WIR1` from `wai-rs`'s refinement encoder (`staged::wir1::encode`), one + unit per frame, toward the source from the stage below as a sink reconstructs it. Each + layer is applied to the decoded base by the reference sink before it is kept; the + index states each prefix's tier, digest, SSE and deadline. +- **A piece's ladder, and stills** (§5.7). A rule's `then.ladder` chooses the rungs a + piece is coded in; otherwise the component's first base rung, its refinement chain and + its floors. §3.4 item 10 now requires a rule's ladder to name exactly one base, every + refinement over a rung it names, and every floor of its component. A base of + `wai.image.png` is a still: the piece's first frame, held for the piece, as a PNG every + byte of which is stated (the `Sub` filter, one fixed-Huffman deflate block whose + matches are stated), so an independent implementation writes the same file. +- **Catalogs** (§8.3, §8.4). The MoQ catalog is now a stage set of moq-streaming-format + §4.1, written by `wai::staged::moq`: `wai_staged` on every track, a receipts track and + an alias per media track, `depends` on a track of every layer below; it replaces + `priority` (MOQT's own, §8.5) and `wai_layer`, which the registry no longer lists. Over + HTTP, whose variants cannot mix bases with no capability in common (http-delivery §4.2), + a still's piece is played from the floor; its stage view and MoQ tracks list it. +- **Live windows** (§9.2). `wai-pack live` packs a Y4M stream from standard input as it + arrives, with the coder the on-demand packer uses, in windows sealed by version-2 + receipts chained `live-window`; its bases and refinements are the on-demand package's, + byte for byte. `wai-pack listing --primary` lists what the two share. +- **Delivery, simulated** (§8.9). `wai_pack::delivery` and `wai-pack sim` run + staged-measurement's simulator over what a live packer publishes and what a class + subscribes to: per-track delivery timeouts, §8.5's priorities, the subscriber's cap, + group receipts on each track's chain. The simulator's model gains a refinement's own + `timeout_us`, and §10.4's bound takes the longest refinement timeout + ([staged-measurement](extensions/staged-measurement.md) Appendix B); its corpus adds a + per-track model. +- **The corpus**: the golden `video-staged-01` (a moving shot, a bumper played twice, a + still shot, a second moving shot; two refinement layers, a still, the event's deadline, + three classes), its supplied floor and how it was written (`supplied_floor.py`), + `staged-mutations/`, and `delivery/`. `stage_diff.py` runs refinement layers, stills, + piece plans and deliveries differentially against `pack_verify.py`, which re-derives + every layer and still from the source byte for byte. `pack-refinement-unsupported` and + `pack-rule-ladder-unsupported` are gone. +- The base golden `video-base-01` keeps its objects, index and receipt; its MoQ catalog + and its `LISTING` change. + ### Staged measurement and the session claim in the browser - **The measurement core, bound for JS** ([staged-measurement](extensions/staged-measurement.md)). -- 2.54.0 (Apple Git-157) From c31ea0556ecbd98e0a08914c39b92bdae0c17d78 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 15:09:34 -0400 Subject: [PATCH 04/21] =?UTF-8?q?wai:=20one=20stall=20bound,=20of=20a=20mo?= =?UTF-8?q?del=20=E2=80=94=20wai-web=20prints=20the=20bound=20the=20native?= =?UTF-8?q?=20tool=20prints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wai-web's simulated() printed the plan-only bound while wai_stage_measure prints the model's, so a per-track case (refinements with timeouts of their own) gave 35000 against the corpus's 65000 and measure::tests failed. - wai-web: `stall_bound_for(model, &p)`. - stage_measure::sim: the plan-only bound is gone from the API; §10.4's bound is `stall_bound_for(model, plan)` alone, its documentation the full rule. The simulator's tests keep a plan-only helper over `bound_with`. Co-Authored-By: Claude Opus 5.5 --- wai/wai-rs/src/stage_measure/sim.rs | 23 ++++++++--------------- wai/wai-rs/src/stage_measure/sim/tests.rs | 23 ++++++++++++++--------- wai/wai-web/src/measure.rs | 2 +- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/wai/wai-rs/src/stage_measure/sim.rs b/wai/wai-rs/src/stage_measure/sim.rs index 5b2fe2e57..6a2f71e64 100644 --- a/wai/wai-rs/src/stage_measure/sim.rs +++ b/wai/wai-rs/src/stage_measure/sim.rs @@ -8,7 +8,7 @@ //! units and counted bytes ([`Model::from_evaluation`] takes them from an evaluated //! title). A [`Plan`] (`wai-sim/1`) is the link, the sender's schedule, carriage, //! layer cap and delivery timeouts, and the sink's decode workers, order, times and -//! playout deadlines. [`simulate`] runs one against the other; [`stall_bound`] states +//! playout deadlines. [`simulate`] runs one against the other; [`stall_bound_for`] states //! the bound §10.4 gives a plan on how far its refinements may delay a base, and //! [`check_stall`] checks a plan against it. @@ -739,27 +739,20 @@ fn simulate_once(m: &Model, p: &Plan) -> Result { }) } -/// §10.4's bound on how far a plan's refinements may delay a base's presentation: -/// `S_link + S_decode`, or `None` where either part is unbounded. +/// §10.4's bound on how far a plan's refinements may delay a base's presentation over a +/// model: `S_link + S_decode`, or `None` where either part is unbounded. /// -/// - `S_link`: one chunk's serialisation under `strict-priority`; under `fifo` with a -/// refinement timeout `T`, `T` plus one chunk; otherwise unbounded. +/// - `S_link`: one chunk's serialisation under `strict-priority`; under `fifo`, `T` plus +/// one chunk, where `T` is the longest timeout any refinement of `m` takes (its own, +/// where the model gives it one, or the plan's), and unbounded where one takes none. /// - `S_decode`: under `base-first` decode, one refinement's decode time; otherwise /// unbounded. /// /// A plan with a base timeout has no bound: a base the refinements delay past its own /// timeout is not presented at all. §10.4 proves the bound for every other plan with a /// bounded link schedule and `base-first` decode, given that bases are sent and decoded -/// in the order they became available, as [`simulate`] sends and decodes them. -/// -/// This is the bound over a model whose objects carry no timeout of their own; a model -/// that gives refinements their own is bounded by [`stall_bound_for`]. -pub fn stall_bound(p: &Plan) -> Option { - bound_with(p, p.timeout_refinement_us) -} - -/// §10.4's bound for `p` over `m`: under `fifo`, `T` is the longest timeout any -/// refinement of `m` takes, its own or the plan's, and there is none where one takes none. +/// in the order they became available, as [`simulate`] sends and decodes them. There is +/// no bound of a plan alone: a refinement's own timeout is the model's. pub fn stall_bound_for(m: &Model, p: &Plan) -> Option { let mut t: Option = None; for o in m.objects.iter().filter(|o| o.layer >= 1) { diff --git a/wai/wai-rs/src/stage_measure/sim/tests.rs b/wai/wai-rs/src/stage_measure/sim/tests.rs index c33567a53..5f54a8c0a 100644 --- a/wai/wai-rs/src/stage_measure/sim/tests.rs +++ b/wai/wai-rs/src/stage_measure/sim/tests.rs @@ -23,6 +23,11 @@ impl Rng { } } +/// §10.4's bound of a plan over a model whose refinements carry no timeout of their own. +fn plan_only_bound(p: &Plan) -> Option { + bound_with(p, p.timeout_refinement_us) +} + fn obj(id: &str, base: u32, layer: u8, from: u64, count: u64, bytes: u64, capture_us: Option) -> SimObject { SimObject { id: id.into(), base, layer, from, count, bytes, capture_us, timeout_us: None } } @@ -216,7 +221,7 @@ fn strict_priority_bound() { let carriage = if r.coin() { Carriage::Datagram } else { Carriage::Subgroup }; let t = if r.coin() { Some(r.range(0, 300_000)) } else { None }; let p = plan(&mut r, Schedule::StrictPriority, carriage, t, DecodeOrder::BaseFirst); - assert!(stall_bound(&p).is_some()); + assert!(plan_only_bound(&p).is_some()); conserved(&simulate(&m, &p).unwrap()); check_stall(&m, &p).unwrap_or_else(|v| panic!("plan {i}: {v:?} {p:?}")); } @@ -262,7 +267,7 @@ fn bound_holds_on_wild_plans() { let mut r = Rng(0x5eed_0000 + seed); let m = wild_model(&mut r); let p = wild_plan(&mut r, &m); - assert!(stall_bound(&p).is_some()); + assert!(plan_only_bound(&p).is_some()); let res = simulate(&m, &p).unwrap_or_else(|e| panic!("seed {seed}: {e}")); conserved(&res); check_stall(&m, &p).unwrap_or_else(|v| panic!("seed {seed}: {v:?}\n{p:?}\n{}", m.to_json())); @@ -356,7 +361,7 @@ fn bound_holds_on_targeted_plans() { let mut r = Rng(0x7a76_e7ed); for i in 0..TARGETED_DRAWS { let (m, p) = targeted(&mut r); - assert!(stall_bound(&p).is_some()); + assert!(plan_only_bound(&p).is_some()); let res = simulate(&m, &p).unwrap_or_else(|e| panic!("draw {i}: {e}\n{p:?}\n{}", m.to_json())); conserved(&res); check_stall(&m, &p).unwrap_or_else(|v| panic!("draw {i}: {v:?}\n{p:?}\n{}", m.to_json())); @@ -455,8 +460,8 @@ fn fifo_no_timeout_violates() { let m = counter_model(); let fifo = counter_plan(Schedule::Fifo, None, DecodeOrder::BaseFirst); let strict = counter_plan(Schedule::StrictPriority, None, DecodeOrder::BaseFirst); - assert_eq!(stall_bound(&fifo), None); - let bound = stall_bound(&strict).unwrap(); + assert_eq!(plan_only_bound(&fifo), None); + let bound = plan_only_bound(&strict).unwrap(); check_stall(&m, &strict).unwrap(); let worst = simulate(&m, &fifo).unwrap().max_stall().unwrap(); assert!(worst > bound as i64, "fifo with no timeout stalls {worst} µs, within the strict bound {bound}"); @@ -475,9 +480,9 @@ fn fifo_decode_violates() { let fifo = Plan { vod_window_us: Some(180_000), ..counter_plan(Schedule::StrictPriority, None, DecodeOrder::Fifo) }; let base_first = Plan { decode_order: DecodeOrder::BaseFirst, ..fifo.clone() }; check_stall(&m, &base_first).unwrap(); - assert_eq!(stall_bound(&fifo), None); + assert_eq!(plan_only_bound(&fifo), None); let worst = simulate(&m, &fifo).unwrap().max_stall().unwrap(); - assert!(worst > stall_bound(&base_first).unwrap() as i64, "fifo decode stalls {worst} µs"); + assert!(worst > plan_only_bound(&base_first).unwrap() as i64, "fifo decode stalls {worst} µs"); } #[test] @@ -563,7 +568,7 @@ fn sums_past_64_bits_are_refused() { // A decode that would end past 64 bits. let small = Model { unit_us: 40_000, objects: vec![obj("B0", 0, 0, 0, 1, 10, None)] }; assert_eq!(simulate(&small, &Plan { chunk_bytes: 1000, bytes_per_ms: 1000, decode_base_us: u64::MAX, ..p.clone() }), Err(SimError::Overflow)); - assert_eq!(stall_bound(&p), None, "a bound past 64 bits is none"); + assert_eq!(plan_only_bound(&p), None, "a bound past 64 bits is none"); // The review's model, read from its JSON: its byte counts pass 2^53 - 1. let o = serde_json::json!({"format": "wai-sim-model/1", "unit_us": 40000, "objects": [ {"id": "B0", "base": 0, "layer": 0, "units": [0, 1], "bytes": 18446744073709551u64, "capture_us": null}, @@ -613,7 +618,7 @@ fn a_refinement_s_own_timeout() { // Under fifo, the bound's T is the longest refinement timeout, its own or the plan's; // with every refinement timed, a plan with none of its own is bounded. let fifo = Plan { schedule: Schedule::Fifo, timeout_refinement_us: None, ..p.clone() }; - assert_eq!(stall_bound(&fifo), None); + assert_eq!(plan_only_bound(&fifo), None); m.objects[2].timeout_us = Some(7000); assert_eq!(stall_bound_for(&m, &fifo), Some(7000 + 1000 + 1)); assert_eq!(stall_bound_for(&m, &Plan { timeout_refinement_us: Some(9000), ..fifo.clone() }), Some(7000 + 1000 + 1), "own timeouts replace the plan's"); diff --git a/wai/wai-web/src/measure.rs b/wai/wai-web/src/measure.rs index d94426c76..0687eded5 100644 --- a/wai/wai-web/src/measure.rs +++ b/wai/wai-web/src/measure.rs @@ -123,7 +123,7 @@ pub(crate) fn simulated(model: &Model, plan_json: &str) -> Result Date: Thu, 8 Oct 2026 15:09:34 -0400 Subject: [PATCH 05/21] wai: staged_resources counts each thread's allocations, not the process's The counters were process-wide, so libtest's main thread, which allocates a 594-byte message when a test passes 60 seconds, landed in the parse window on a loaded host: 1,310,720 + 594 = 1,311,314 bytes against 1,310,721 declared. - The allocator keeps per-thread counts (`thread_local!` `const` `Cell`s, no destructor, reached with `try_with`) of bytes allocated, allocations, bytes live (signed: a thread may free what another allocated) and their peak, and every window reads the measuring thread's. - A new check: a megabyte another thread allocates inside a window does not count, and one this thread allocates does. Co-Authored-By: Claude Opus 5.5 --- wai/wai-rs/tests/staged_resources.rs | 109 +++++++++++++++++++-------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/wai/wai-rs/tests/staged_resources.rs b/wai/wai-rs/tests/staged_resources.rs index b91bff0e6..3e5991480 100644 --- a/wai/wai-rs/tests/staged_resources.rs +++ b/wai/wai-rs/tests/staged_resources.rs @@ -1,6 +1,9 @@ //! The staged sink's own work and memory (`extensions/staged-delivery.md` §4.5 and -//! Appendix A.5), measured with a global allocator that counts the bytes allocated, -//! the allocations, the bytes live and their peak. +//! Appendix A.5), measured with a global allocator that counts, for each thread, the +//! bytes it allocated, its allocations, the bytes live and their peak. The counts are +//! the measuring thread's own: another thread's allocations (the test harness's, which +//! allocates a message on its main thread when a test passes 60 seconds) never land in +//! a window. //! //! - Work per layer is linear in its units: an offer of a layer over 4096, 16384 and //! 65536 units (one layer alone; a layer whose `prev` is a run per unit, held; a @@ -15,12 +18,12 @@ //! many layers that conflict, or that expire, leaves the sink holding what it held. //! - The parse holds no more than the declared working bytes beyond the payload. //! -//! Its own test binary with a single test, so that no other test's allocations land in -//! the windows it measures. +//! Its own test binary with a single test, and the counts per thread, so that no other +//! test's or thread's allocations land in the windows it measures. #![cfg(all(feature = "staged", feature = "video_int"))] use std::alloc::{GlobalAlloc, Layout, System}; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::cell::Cell; use std::time::Duration; use wai::codecs::{DeterminismTier, CAP_VIDEO_INT_MOTION, CAP_VIDEO_INT_REFINE}; @@ -28,17 +31,48 @@ use wai::container::{Conditioning, Manifest, ModelRequirement, Prev, PrevRun, Re use wai::staged::wir1::{self, Below, Predictor, RefineParams, UnitKind}; use wai::staged::{BaseInfo, ChainConfig, HoldBound, ReceiptEvidence, Samples, StageRefusal, StageSink, UnitBuf}; -/// The system allocator, counting bytes allocated, allocations, bytes live and peak. +/// The system allocator, counting for the calling thread the bytes it allocated, its +/// allocations, the bytes live and their peak. Live bytes are signed: a thread may free +/// what another allocated. struct Count; -static ALLOCATED: AtomicUsize = AtomicUsize::new(0); -static CALLS: AtomicUsize = AtomicUsize::new(0); -static CUR: AtomicUsize = AtomicUsize::new(0); -static PEAK: AtomicUsize = AtomicUsize::new(0); + +/// One thread's counts. `const`-initialised `Cell`s with no destructor, so reaching them +/// from the allocator allocates nothing; `try_with` skips a thread being torn down. +struct Counts { + allocated: Cell, + calls: Cell, + cur: Cell, + peak: Cell, +} + +thread_local! { + static COUNTS: Counts = const { Counts { allocated: Cell::new(0), calls: Cell::new(0), cur: Cell::new(0), peak: Cell::new(0) } }; +} fn grow(n: usize) { - ALLOCATED.fetch_add(n, Relaxed); - CALLS.fetch_add(1, Relaxed); - PEAK.fetch_max(CUR.fetch_add(n, Relaxed) + n, Relaxed); + let _ = COUNTS.try_with(|c| { + c.allocated.set(c.allocated.get().wrapping_add(n)); + c.calls.set(c.calls.get().wrapping_add(1)); + let cur = c.cur.get().wrapping_add(n as isize); + c.cur.set(cur); + if cur > c.peak.get() { + c.peak.set(cur); + } + }); +} + +fn shrink(n: usize) { + let _ = COUNTS.try_with(|c| c.cur.set(c.cur.get().wrapping_sub(n as isize))); +} + +/// The calling thread's live bytes. +fn cur() -> isize { + COUNTS.with(|c| c.cur.get()) +} + +/// The calling thread's live bytes past `before`, or 0 below it. +fn grown_since(before: isize) -> usize { + (cur() - before).max(0) as usize } unsafe impl GlobalAlloc for Count { @@ -54,12 +88,12 @@ unsafe impl GlobalAlloc for Count { if new >= l.size() { grow(new - l.size()); } else { - CUR.fetch_sub(l.size() - new, Relaxed); + shrink(l.size() - new); } unsafe { System.realloc(p, l, new) } } unsafe fn dealloc(&self, p: *mut u8, l: Layout) { - CUR.fetch_sub(l.size(), Relaxed); + shrink(l.size()); unsafe { System.dealloc(p, l) } } } @@ -110,13 +144,19 @@ fn now() -> Duration { } fn measure(f: impl FnOnce() -> T) -> (T, Window) { - let start = CUR.load(Relaxed); - PEAK.store(start, Relaxed); - let (a0, c0) = (ALLOCATED.load(Relaxed), CALLS.load(Relaxed)); + let (start, a0, c0) = COUNTS.with(|c| { + c.peak.set(c.cur.get()); + (c.cur.get(), c.allocated.get(), c.calls.get()) + }); let t = now(); let out = f(); let time = now() - t; - let w = Window { allocated: ALLOCATED.load(Relaxed) - a0, calls: CALLS.load(Relaxed) - c0, peak: PEAK.load(Relaxed) - start, time }; + let w = COUNTS.with(|c| Window { + allocated: c.allocated.get().wrapping_sub(a0), + calls: c.calls.get().wrapping_sub(c0), + peak: (c.peak.get() - start).max(0) as usize, + time, + }); (out, w) } @@ -249,6 +289,15 @@ fn offers(n: u32) -> [Window; 5] { #[test] fn staged_sink_resources() { + // ── The counts are the measuring thread's own ── + // A megabyte another thread allocates inside a window is not this thread's; what this + // thread allocates is. + let (_, other) = measure(|| std::thread::spawn(|| std::hint::black_box(vec![1u8; 1 << 20])).join().unwrap().len()); + let (_, own) = measure(|| std::hint::black_box(vec![1u8; 1 << 20]).len()); + println!("counts: another thread's megabyte counts {} bytes here, this thread's {}", other.peak, own.peak); + assert!(other.peak < 1 << 16, "another thread's allocation counted: {}", other.peak); + assert!(own.peak >= 1 << 20, "this thread's allocation not counted: {}", own.peak); + // ── Work per layer, linear in its units ── let sizes = [4096u32, 16384, 65536]; // The fastest of three runs at each size, so a busy machine does not decide. @@ -337,19 +386,19 @@ fn staged_sink_resources() { let layers: Vec> = (0..48).map(|i| noisy(&bh, Prev::One(hex(&bh)), 1, &units, i)).collect(); let after_first = { sink.offer(&layers[0], 0, ReceiptEvidence::None).unwrap(); - CUR.load(Relaxed) + cur() }; for (i, l) in layers.iter().enumerate().skip(1) { let r = sink.offer(l, i as u64, ReceiptEvidence::None).unwrap(); assert_eq!(r[0].refused, vec![(0, StageRefusal::LayerConflict)]); } - let grew = CUR.load(Relaxed).saturating_sub(after_first); + let grew = grown_since(after_first); println!("retention: 47 refused layers of {} bytes each; the sink grew {grew} bytes", layers[1].len()); assert!(grew < layers[1].len() / 2, "the sink kept {grew} bytes of layers no unit holds"); // Held layers that expire are dropped too. let mut sink = StageSink::new(cfg(64, u64::MAX)); sink.add_base(info.clone(), units.clone()); - let before = CUR.load(Relaxed); + let before = cur(); for (i, l) in layers.iter().enumerate().take(16) { let mut l2 = Wai::from_bytes(l).unwrap(); let r = Refines { base: hex(&bh), prev: Prev::One(hex(&[i as u8 + 1; 32])), layer: 2, span: None }; @@ -357,11 +406,11 @@ fn staged_sink_resources() { let r = sink.offer(&l2.to_bytes().unwrap(), 0, ReceiptEvidence::None).unwrap(); assert_eq!(r[0].held, vec![0]); } - assert!(CUR.load(Relaxed) > before + 16 * layers[1].len() / 2, "held layers are kept"); + assert!(cur() > before + (16 * layers[1].len() / 2) as isize, "held layers are kept"); let r = sink.expire(10_000); assert_eq!(r.len(), 16); drop(r); - let grew = CUR.load(Relaxed).saturating_sub(before); + let grew = grown_since(before); assert!(grew < layers[1].len() / 2, "the sink kept {grew} bytes after every held layer expired"); // ── Records of dropped chains (§4.4) ── @@ -370,20 +419,20 @@ fn staged_sink_resources() { // holds for them within a small multiple of it. let units: Vec = base(4096, 1, [0; 32]).1; let mut sink = StageSink::new(cfg(16, u64::MAX)); - let before = CUR.load(Relaxed); + let before = cur(); for i in 0..20u8 { let mut info = base(1, 1, [0x40 + i; 32]).0; info.content_hash = [0x40 + i; 32]; sink.add_base(info, units.clone()); sink.remove_base(&[0x40 + i; 32]); } - let grew = CUR.load(Relaxed).saturating_sub(before); + let grew = grown_since(before); println!("records: 20 dropped bases of 4096 units with nothing recorded keep {} bytes; the sink grew {grew} bytes", sink.kept_bytes()); assert_eq!(sink.kept_bytes(), 0); assert!(grew < 16 << 10, "the sink kept {grew} bytes for bases with nothing recorded"); let record_budget = 1 << 20; let mut sink = StageSink::new(ChainConfig { record_budget, ..cfg(16, u64::MAX) }); - let before = CUR.load(Relaxed); + let before = cur(); for i in 0..20u8 { let h = [0x60 + i; 32]; let mut info = base(1, 1, h).0; @@ -394,7 +443,7 @@ fn staged_sink_resources() { sink.remove_base(&h); assert!(sink.kept_bytes() <= record_budget); } - let grew = CUR.load(Relaxed).saturating_sub(before); + let grew = grown_since(before); println!("records: 20 dropped bases of 4096 refined units keep {} counted bytes within {record_budget}; the sink grew {grew} bytes", sink.kept_bytes()); assert!(grew as u64 <= 4 * record_budget, "the sink holds {grew} bytes for a record budget of {record_budget}"); @@ -453,13 +502,13 @@ fn staged_sink_resources() { let (info, units) = base(16, 256, bh); let out: u64 = units.iter().map(|u| u.samples.byte_len()).sum(); let mut sink = StageSink::new(cfg(16, out / 2)); - let before = CUR.load(Relaxed); + let before = cur(); let r = sink.add_base(info, units); assert_eq!(r.len(), 1); assert_eq!(r[0].envelope, Some(StageRefusal::ChainOverLimit)); drop(r); assert!(sink.chain(&bh).is_none()); - let left = CUR.load(Relaxed).saturating_sub(before); + let left = grown_since(before); println!("budget: a base of {out} output bytes in a {}-byte budget is refused; the sink holds {left} bytes of it", out / 2); assert!(left < 4096, "the sink kept {left} bytes of a base it refused"); } -- 2.54.0 (Apple Git-157) From bbcab8269f67c1d2f082414e01ac3ffa06824fd2 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 15:17:02 -0400 Subject: [PATCH 06/21] wai: a repeated object is one object of a sequence; pack_verify reads the playlists with http_verify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http_verify.py joined a layer only to the first occurrence of its base, so the staged golden's tv playlist, which lists the bumper's base twice, gave a floor without +video.int_refine while wai-rs, which reads each object once, gave it with: the two implementations of http-delivery §4.2 disagreed. - http-delivery §4.2: an object a sequence lists more than once is one object of it, described once with every layer it has, and a layer joins every object of the sequence that is its base. - wai-rs (`codecs::described`) and http_verify.py both: each object once, each layer joined to every object that is its base. - http-conformance: two vectors, a base listed twice with its layer beside the first and beside the second occurrence; the union and floor keep the layer. - pack_verify.py runs http_verify.py's adjacency, union and floor over every golden's class playlists and MPDs, so CI sees a disagreement; wai-pack.yml's push filter lists http_verify.py. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-pack.yml | 2 ++ wai/extensions/http-delivery.md | 7 ++-- wai/http-conformance/cases.json | 2 ++ wai/http-conformance/http_verify.py | 29 ++++++++------- wai/pack-conformance/pack_verify.py | 41 ++++++++++++++++++++++ wai/wai-rs/src/bin/wai_cli/http_vectors.rs | 4 +++ wai/wai-rs/src/http_binding/codecs.rs | 27 +++++++++----- 7 files changed, 89 insertions(+), 23 deletions(-) diff --git a/.github/workflows/wai-pack.yml b/.github/workflows/wai-pack.yml index 31cfe2a71..a7a6557a0 100644 --- a/.github/workflows/wai-pack.yml +++ b/.github/workflows/wai-pack.yml @@ -39,6 +39,7 @@ on: - 'wai/staged-conformance/staged_verify.py' - 'wai/staged-measure-conformance/sim_verify.py' - 'wai/staged-measure-conformance/measure_verify.py' + - 'wai/http-conformance/http_verify.py' - 'wai/registry/capabilities.json' - '.github/workflows/wai-pack.yml' push: @@ -53,6 +54,7 @@ on: - 'wai/staged-conformance/staged_verify.py' - 'wai/staged-measure-conformance/sim_verify.py' - 'wai/staged-measure-conformance/measure_verify.py' + - 'wai/http-conformance/http_verify.py' - 'wai/registry/capabilities.json' - '.github/workflows/wai-pack.yml' diff --git a/wai/extensions/http-delivery.md b/wai/extensions/http-delivery.md index 016177264..6f477fcbd 100644 --- a/wai/extensions/http-delivery.md +++ b/wai/extensions/http-delivery.md @@ -142,8 +142,11 @@ lcalnum = %x61-7A / %x30-39 sequence's objects — a `WAI1`, through its `capability` (staged delivery never takes a base through its fallback), or a component of a `WAI2`, by the content hash of the component's exploded envelope ([SPEC](../SPEC.md) §7.1), through the component's - capability. A sequence with a layer whose base it does not hold cannot be described - (`codecs_layer_unbound`). `alt`s are ordered by first appearance, and the + capability. An object a sequence lists more than once (a repeated piece) is one object + of it, described once with every layer it has, and a layer joins every object of the + sequence that is its base, so where a layer is listed beside one occurrence of its base + it describes them all. A sequence with a layer whose base it does not hold cannot be + described (`codecs_layer_unbound`). `alt`s are ordered by first appearance, and the capabilities after `+` by first appearance in table order, then a sequence's layers in order. The union is exhaustive: a sink that finds, in a head it has verified, a selectable, companion or refinement capability the union does not list refuses that diff --git a/wai/http-conformance/cases.json b/wai/http-conformance/cases.json index 148adc9ce..edf6647d9 100644 --- a/wai/http-conformance/cases.json +++ b/wai/http-conformance/cases.json @@ -285,6 +285,8 @@ {"name":"parse_head_kid_string","op":"parse_head","input":{"file":"bad/OK.wai"},"expect":{"error":"head_malformed"}}, {"name":"parse_head_kind_number","op":"parse_head","input":{"file":"bad/OKN.wai"},"expect":{"error":"head_malformed"}}, {"name":"union_O10_layers","op":"union_floor","input":{"objects":["O10","O10L1","O10L2"]},"expect":{"union":"wai1.audio.flac+audio.int_refine","floor":"wai1.audio.flac+audio.int_refine"}}, +{"name":"union_O10_repeated_layer_after_first","op":"union_floor","input":{"objects":["O10","O10L1","O10"]},"expect":{"union":"wai1.audio.flac+audio.int_refine","floor":"wai1.audio.flac+audio.int_refine"}}, +{"name":"union_O10_repeated_layer_after_second","op":"union_floor","input":{"objects":["O10","O10","O10L1"]},"expect":{"union":"wai1.audio.flac+audio.int_refine","floor":"wai1.audio.flac+audio.int_refine"}}, {"name":"union_O10_layers_unbound","op":"union_floor","input":{"objects":["O10L1","O10L2"]},"expect":{"error":"codecs_layer_unbound"}}, {"name":"fetch_O10_base","op":"fetch","input":{"object":"O10","advertises":["wai.audio.flac"]},"expect":{"role":"base","pick":0,"verified_by":"whole-object","why_whole":"single_component","body_bytes":1910,"requests":2,"companions":[],"refinements":[],"abandoned":[],"dropped":[],"skipped":[],"component_sha256":"67ddfc4700c518d0f0028fd03920901057f419d9fc84349e1c9cf7a75314fefc"}}, {"name":"fetch_O10L1_discovered","op":"fetch","input":{"object":"O10L1","fragment":"#n=12711&h=1009","advertises":[]},"expect":{"role":"layer","pick":0,"verified_by":"whole-object","why_whole":"layer","body_bytes":12892,"requests":3,"companions":[],"refinements":[],"abandoned":[],"dropped":[],"skipped":[],"component_sha256":"96c9e16b31256d323efe54d996bfab6223f4eeb017da5d55ece7fd74fee750e2"}}, diff --git a/wai/http-conformance/http_verify.py b/wai/http-conformance/http_verify.py index 947bd8ed0..947bd9e34 100644 --- a/wai/http-conformance/http_verify.py +++ b/wai/http-conformance/http_verify.py @@ -592,6 +592,15 @@ def base_capability(b, base): def described(objects): + """§4.2: each object that is not a layer as an element, each layer's capability after + `+` in the alt of every object of the sequence that is its base. An object listed more + than once is one object, described once with every layer it has.""" + seen, uniq = set(), [] + for o in objects: + if o["cid"] not in seen: + seen.add(o["cid"]) + uniq.append(o) + objects = uniq els = [None if is_layer(o["head"]) else element_of(o["head"]) for o in objects] for o in objects: if not is_layer(o["head"]): @@ -603,19 +612,15 @@ def described(objects): base = SV.parse_refines(m.get("refines"))["base"] except SV.Malformed: raise Refusal("codecs_malformed") - target = None - for k, b in enumerate(objects): - bc = base_capability(b, base) - if bc is not None: - target = (k, bc) - break - if target is None: + targets = [(k, bc) for k, b in enumerate(objects) for bc in [base_capability(b, base)] if bc is not None] + if not targets: raise Refusal("codecs_layer_unbound") - alt = next((a for a in els[target[0]][1] if a[0] == target[1]), None) - if alt is None: - raise Refusal("codecs_layer_unbound") - if cap not in alt[1:]: - alt.append(cap) + for k, bc in targets: + alt = next((a for a in els[k][1] if a[0] == bc), None) + if alt is None: + raise Refusal("codecs_layer_unbound") + if cap not in alt[1:]: + alt.append(cap) return [e for e in els if e is not None] diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index dcc586843..8a26ad29c 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -82,6 +82,9 @@ import _ed25519 as ED # noqa: E402 import int_payload_verify as IPV # noqa: E402 import staged_verify as SV # noqa: E402 import sim_verify as SIM # noqa: E402 +sys.path.insert(0, os.path.join(HERE, "..", "http-conformance")) +import http_verify as HV # noqa: E402 +HV.load_registry() MAX_INTEGER = (1 << 53) - 1 U32 = (1 << 32) - 1 @@ -3157,11 +3160,49 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): named.add(path) rep.check(path in files and load(os.path.join(pkg, path)) == cats[path], f"{t}: {path} is the one the index, the objects and the floor give (catalog-mismatch)") + http_views(rep, t, c["id"], cats, {f"{e['hash'].hex()}.wai": objs[i] for i, e in enumerate(E)}) if inputs: verify_sources(rep, t, root, P, comp, ix, stage, units_of, objs) return named +def http_views(rep, t, c, cats, objmap): + """§8.3 read by http-delivery's own verifier (http_verify.py): each class's WAI media + playlist adjacent to the classical one, the union and floor of the objects it lists + (an object listed twice is one, a layer joins every occurrence of its base, + http-delivery §4.2) equal to its master's CODECS and X-WAI-FLOOR, and its MPD adjacent + to the classical MPD.""" + floor_text = cats[f"hls/classic/floor-{c}.m3u8"].decode() + for path in sorted(cats): + m = re.fullmatch(r"hls/([^/]+)/wai-" + re.escape(c) + r"\.m3u8", path) + if not m: + continue + cls, text = m.group(1), cats[path].decode() + try: + HV.check_adjacent(text, floor_text) + adjacent = True + except HV.Refusal: + adjacent = False + rep.check(adjacent, f"{t}: {path} is adjacent to the floor's playlist (http-delivery §11.2)") + try: + _, _, every = HV.listed_objects(objmap, HV.parse_media_playlist(text)) + union, floor = HV.union_floor_of(every) + got = (HV.render(union), HV.render(floor)) + except (HV.Refusal, SystemExit) as x: + got = (f"refused {x}", "") + master = cats[f"hls/{cls}/wai.m3u8"].decode() + want = (re.search(r'[,:]CODECS="([^"]+)"', master).group(1), re.search(r'X-WAI-FLOOR="([^"]+)"', master).group(1)) + rep.check(got == want, f"{t}: hls/{cls}/wai.m3u8's CODECS and X-WAI-FLOOR are http-delivery §4.2's over what it lists: {got}, {want}") + mpd = cats.get(f"dash/{cls}.mpd") + if mpd is not None: + try: + HV.check_dash_adjacent(mpd.decode(), cats["dash/classic.mpd"].decode()) + adjacent = True + except HV.Refusal: + adjacent = False + rep.check(adjacent, f"{t}: dash/{cls}.mpd is adjacent to the classical MPD (http-delivery §11.2)") + + def verify_sources(rep, t, root, P, comp, ix, stage, units_of, objs): """What only the source decides: the pieces (§4.3, §4.6) and their rules, moments and class plan (§8.1); each piece's ladder (§5.7) re-derived from the source: an diff --git a/wai/wai-rs/src/bin/wai_cli/http_vectors.rs b/wai/wai-rs/src/bin/wai_cli/http_vectors.rs index 3e6da62aa..912fb7466 100644 --- a/wai/wai-rs/src/bin/wai_cli/http_vectors.rs +++ b/wai/wai-rs/src/bin/wai_cli/http_vectors.rs @@ -1047,6 +1047,10 @@ fn cases(objs: &BTreeMap>, extra: &mut BTreeMap> v.push(refused("parse_head_kind_number", "parse_head", json!({ "file": "bad/OKN.wai" }), "head_malformed")); // Item 2: layers. v.push(case("union_O10_layers", "union_floor", json!({ "objects": ["O10", "O10L1", "O10L2"] }))); + // §4.2: a base listed twice is one object, with every layer it has, whichever + // occurrence a layer is listed beside: the floor keeps the layer. + v.push(case("union_O10_repeated_layer_after_first", "union_floor", json!({ "objects": ["O10", "O10L1", "O10"] }))); + v.push(case("union_O10_repeated_layer_after_second", "union_floor", json!({ "objects": ["O10", "O10", "O10L1"] }))); v.push(refused("union_O10_layers_unbound", "union_floor", json!({ "objects": ["O10L1", "O10L2"] }), "codecs_layer_unbound")); v.push(case("fetch_O10_base", "fetch", json!({ "object": "O10", "advertises": ["wai.audio.flac"] }))); v.push(case("fetch_O10L1_discovered", "fetch", json!({ "object": "O10L1", "fragment": frag("O10L1", Some(1009)), "advertises": [] }))); diff --git a/wai/wai-rs/src/http_binding/codecs.rs b/wai/wai-rs/src/http_binding/codecs.rs index 568a3c5ae..a4d35e39b 100644 --- a/wai/wai-rs/src/http_binding/codecs.rs +++ b/wai/wai-rs/src/http_binding/codecs.rs @@ -305,10 +305,14 @@ fn base_capability(b: &SeqObject, base: &[u8; 32]) -> Option { /// capability after `+` in the alt of the base its `refines.base` names: a `WAI1` /// object of the sequence, through its capability (staged delivery's base is never /// decoded through a fallback), or a `WAI2` component, through the component's -/// capability. Layers join in sequence order, after the object's own capabilities. +/// capability. Layers join in sequence order, after the object's own capabilities. An +/// object the sequence lists more than once is one object of it, described once with +/// every layer it has, and a layer joins every object of the sequence that is its base. fn described(objects: &[SeqObject]) -> Result, CodecsError> { + let mut seen = std::collections::BTreeSet::new(); + let objects: Vec<&SeqObject> = objects.iter().filter(|o| seen.insert(o.cid)).collect(); let mut els: Vec> = Vec::new(); - for o in objects { + for o in &objects { els.push(if o.head.is_layer() { None } else { Some(element_of(&o.head)?) }); } for o in objects.iter().filter(|o| o.head.is_layer()) { @@ -319,14 +323,19 @@ fn described(objects: &[SeqObject]) -> Result, CodecsError> { .ok_or_else(|| CodecsError::Malformed(format!("the layer wai.{cap} carries no refines"))) .and_then(|v| Refines::from_value(v, RefForm::ContentHash).map_err(|e| CodecsError::Malformed(format!("refines: {}", e.0))))?; let base = super::unhex32(&refines.base).ok_or_else(|| CodecsError::Malformed("refines.base".into()))?; - let target: Option<(usize, String)> = objects.iter().enumerate() + let targets: Vec<(usize, String)> = objects.iter().enumerate() .filter(|(_, b)| !b.head.is_layer()) - .find_map(|(k, b)| base_capability(b, &base).map(|c| (k, c))); - let (k, base_cap) = target.ok_or_else(|| CodecsError::LayerUnbound(format!("wai.{cap} refines {}", refines.base)))?; - let el = els[k].as_mut().expect("a base is not a layer"); - let alt = el.alts.iter_mut().find(|a| a.cap == base_cap) - .ok_or_else(|| CodecsError::LayerUnbound(format!("wai.{cap}: its base's capability is not dispatched")))?; - push_unique(&mut alt.plus, cap); + .filter_map(|(k, b)| base_capability(b, &base).map(|c| (k, c))) + .collect(); + if targets.is_empty() { + return Err(CodecsError::LayerUnbound(format!("wai.{cap} refines {}", refines.base))); + } + for (k, base_cap) in targets { + let el = els[k].as_mut().expect("a base is not a layer"); + let alt = el.alts.iter_mut().find(|a| a.cap == base_cap) + .ok_or_else(|| CodecsError::LayerUnbound(format!("wai.{cap}: its base's capability is not dispatched")))?; + push_unique(&mut alt.plus, cap.clone()); + } } Ok(els.into_iter().flatten().collect()) } -- 2.54.0 (Apple Git-157) From 979c9e002c5d845cada3a351fd669b66fbedfd89 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 15:30:00 -0400 Subject: [PATCH 07/21] wai-pack: a still held for its piece is hashed, never repeated; no piece past max_ms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verifier built a still's prefix as the frame repeated for the piece's duration (`vec![rgb; n]`), so a package stating a long duration made it allocate in proportion to that number. - verify.rs: a still's prefix digest is its frame's digest fed n times (`pack::held_digest`); no frame is repeated in memory. - §4.4 bounds every piece by max_ms; the verifier holds an index to it first of every per-piece rule, so nothing is decoded for a piece past it (index-policy-mismatch). pack_verify.py does the same, and hashes a held still once. - staged-mutations: the last piece, and the still as the last piece of a cut package, each lasting 2^32 − 1 frames with their rules, moments and class plan what that span gives, are refused, not aborted. Co-Authored-By: Claude Opus 5.5 --- wai/pack-conformance/pack_verify.py | 21 +++++++- .../staged-mutations/expected.json | 17 ++++++ .../staged-mutations/piece-past-max/LISTING | 41 ++++++++++++++ .../piece-past-max/dash/classic.mpd | 24 +++++++++ .../piece-past-max/dash/mobile.mpd | 37 +++++++++++++ .../piece-past-max/dash/tv.mpd | 51 ++++++++++++++++++ .../piece-past-max/hls/classic/floor-v.m3u8 | 18 +++++++ .../piece-past-max/hls/classic/master.m3u8 | 5 ++ .../piece-past-max/hls/legacy/master.m3u8 | 5 ++ .../piece-past-max/hls/mobile/master.m3u8 | 6 +++ .../piece-past-max/hls/mobile/wai-v.m3u8 | 21 ++++++++ .../piece-past-max/hls/mobile/wai.m3u8 | 7 +++ .../piece-past-max/hls/tv/master.m3u8 | 6 +++ .../piece-past-max/hls/tv/wai-v.m3u8 | 25 +++++++++ .../piece-past-max/hls/tv/wai.m3u8 | 7 +++ .../piece-past-max/index-v.wsix | Bin 0 -> 2792 bytes .../piece-past-max/package.receipt.json | 1 + .../piece-past-max/stages/legacy.json | 1 + .../piece-past-max/stages/mobile.json | 1 + .../piece-past-max/stages/tv.json | 1 + .../staged-mutations/still-past-max/LISTING | 35 ++++++++++++ .../still-past-max/dash/classic.mpd | 20 +++++++ .../still-past-max/dash/mobile.mpd | 29 ++++++++++ .../still-past-max/dash/tv.mpd | 39 ++++++++++++++ .../still-past-max/hls/classic/floor-v.m3u8 | 14 +++++ .../still-past-max/hls/classic/master.m3u8 | 5 ++ .../still-past-max/hls/legacy/master.m3u8 | 5 ++ .../still-past-max/hls/mobile/master.m3u8 | 6 +++ .../still-past-max/hls/mobile/wai-v.m3u8 | 15 ++++++ .../still-past-max/hls/mobile/wai.m3u8 | 7 +++ .../still-past-max/hls/tv/master.m3u8 | 6 +++ .../still-past-max/hls/tv/wai-v.m3u8 | 17 ++++++ .../still-past-max/hls/tv/wai.m3u8 | 7 +++ .../still-past-max/index-v.wsix | Bin 0 -> 1704 bytes .../still-past-max/moq/catalog.json | 1 + .../still-past-max/package.receipt.json | 1 + .../still-past-max/plan-v.txt | 3 ++ .../still-past-max/stages/legacy.json | 1 + .../still-past-max/stages/mobile.json | 1 + .../still-past-max/stages/tv.json | 1 + wai/wai-pack/src/golden.rs | 38 +++++++++++++ wai/wai-pack/src/pack.rs | 12 +++++ wai/wai-pack/src/verify.rs | 26 +++++---- wai/wai-pack/tests/staged.rs | 1 + 44 files changed, 574 insertions(+), 11 deletions(-) create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/LISTING create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/dash/classic.mpd create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/dash/mobile.mpd create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/floor-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/legacy/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/stages/legacy.json create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/piece-past-max/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/dash/classic.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/dash/mobile.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/classic/floor-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/classic/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/legacy/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/tv/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/moq/catalog.json create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/plan-v.txt create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/stages/legacy.json create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/still-past-max/stages/tv.json diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index 8a26ad29c..51216a488 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -2748,6 +2748,17 @@ def seg_params(P, tb): "window": s["cut_window"], "lookahead": s["lookahead_frames"]} +def prefix_digest_of(frames): + """SPEC §7's clip digest, each frame's SHA-256 fed in turn: a still's frame, held, is + hashed once and its digest fed for every frame (§5.7).""" + h, last, d = hashlib.sha256(), None, None + for f in frames: + if f is not last: + last, d = f, hashlib.sha256(f).digest() + h.update(d) + return h.digest() + + def clip_digest(frames): """SPEC §7, *Digests*: the SHA-256 of the frames' SHA-256 digests concatenated.""" return hashlib.sha256(b"".join(hashlib.sha256(f).digest() for f in frames)).digest() @@ -2970,6 +2981,11 @@ def index_against_policy(P, comp, c, ix): # Each piece: its kind, its rules and moments, its entries the rungs of its ladder # (§5.7) with the plan's deadline on each refinement (§8.6), then the class plan. E = ix["entries"] + # §4.4: no piece lasts longer than max_ms; first, so nothing is decoded for one that does. + mx = units_from_ms(P["segmentation"]["max_ms"], *ix["tb"]) + long = next((k for k, pc in enumerate(ix["pieces"]) if pc["duration"] > mx), None) + if long is not None: + return f"piece {long}: past the policy's max_ms" for k, pc in enumerate(ix["pieces"]): if pc["kind"] >= len(KINDS): return f"piece {k}: a kind a video piece does not have" @@ -3045,7 +3061,8 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): if e["role"] == BASE: try: if e["cap"] == "wai.image.png": - # §5.7: a still, its one frame held for the piece. + # §5.7: a still, its one frame held for the piece (n is within max_ms, + # which the index was held to, and the list holds n references). pw, ph, rgb = png_frame(payload) wv, frames = None, [rgb] * n else: @@ -3079,7 +3096,7 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): return named | unreached(files, c['id']) for i, e in enumerate(E): if e["tier"] == 2: - rep.check(clip_digest(stage[i]) == e["prefix"], + rep.check(prefix_digest_of(stage[i]) == e["prefix"], f"{t}: entry {i}: the prefix decodes to its prefix_digest (prefix-digest)") # The floor objects (§7.2) and the CMAF files their temporal units give (§7.6). diff --git a/wai/pack-conformance/staged-mutations/expected.json b/wai/pack-conformance/staged-mutations/expected.json index b99d0c157..0df2d7815 100644 --- a/wai/pack-conformance/staged-mutations/expected.json +++ b/wai/pack-conformance/staged-mutations/expected.json @@ -25,6 +25,23 @@ "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" ] }, + { + "name": "piece-past-max", + "reason": "index-policy-mismatch", + "remove": [] + }, + { + "name": "still-past-max", + "reason": "index-policy-mismatch", + "remove": [ + "floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s", + "floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s", + "objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai", + "objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai", + "objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai", + "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" + ] + }, { "name": "still-held-digest", "reason": "prefix-digest", diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/LISTING b/wai/pack-conformance/staged-mutations/piece-past-max/LISTING new file mode 100644 index 000000000..4574e1f47 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/LISTING @@ -0,0 +1,41 @@ +563e5378ed4d13a3a3cb11aab1873afbe07ab8adecdac26f2468702be55ad9e1 dash/classic.mpd +c0e75fa3880482dc7fb86d53e8847e25ce33d0103ceae62a15dbf4bc0cf6bc3a dash/mobile.mpd +f214f0c0583980e1fff9b6d96dc17743190e85cf875ebeadb6df435926764a61 dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +2a696d51aac0a2865bb265ccb3029c13a3933f80c09a9f7c9083582e358ff409 hls/classic/floor-v.m3u8 +04281fde53ffef78936bb5b8d7228671ff20cca068cd745c03ab2cbd16b16522 hls/classic/master.m3u8 +04281fde53ffef78936bb5b8d7228671ff20cca068cd745c03ab2cbd16b16522 hls/legacy/master.m3u8 +24cb2a192f121d6a9c086f59a2750bf8a578319fe1bb04acac25e3de9a3921b7 hls/mobile/master.m3u8 +9c80c3469829f2f7d2639a30124d233cc252c117e14f348e9b6aa991824c5ec6 hls/mobile/wai-v.m3u8 +6808dbfba087fe644fd7e73321ea66a5e1351daa49b877d6489f8ac7ab3ab98f hls/mobile/wai.m3u8 +24cb2a192f121d6a9c086f59a2750bf8a578319fe1bb04acac25e3de9a3921b7 hls/tv/master.m3u8 +099ea6ee49bc03f2bb5dc8c0a5f679ad7c9e294e95007976a1ad858d3573b336 hls/tv/wai-v.m3u8 +162024e0bb15e0e744fa46f25cc9ba8cf3da7eb66dd5625e0b672f0b74a48b80 hls/tv/wai.m3u8 +22eff3b157e4cdf5a2e350ec5957ac18b43978568d3ae4119d36611ee0dccb7c index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +2295a23169c0307290385a1cbb1541b6de760b3fc0880f84eff1d01a29729d55 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +81e00b0beb6ac3c0970cdd19cffb21f68f582c80195625b6ba72b82cd91447a2 stages/legacy.json +2f8dbfbfd553817137d59b595b335413a1f82947cb9c9678128f0705b7f3dee3 stages/mobile.json +2654c4b1f0e81d8b1295720e70369f9344a10257fca1852a971b3acb19567c6d stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/dash/classic.mpd b/wai/pack-conformance/staged-mutations/piece-past-max/dash/classic.mpd new file mode 100644 index 000000000..21aed37ec --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/dash/classic.mpd @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/dash/mobile.mpd b/wai/pack-conformance/staged-mutations/piece-past-max/dash/mobile.mpd new file mode 100644 index 000000000..0d0aa3ed8 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/dash/mobile.mpd @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/dash/tv.mpd b/wai/pack-conformance/staged-mutations/piece-past-max/dash/tv.mpd new file mode 100644 index 000000000..4da20fabe --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/dash/tv.mpd @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/floor-v.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/floor-v.m3u8 new file mode 100644 index 000000000..e243737b1 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/floor-v.m3u8 @@ -0,0 +1,18 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-MAP:URI="../../floor/v/init.mp4" +#EXTINF:2.000, +../../floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +#EXTINF:1.000, +../../floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +#EXTINF:1.600, +../../floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +#EXTINF:1.000, +../../floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +#EXTINF:171798691.800, +../../floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/master.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/master.m3u8 new file mode 100644 index 000000000..0a72b1fee --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/classic/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=9120,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/legacy/master.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/legacy/master.m3u8 new file mode 100644 index 000000000..0a72b1fee --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/legacy/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=9120,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/master.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/master.m3u8 new file mode 100644 index 000000000..e5acfa27a --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=9120,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..f71957793 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai-v.m3u8 @@ -0,0 +1,21 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:171798691.800, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai.m3u8 new file mode 100644 index 000000000..4b5404564 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/mobile/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="mobile" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/mobile.json" +#EXT-X-STREAM-INF:BANDWIDTH=323176,AVERAGE-BANDWIDTH=1,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/master.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/master.m3u8 new file mode 100644 index 000000000..e5acfa27a --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=9120,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..090805eba --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:171798691.800, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai#n=101295" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai.m3u8 new file mode 100644 index 000000000..68175af41 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=645600,AVERAGE-BANDWIDTH=1,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/index-v.wsix b/wai/pack-conformance/staged-mutations/piece-past-max/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..799dfc3c141317738caac970d632442fc295324d GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5ty2Le8@JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+e|=^iv%W&af2S6wRXs&lk69Zm)<5oB+Vy8nz@^Tu z-=azyRf!V&Wq>Nih0Qgi4O@wTj9|I}h?(KattfvF@O%pRC%;8`Un# zoSb0oZ~_#MY@pBwMFJ53q#gt=L#@?)=28cANiRpXU~#U3!B4NDz>@%X-#}OsWRV)`*k_0 z{UE!Mk9q^qYIpd*v!-dXN@S7{}0|HQ8h4G;@q@js^wW;2BT zdg^RGr>zN#7Z`mU<(joU{!U*^w6wI7(|P~zYGLL94YU|@Ku z^MkIbKGJEI6L=YquiWKWd)K0Na@}!Lh7FL2g4wSy2i5+x1IA54QBg%rJxi5LXEIcX zS9l2t&2tt=)8y~}dOu9>5;UHX!=IA){|w8xP-iq2e|WP{YSn{7a~-4JZet2M+tXv@ z#v}YtWo^ODC2MZ-p92-5KmgMQihocNg7N>{2s6(xE%1Mm7iO4iVxj5yP(d!o>d^16 zGfJOc4suNaYX%Z4pbROfc`)%Mf+4b(UWdlazq;-IgR=~~)}@w&HL0#&Vfi@gn|}7* zb$g)c6=omEescVO1{#1*KoN)ph)w@6XM^kqMG3e70MhhI|1kSOWjr|j0|Nw9{?RM_ z!|dmV*v|y=KgfQ1rGJ?H@c3r~I)n%SQV#;K@((@z&)<~HHve~40IOc*u6OThB18ld z1g}=VJo3#lCwA{lrj=FI5KExhkO5>CIR2q_hn+k;CH}9(tcZ2298=}x^tuksH@ULv z+r5w8s^?@~*R1{h-VB`bVCCPADpW_$*d4uhi(KdDNgYe)ZOPmi^fX#LviV7>;#=)4 zoHPFf%Ko>7BwU#NpgJ5@{wV+hMef}Kvq?|14m)doseLL`Q|^D@kF{HJSXdhG_6^yu zetbOq%ol7Sto+$v11m`&?pQkOpK->M?&dgq9&z6OBiikMUP|u>tFP;t%_+O#sC4`K zZdCjEA@+llKgj+IY}QHz%4wdP)rDg&HmFueOe#sdG?ClncgQa_%Zm)Am%AVp7|i~J zE>!y`J5NLr^y=%5sXh`77Sf;u2Lv$xgTkMj{11zN z8?^eA5nhr(>;bl~7`%|22=pQn56EExWqn3q3k2kn|3Fq56GKTE8$)h>Qf5vn8$(WN zdSY^=2t#>dre0ZQN@~7dW?o5rZhlE-ejd7LQEFOdUMeq=+Qc$L2sbk~F+EkUATJ%_ GECvA5CbH-N literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/package.receipt.json b/wai/pack-conformance/staged-mutations/piece-past-max/package.receipt.json new file mode 100644 index 000000000..285beb81f --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"e5fbd69479a0dac74d364b2c8d144dc7cc137310b232bbae5e5ffdfa762de852","media_class":"video","stage_root":"c10304943d547971b05fdb57231dcd6e097d7df38cc68ea92c376ddc957e022d"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"6914d6429b5e8da3e847fb266a60ec6213c7b9f3689b9cd3c5682db7bba8c440","sig":"593b61a457e37d4d7a3fd1b39f52734fcfe225e57d0af69a4b538ef3577979aa5f83744bfcd765e1223229b53b89e44055da7c6428b5c3e5097ca5ad6723610f","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484464,"total_bytes":566398,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/stages/legacy.json b/wai/pack-conformance/staged-mutations/piece-past-max/stages/legacy.json new file mode 100644 index 000000000..5fe4822ba --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/stages/legacy.json @@ -0,0 +1 @@ +{"class":"legacy","components":{"v":{"pieces":[{"dur":50,"floor_only":true,"p":0,"stages":[],"start":0},{"dur":25,"floor_only":true,"p":1,"stages":[],"start":50},{"dur":40,"floor_only":true,"p":2,"stages":[],"start":75},{"dur":25,"floor_only":true,"p":3,"stages":[],"start":115},{"dur":4294967295,"floor_only":true,"p":4,"stages":[],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/stages/mobile.json b/wai/pack-conformance/staged-mutations/piece-past-max/stages/mobile.json new file mode 100644 index 000000000..878f9fbee --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":115},{"dur":4294967295,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/piece-past-max/stages/tv.json b/wai/pack-conformance/staged-mutations/piece-past-max/stages/tv.json new file mode 100644 index 000000000..cce32db32 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/piece-past-max/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":4294967295,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101295,"deadline_ms":800,"h":"a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-past-max/LISTING b/wai/pack-conformance/staged-mutations/still-past-max/LISTING new file mode 100644 index 000000000..5126aa3c1 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/LISTING @@ -0,0 +1,35 @@ +1dd9f02cff2cc3b45db466553ff9fc8e2c07303fe30ea2e436662dcaabe9ef75 dash/classic.mpd +c6b1e2f7d9b8506d1ab829a633ea6fa5e1145fc6ecd73746f23f5c56d74aeae7 dash/mobile.mpd +612c156b15f41cae40fe3367bb44e62d86e9fd5ebad65f44b6fad5c4a2603e1d dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +03e68ee03febde6a20fc604ad4d0847df54e626bafbd9f0da433d15aab73a7ad hls/classic/floor-v.m3u8 +76be7b8cc0dd840f0db67800c9606d212322f3a54cb010b2194354956bba612d hls/classic/master.m3u8 +76be7b8cc0dd840f0db67800c9606d212322f3a54cb010b2194354956bba612d hls/legacy/master.m3u8 +b2436c0656f9feb9bf45ea68ab98e5829c215e63d0b34514c4588a01e8401750 hls/mobile/master.m3u8 +13fefa90f537a9fe15766224d40216b249a17df19b156c4c0d0921488cd8cc01 hls/mobile/wai-v.m3u8 +31284c7b277203d780c46aaa97e00e475cf32223e9871fb5ffd2471cdbac9a64 hls/mobile/wai.m3u8 +b2436c0656f9feb9bf45ea68ab98e5829c215e63d0b34514c4588a01e8401750 hls/tv/master.m3u8 +b43798b36c7fe1f197a79290e7d300471d8591b26ab9b3a4e155d3342c1a6c19 hls/tv/wai-v.m3u8 +fd7f47142bf62014377c02de762f2b76c26e681a908f1713e2ba53d90de51377 hls/tv/wai.m3u8 +72b8864c759939e988c6112c758aa8ffe72e6149db5b0e460fecd62aa6b53d46 index-v.wsix +22652936803efca38577556f8a2cc61b4a8dde41c9737d8379855a84d6fb7aa8 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +d8a93f21183c28797a913f0d418ff9ce2b57bb10118f6bcd6621d626677f6437 package.receipt.json +6261e5b48f122bd416b9f71b2416fba9f1e3c17b67682ba6a3f26e0a27ef9f5e plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +1fa994294a440e0b5b16a2160fe6dfcf917400862e8d84bae99c8e6c0be19fcb stages/legacy.json +43ba56b1cafbedd9b02b757f1ee91dd58b09003fd66723f0e13f67befd2dedc5 stages/mobile.json +bad2931b8833d813a4bec6fb8d5d6ef1042891b4b0fec33e7229407e1cdd423d stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-past-max/dash/classic.mpd b/wai/pack-conformance/staged-mutations/still-past-max/dash/classic.mpd new file mode 100644 index 000000000..e9d9c90ec --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/dash/classic.mpd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-past-max/dash/mobile.mpd b/wai/pack-conformance/staged-mutations/still-past-max/dash/mobile.mpd new file mode 100644 index 000000000..8ee10f0a9 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/dash/mobile.mpd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-past-max/dash/tv.mpd b/wai/pack-conformance/staged-mutations/still-past-max/dash/tv.mpd new file mode 100644 index 000000000..120e1e1a1 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/dash/tv.mpd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/floor-v.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/floor-v.m3u8 new file mode 100644 index 000000000..1292b1000 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/floor-v.m3u8 @@ -0,0 +1,14 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-MAP:URI="../../floor/v/init.mp4" +#EXTINF:2.000, +../../floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +#EXTINF:1.000, +../../floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +#EXTINF:171798691.800, +../../floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/master.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/master.m3u8 new file mode 100644 index 000000000..57ba51d6c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/classic/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/legacy/master.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/legacy/master.m3u8 new file mode 100644 index 000000000..57ba51d6c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/legacy/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/master.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/master.m3u8 new file mode 100644 index 000000000..f190a7fdd --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..0de63220d --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai-v.m3u8 @@ -0,0 +1,15 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:171798691.800, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai.m3u8 new file mode 100644 index 000000000..d0eb98cb7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/mobile/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="mobile" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/mobile.json" +#EXT-X-STREAM-INF:BANDWIDTH=323176,AVERAGE-BANDWIDTH=177139,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/master.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/master.m3u8 new file mode 100644 index 000000000..f190a7fdd --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=1,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..12593a511 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai-v.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:171798692 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:171798691.800, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai.m3u8 new file mode 100644 index 000000000..e60a26af9 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=645600,AVERAGE-BANDWIDTH=465358,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/index-v.wsix b/wai/pack-conformance/staged-mutations/still-past-max/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..dade532db756ee5929797e4ab2461e5b7e5bbc0e GIT binary patch literal 1704 zcmWF!_KaX;U|^60Vi3&?#9Tnk0cA_SO`9ox^Ln2Yf9leeGj0dG?6ckb!|aUK1O88I zreCWI2Pz{8aDLR$ZxNemn)jvhOIncbzUv*+)*SO$BVzfIb+1?TR2E!@89^mjfG%W0 zQUntRxgVsNg@Fm=4LIE){MRm;o{eWDm>@{Ep5K{5IL})vH~9<^){o-1;r5q+vyS$xrW>?~ZO~JD6d*boRc; z1#b>>z+8&Z#|rcV$o@M(kIc4v7OHhP@rHrle22Z>7jM|Tn*CVBW73Y_2XacbB!8as zz1|wE(HUw?2|KEJH$%66QdV$lnw=H9;E0){&cv71wLD_$H`K-FWh|ceEk`;KRXvCY zw4VuPf84wBBeLDOJKi2_C@B~0UM=qZW5+E!hly+)23ZOXeOtT# z)LJcwbKr`!r?%A^83l``1N#J-qwukRa=NM2pZ(DUnGR|fL)AKtU3rhs8{G}r)G{3}4sgE=5IFTUu+ z4zbwzk)OG8_AE)YusM9CVw=04*2JfiD)WuFUzelW53(CM{)-Mj`DBx$bJBKp#BS+B zx_YVsIx=bQo#noFl~(fhPyEWz0I?7j|8vSKfK^7CpZ4*6+`k0_`U5 ztC~@-@2cwFxP654W&T`L`+-RgCH~DX#If2228Ne9Kj@n3Bb|0RftT_4%3Y4NcP(ls z*Bv)y*Z_$rnEeWKQ0-4UVB91W6;;&KvsB4+CPRgIg_n@fJZFJ4P5%C`_rvrqLE{-Y z{3(h5&#;UObw*?Hhc^qQRy{a0*D>ntHm0DnJv~NlJi-rE))w4cvgRiLIZ%NJ1Tbx& z_y;8+82`_WF!K!40{1Xd8W}JdFfDl07V&} ARR910 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/moq/catalog.json b/wai/pack-conformance/staged-mutations/still-past-max/moq/catalog.json new file mode 100644 index 000000000..340931feb --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/moq/catalog.json @@ -0,0 +1 @@ +{"tracks":[{"altGroup":1,"isLive":false,"name":"v/l0","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_motion","wai.image.png"],"wai_determinism":"decode-equivalence","wai_receipt_alias":6826975590310646,"wai_receipts":true,"wai_receipts_track":"v/l0/receipts","wai_staged":{"layer":0,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1300,"wai_determinism":"decode-equivalence","wai_receipt_alias":5745064158311842,"wai_receipts":true,"wai_receipts_track":"v/l1/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0","v/l1"],"isLive":false,"name":"v/l2","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1100,"wai_determinism":"decode-equivalence","wai_receipt_alias":6697012243957121,"wai_receipts":true,"wai_receipts_track":"v/l2/receipts","wai_staged":{"layer":2,"lead_ms":null,"set":"v"}},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","namespace":"video-staged-01","packaging":"cmaf","renderGroup":1,"width":64},{"isLive":false,"name":"v/l0/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l2/receipts","namespace":"video-staged-01","packaging":"wai-receipts"}],"version":1} diff --git a/wai/pack-conformance/staged-mutations/still-past-max/package.receipt.json b/wai/pack-conformance/staged-mutations/still-past-max/package.receipt.json new file mode 100644 index 000000000..3665c6c6d --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"def786bca935792aa5e8e076c5aa64628533e5ba1ce52cbbe070295482d170b1","media_class":"video","stage_root":"4bcbbc9b81de159ed100e3dc118187dfc9ce432bcb5942d2552b183eb66bff1f"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"6393d087ac18dfbdbb923ae0e2cf3b826cc87762052ef7813bef66e82c43df56","sig":"e82501852a56cc9118e48f3680f4d7433837222c5a348e5cf7c70144b72bc1b456afee0e17997290558acec5367de61f2caf6a26d789b472b1b3268fcb228305","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":267309,"total_bytes":267309,"total_entries":10,"unique_objects":10,"version":2,"work_units":10} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-past-max/plan-v.txt b/wai/pack-conformance/staged-mutations/still-past-max/plan-v.txt new file mode 100644 index 000000000..941262bd3 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/plan-v.txt @@ -0,0 +1,3 @@ +0 +50 +75 diff --git a/wai/pack-conformance/staged-mutations/still-past-max/stages/legacy.json b/wai/pack-conformance/staged-mutations/still-past-max/stages/legacy.json new file mode 100644 index 000000000..26eccd4f1 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/stages/legacy.json @@ -0,0 +1 @@ +{"class":"legacy","components":{"v":{"pieces":[{"dur":50,"floor_only":true,"p":0,"stages":[],"start":0},{"dur":25,"floor_only":true,"p":1,"stages":[],"start":50},{"dur":4294967295,"floor_only":true,"p":2,"stages":[],"start":75}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-past-max/stages/mobile.json b/wai/pack-conformance/staged-mutations/still-past-max/stages/mobile.json new file mode 100644 index 000000000..449191cc0 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":4294967295,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-past-max/stages/tv.json b/wai/pack-conformance/staged-mutations/still-past-max/stages/tv.json new file mode 100644 index 000000000..4d9ae4ab7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-past-max/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":4294967295,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/wai-pack/src/golden.rs b/wai/wai-pack/src/golden.rs index 72986c601..604c1f64e 100644 --- a/wai/wai-pack/src/golden.rs +++ b/wai/wai-pack/src/golden.rs @@ -1025,6 +1025,44 @@ pub fn staged_mutations(g: &Files, key: &SigningKey) -> Vec { let env = objects::refinement_envelope("video", x.capability(&x.entries[k]), payload, &base, &x.entries[k - 1].content_hash, x.entries[k].layer); replace_object(&mut f, k, env); add("layer-replaced", "prefix-digest", sealed(f)); + // A piece past the policy's max_ms (§4.4), its rules, moments and class plan what its + // new span gives: the last piece, and the still as the last piece of a package cut + // after it. Refused before anything is decoded for it (index-policy-mismatch), so a + // still is never held for 2^32 − 1 frames. + let policy = crate::policy::Policy::parse_bytes(&g["policy.toml"]).expect("the golden's policy"); + let retime = |x: &mut StageIndex, p: usize, duration: u32| { + let tb = policy.components[0].timebase; + x.pieces[p].duration = duration; + let kind = crate::classify::PieceKind::from_code(x.pieces[p].kind).expect("a kind"); + let pp = crate::plan::plan_piece(&policy, tb, x.pieces[p].start, duration as u64, kind); + (x.pieces[p].rules, x.pieces[p].moments) = (pp.rules, pp.moments); + let bytes: Vec = x.piece_entries(p).iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)).map(|e| e.byte_len).collect(); + for (ci, c) in policy.classes.iter().enumerate() { + x.plan[ci][p] = crate::plan::class_plan(c, &bytes, pp.max_layer, duration as u64, tb); + } + }; + let mut f = g.clone(); + let mut x = golden_index(&f); + retime(&mut x, last, u32::MAX); + set_golden_index(&mut f, &x); + add("piece-past-max", "index-policy-mismatch", sealed(f)); + let mut f = g.clone(); + let mut x = golden_index(&f); + let still = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").map(|k| x.entries[k].piece as usize).expect("the still"); + let keep = (x.pieces[still].first_entry + x.pieces[still].n_entries as u32) as usize; + x.pieces.truncate(still + 1); + x.entries.truncate(keep); + for row in x.plan.iter_mut() { + row.truncate(still + 1); + } + let starts: Vec = x.pieces.iter().map(|p| p.start).collect(); + x.header.plan_digest = wsix::plan_digest(&starts); + retime(&mut x, still, u32::MAX); + let named: std::collections::BTreeSet = x.entries.iter().map(|e| objects::object_path(&e.content_hash)).collect(); + f.retain(|k, _| !k.starts_with("objects/") || named.contains(k)); + f.insert("plan-v.txt".into(), crate::pack::plan_file(&starts).into_bytes()); + set_golden_index(&mut f, &x); + add("still-past-max", "index-policy-mismatch", sealed(f)); // The still's prefix digest of its one frame, not of the frame held for its piece. let mut f = g.clone(); let mut x = golden_index(&f); diff --git a/wai/wai-pack/src/pack.rs b/wai/wai-pack/src/pack.rs index 1285c797e..b0265e78e 100644 --- a/wai/wai-pack/src/pack.rs +++ b/wai/wai-pack/src/pack.rs @@ -163,6 +163,18 @@ pub fn clip_digest>(frames: &[F]) -> [u8; 32] { h.finalize().into() } +/// The clip digest of one RGB8 frame held for `n` frames (§5.7): the SHA-256 of `n` +/// copies of the frame's SHA-256, fed one at a time, so no frame is repeated in memory. +pub fn held_digest(frame: &[u8], n: u64) -> [u8; 32] { + use sha2::Digest as _; + let one = wai::output_tree::out_digest(frame); + let mut h = sha2::Sha256::new(); + for _ in 0..n { + h.update(one); + } + h.finalize().into() +} + /// An object's manifest and payload lengths. Where WAI checks its payload's format, they /// are its counted bytes (staged-measurement §3, `measure_framing::count_object`), which /// also checks the payload parses as its capability's pinned format; a floor's payload, diff --git a/wai/wai-pack/src/verify.rs b/wai/wai-pack/src/verify.rs index 65b743bea..71b873e8c 100644 --- a/wai/wai-pack/src/verify.rs +++ b/wai/wai-pack/src/verify.rs @@ -276,6 +276,13 @@ pub fn index_against_policy(policy: &Policy, comp: &Component, ix: &StageIndex) // Each piece: a kind a video piece has; its rules and moments (§8.1); its entries the // rungs of its ladder (§5.7), a base, each refinement in layer order, each floor, with // the plan's deadline on every refinement (§8.6); its class plan (§8.1). + // §4.4: no piece lasts longer than the policy's max_ms. Checked first, so a piece + // whose duration is past it is refused before anything is decoded for it (a still is + // held for its duration, §5.7). + let max = tb.units_from_ms(policy.segmentation.max_ms); + if let Some(p) = ix.pieces.iter().position(|pc| pc.duration as u64 > max) { + return bad(format!("piece {p}: {} units, past the policy's max_ms ({max} units, §4.4)", ix.pieces[p].duration)); + } for (p, piece) in ix.pieces.iter().enumerate() { let Some(kind) = PieceKind::from_code(piece.kind) else { return bad(format!("piece {p}: a kind a video piece does not have")) }; let pp = plan_piece(policy, tb, piece.start, piece.duration as u64, kind); @@ -379,17 +386,18 @@ fn verify_component( catalog::render_all(&view, policy, policy_digest).map_err(|e| vec![reason("catalog-mismatch", format!("no catalog can be written: {}", e.0))]) } -/// A base object decoded to the frames a sink presents for a piece of `n` frames, and -/// their dimensions: an integer payload's own frames, or a still's one frame held for the -/// piece (§5.7). -fn decode_frames(cap: &str, env: &[u8], n: u32) -> Result<(Vec>, u32, u32), String> { +/// A base object decoded to the clip digest of the frames a sink presents for a piece of +/// `n` frames, and their dimensions: an integer payload's own frames, or a still's one +/// frame held for the piece (§5.7), whose digest is its frame's fed `n` times, so no +/// frame is repeated in memory. +fn decode_digest(cap: &str, env: &[u8], n: u32) -> Result<([u8; 32], u32, u32), String> { let w = wai::container::Wai::from_bytes(env).map_err(|e| e.to_string())?; if cap == "wai.image.png" { let (width, height, rgb) = crate::png::decode(&w.payload)?; - return Ok((vec![rgb; n as usize], width, height)); + return Ok((crate::pack::held_digest(&rgb, n as u64), width, height)); } match wai::int_payload::decode_int(cap, &w.payload, &wai::int_payload::IntParams::none()).map_err(|e| e.to_string())? { - wai::int_payload::IntDecoded::Video { frames, width, height } => Ok((frames, width, height)), + wai::int_payload::IntDecoded::Video { frames, width, height } => Ok((crate::pack::clip_digest(&frames), width, height)), _ => Err(format!("{cap} does not decode to frames here")), } } @@ -408,10 +416,10 @@ fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Res let base = &es[bi]; let bytes = &objects[&base.content_hash]; let at = |k: usize| format!("entry {}", first + k); - match decode_frames(ix.capability(base), bytes, ix.pieces[p].duration) { - Ok((frames, w, h)) => { + match decode_digest(ix.capability(base), bytes, ix.pieces[p].duration) { + Ok((digest, w, h)) => { dims.get_or_insert((w, h)); - if base.prefix_tier == wsix::TIER_DECODE && crate::pack::clip_digest(&frames) != base.prefix_digest { + if base.prefix_tier == wsix::TIER_DECODE && digest != base.prefix_digest { errs.push(reason("prefix-digest", format!("{}: the decode is not its prefix digest", at(bi)))); } } diff --git a/wai/wai-pack/tests/staged.rs b/wai/wai-pack/tests/staged.rs index 8e2d84937..d867e4ede 100644 --- a/wai/wai-pack/tests/staged.rs +++ b/wai/wai-pack/tests/staged.rs @@ -94,6 +94,7 @@ fn every_prefix_decodes_to_its_digest() { let (_, _, rgb) = wai_pack::png::decode(&w.payload).unwrap(); let held = vec![rgb; ix.pieces[p].duration as usize]; assert_eq!(wai_pack::pack::clip_digest(&held), es[0].prefix_digest, "piece {p}: the still held for its piece"); + assert_eq!(wai_pack::pack::held_digest(&held[0], held.len() as u64), es[0].prefix_digest, "and its digest fed n times"); assert_eq!(es[0].sse, 0, "a still shot is its first frame"); continue; } -- 2.54.0 (Apple Git-157) From 7598dde8bcf1f26ed3fcf85ec82b342da91f36db Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 15:41:29 -0400 Subject: [PATCH 08/21] wai-pack: one PNG reader's rules in both verifiers, with a corpus of stills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §5.7 says a still is any PNG of 8-bit RGB without interlace, and the two readers disagreed on what that admits. Rust refused a suggested palette, which an RGB picture may carry; Python accepted an interlace, bytes after IEND and a short row. Both now read by PNG's chunk rules: IHDR first; an optional PLTE before the image data; consecutive IDATs; ancillary chunks read past; unknown critical chunks refused; IEND last and empty; a whole zlib stream with a window of at most 32 KiB, its Adler-32 checked and nothing after it; exact rows. Rust now inflates with miniz_oxide's core decompressor, so it sees how much input the stream used. It refuses bytes after the stream, which it used to read past whenever their last four bytes were the checksum. It also refuses the window PNG forbids, which a non-wrapping buffer would otherwise let through. pack-conformance/stills/ holds the files both readers give one verdict on. wai-pack's conformance check and pack_verify.py check each file against its verdict and, when it is read, the SHA-256 of its samples. Co-Authored-By: Claude Opus 5.5 --- wai/pack-conformance/README.md | 21 +++- wai/pack-conformance/pack_verify.py | 95 ++++++++++++++- wai/pack-conformance/stills/ancillary.png | Bin 0 -> 169 bytes .../stills/bytes-after-end.png | Bin 0 -> 116 bytes .../stills/bytes-after-stream.png | Bin 0 -> 119 bytes .../stills/chunk-between-data.png | Bin 0 -> 154 bytes wai/pack-conformance/stills/crc.png | Bin 0 -> 115 bytes wai/pack-conformance/stills/data-split.png | Bin 0 -> 127 bytes wai/pack-conformance/stills/expected.json | 83 ++++++++++++++ wai/pack-conformance/stills/filter-5.png | Bin 0 -> 115 bytes wai/pack-conformance/stills/interlace.png | Bin 0 -> 115 bytes wai/pack-conformance/stills/module-still.png | Bin 0 -> 114 bytes wai/pack-conformance/stills/no-end.png | Bin 0 -> 103 bytes .../stills/palette-after-data.png | Bin 0 -> 133 bytes wai/pack-conformance/stills/palette.png | Bin 0 -> 133 bytes wai/pack-conformance/stills/plain.png | Bin 0 -> 115 bytes wai/pack-conformance/stills/rgba.png | Bin 0 -> 115 bytes wai/pack-conformance/stills/short-row.png | Bin 0 -> 114 bytes .../stills/stream-checksum.png | Bin 0 -> 115 bytes .../stills/unknown-critical.png | Bin 0 -> 128 bytes .../stills/window-past-32k.png | Bin 0 -> 115 bytes wai/wai-pack/src/golden.rs | 108 ++++++++++++++++++ wai/wai-pack/src/png.rs | 63 ++++++++-- 23 files changed, 354 insertions(+), 16 deletions(-) create mode 100644 wai/pack-conformance/stills/ancillary.png create mode 100644 wai/pack-conformance/stills/bytes-after-end.png create mode 100644 wai/pack-conformance/stills/bytes-after-stream.png create mode 100644 wai/pack-conformance/stills/chunk-between-data.png create mode 100644 wai/pack-conformance/stills/crc.png create mode 100644 wai/pack-conformance/stills/data-split.png create mode 100644 wai/pack-conformance/stills/expected.json create mode 100644 wai/pack-conformance/stills/filter-5.png create mode 100644 wai/pack-conformance/stills/interlace.png create mode 100644 wai/pack-conformance/stills/module-still.png create mode 100644 wai/pack-conformance/stills/no-end.png create mode 100644 wai/pack-conformance/stills/palette-after-data.png create mode 100644 wai/pack-conformance/stills/palette.png create mode 100644 wai/pack-conformance/stills/plain.png create mode 100644 wai/pack-conformance/stills/rgba.png create mode 100644 wai/pack-conformance/stills/short-row.png create mode 100644 wai/pack-conformance/stills/stream-checksum.png create mode 100644 wai/pack-conformance/stills/unknown-critical.png create mode 100644 wai/pack-conformance/stills/window-past-32k.png diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index 666aeb31c..45d943957 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -67,12 +67,22 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). `"signature": "refused"`. - `catalogs/` — stage indexes no golden package has (refinements on more than one deadline), with the MoQ catalog each gives (§8.4). +- `stills/` — PNG files a still's reader reads or refuses (§5.7: any PNG of 8-bit RGB + without interlace), each a generated 5 × 3 picture written chunk by chunk, and + `expected.json`: each file's verdict and, read, the SHA-256 of its RGB8 samples. Read: + the module's own still, and files with a suggested palette, ancillary chunks, and the + image data split across two chunks. Refused: an interlace, bytes after `IEND`, a short + row, a palette after the image data, an unknown critical chunk, no `IEND`, a chunk + between the image data, a chunk's CRC, a sixth filter, another colour type, a zlib + window past 32 KiB, bytes after the zlib stream, and its Adler-32. - `staged-mutations/` — the staged golden altered and re-sealed with its key, in the layout of `package-mutations/`: a refinement on another deadline than its plan's, a piece missing its top layer, a still where the policy's rule now gives the piece the default ladder (each `index-policy-mismatch`), a top layer coded again at another step and a still's digest of its one frame rather than its frame held for the piece (each - `prefix-digest`). + `prefix-digest`), and a piece and a still each longer than the policy's `max_ms` + (`index-policy-mismatch`, refused before a frame is decoded, so a still's declared + duration never sets what a verifier allocates). - `delivery/` — `expected.json`: the staged golden packed live and delivered to a `tv` subscriber over a simulated link (§8.9), under §8.5's priorities, without them, and capped at layer 1: each piece's presented layer, base arrival and dropped layers, and @@ -117,8 +127,8 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. digests of each equal pair to be equal, decodes every encode payload through `decode_int` to the encoder's reconstruction and recomputes its SSE. It packs each golden again and compares every file, verifies the committed package - (`wai-pack verify`), and gives every mutation and every package mutation its - reason. `cargo test` + (`wai-pack verify`), gives every mutation and every package mutation its + reason, and reads every still to its verdict. `cargo test` runs the same check, with the scene cuts of each fixture stated as tests (`tests/scenecut.rs`). - `pack_verify.py`, Python standard library and, to sign what it re-derives, `cryptography` (TOML through `tomllib`, so 3.11 or @@ -141,7 +151,10 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. its reason. Every prefix is decoded: a still by its own PNG reader, and each refinement layer by the `WIR1` decoder of `../staged-conformance/staged_verify.py` (written from staged-delivery); and the MoQ catalog is checked by that file's moq-streaming-format - §4.1 check. From the regenerated fixture it re-derives what only the source decides: + §4.1 check. Every HTTP playlist of a golden is read by + `../http-conformance/http_verify.py`'s union, floor and adjacency checks + (http-delivery §4.2), and every file of `stills/` by its own PNG reader, to the + corpus's verdict. From the regenerated fixture it re-derives what only the source decides: the pieces, their rules, moments, ladders and class plan, each base's SSE and step and the encoder's choices, each still's bytes, and each refinement layer's bytes with its own rANS coder and table fit; and it checks the supplied floor against the policy's diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index 51216a488..9085a0739 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -1784,9 +1784,84 @@ def png_still(rgb, w, h): def png_frame(b): - """A still's frame, by staged_verify.py's PNG reader (zlib, the five filters): its - width, height and RGB8 bytes.""" - (h, w, _), out = SV.decode_png(b) + """§5.7: a still read as any PNG of 8-bit RGB without interlace, by PNG's chunk rules: + every chunk's type four letters and its CRC checked; IHDR first (8-bit RGB, methods + 0, interlace 0); an optional PLTE before the image data, read past; the IDATs + consecutive; ancillary chunks read past; any other critical chunk refused; IEND last, + empty, nothing after it; the zlib stream whole with its Adler-32, its rows exactly + 1 + 3·width bytes, its window 32 KiB at most and nothing after it; the five filters. Its width, height and RGB8 bytes; ValueError + where it is not such a PNG.""" + if b[:8] != b"\x89PNG\r\n\x1a\n": + raise ValueError("not a PNG signature") + at, ihdr, z, ended, plte, idat = 8, None, b"", False, False, 0 + while at < len(b): + if ended: + raise ValueError("bytes after IEND") + if at + 12 > len(b): + raise ValueError("a chunk past the end") + n = struct.unpack_from(">I", b, at)[0] + kind, data = b[at + 4:at + 8], b[at + 8:at + 8 + n] + if at + 12 + n > len(b): + raise ValueError("a chunk past the end") + if not all(65 <= c <= 90 or 97 <= c <= 122 for c in kind): + raise ValueError("a chunk type that is not four letters") + if zlib.crc32(kind + data) != struct.unpack_from(">I", b, at + 8 + n)[0]: + raise ValueError("a chunk CRC") + if idat == 1 and kind != b"IDAT": + idat = 2 + if kind == b"IHDR": + if ihdr is not None or at != 8 or n != 13: + raise ValueError("IHDR is not one 13-byte chunk first") + w, h = struct.unpack(">II", data[:8]) + if data[8:] != bytes([8, 2, 0, 0, 0]) or w == 0 or h == 0 or 3 * w * h > 1 << 26: + raise ValueError("not 8-bit RGB without interlace, or a size past 2^26 samples") + ihdr = (w, h) + elif ihdr is None: + raise ValueError("a chunk before IHDR") + elif kind == b"PLTE": + if plte or idat or n == 0 or n % 3 or n > 768: + raise ValueError("PLTE: more than one, after the image data, or not 1 to 256 entries") + plte = True + elif kind == b"IDAT": + if idat == 2: + raise ValueError("IDAT chunks that are not consecutive") + idat = 1 + z += data + elif kind == b"IEND": + if n: + raise ValueError("IEND with data") + ended = True + elif not kind[0] & 0x20: + raise ValueError("an unknown critical chunk") + at += 12 + n + if ihdr is None or not ended: + raise ValueError("no IHDR or no IEND") + w, h = ihdr + if not z or z[0] >> 4 > 7: + raise ValueError("the zlib stream: no header, or a window past 32 KiB") + d = zlib.decompressobj() + try: + raw = d.decompress(z, (3 * w + 1) * h + 1) + except zlib.error as x: + raise ValueError(f"the zlib stream: {x}") + if not d.eof or d.unused_data or d.unconsumed_tail or len(raw) != (3 * w + 1) * h: + raise ValueError("the rows are not the picture's size, or the stream is not whole") + stride, out = 3 * w, bytearray(3 * w * h) + for y in range(h): + f, row = raw[y * (stride + 1)], raw[y * (stride + 1) + 1:(y + 1) * (stride + 1)] + if f > 4: + raise ValueError(f"row {y}: filter {f}") + for i in range(stride): + a = out[y * stride + i - 3] if i >= 3 else 0 + up = out[(y - 1) * stride + i] if y else 0 + c = out[(y - 1) * stride + i - 3] if y and i >= 3 else 0 + if f == 4: + pp = a + up - c + pa, pb, pc = abs(pp - a), abs(pp - up), abs(pp - c) + pr = a if pa <= pb and pa <= pc else (up if pb <= pc else c) + else: + pr = (0, a, up, (a + up) // 2)[f] + out[y * stride + i] = (row[i] + pr) & 255 return w, h, bytes(out) @@ -3522,6 +3597,20 @@ def verify(d): rep.check(tree(os.path.join(d, "receipts")) == sorted([c["file"] for c in rc["cases"]] + ["expected.json"]), "receipts/: every receipt is a case") + # stills/: PNG files a still's reader reads or refuses (§5.7), each read by this + # verifier's own reader. + st = json.loads(load(os.path.join(d, "stills", "expected.json"))) + for c in st["cases"]: + try: + w, h, px = png_frame(load(os.path.join(d, "stills", c["file"]))) + got = ("ok", w, h, hashlib.sha256(px).hexdigest()) + except ValueError as x: + got = ("refused", str(x)) + want = ("ok", st["width"], st["height"], c.get("rgb_sha256")) if c["verdict"] == "ok" else ("refused",) + rep.check(got[:len(want)] == want, f"stills/{c['file']}: {got[0]}, the corpus says {c['verdict']}") + rep.check(tree(os.path.join(d, "stills")) == sorted([c["file"] for c in st["cases"]] + ["expected.json"]), + "stills/: every file is a case") + # catalogs/: MoQ catalogs of indexes with refinements on more than one deadline (§8.4). cc = json.loads(load(os.path.join(d, "catalogs", "expected.json"))) for c in cc["cases"]: diff --git a/wai/pack-conformance/stills/ancillary.png b/wai/pack-conformance/stills/ancillary.png new file mode 100644 index 0000000000000000000000000000000000000000..7aaba1cc9aba7fc36b9cc767f41c662f00d80548 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQv4;Z5hc#~xw)x%B@BrQ#U+_J zIa~Yud4N(@o-U3d6^zLdXH$g~FU(y2J@eDE^!eg?sTyx$gI1b7J@$$BcEvyKE7Arj pQ)Y+VzkeZnOVE?E*DRwSGbo0O`y@X8c?Q1)44$rjF6*2UngCG`I|Tp$ literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/bytes-after-end.png b/wai/pack-conformance/stills/bytes-after-end.png new file mode 100644 index 0000000000000000000000000000000000000000..f6884ee3e98bc6405cb7021fc4d61051793c4105 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQdXWWjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I QXbOX;tDnm{r-UX30PqkgsQ>@~ literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/bytes-after-stream.png b/wai/pack-conformance/stills/bytes-after-stream.png new file mode 100644 index 0000000000000000000000000000000000000000..c7e4c1acd531c185b88d3ef1b34c2f92304dea87 GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQg)s$jv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu`v@Z%kR R@j#OpJYD@<);T3K0RUX(DaQZ+ literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/chunk-between-data.png b/wai/pack-conformance/stills/chunk-between-data.png new file mode 100644 index 0000000000000000000000000000000000000000..e9163447b94980c5d9860f5e35143df7f877d968 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQnH>djv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-Csdba0Bv2uLiEBiObAE1aYF-ILqC#;=W=_u5K7SsV8N9bE v{%K#4Hb|K=JM8}b3)x$Oo}9gA8U2_+Fd@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I PXbOX;tDnm{r-UW|#=R-3 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/data-split.png b/wai/pack-conformance/stills/data-split.png new file mode 100644 index 0000000000000000000000000000000000000000..9b9c80fd122dd5599b2633db5fadc0400653365b GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQnH>djv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-Csdba0BupXi?TUZeSELP6rpykzfB!=EmY^qRuUSSvW>5?l V_i^>=n+{aS;OXk;vd$@?2>>*ND(3(I literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/expected.json b/wai/pack-conformance/stills/expected.json new file mode 100644 index 000000000..e8129f558 --- /dev/null +++ b/wai/pack-conformance/stills/expected.json @@ -0,0 +1,83 @@ +{ + "width": 5, + "height": 3, + "cases": [ + { + "file": "plain.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "palette.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "ancillary.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "data-split.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "module-still.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "interlace.png", + "verdict": "refused" + }, + { + "file": "bytes-after-end.png", + "verdict": "refused" + }, + { + "file": "short-row.png", + "verdict": "refused" + }, + { + "file": "palette-after-data.png", + "verdict": "refused" + }, + { + "file": "unknown-critical.png", + "verdict": "refused" + }, + { + "file": "no-end.png", + "verdict": "refused" + }, + { + "file": "chunk-between-data.png", + "verdict": "refused" + }, + { + "file": "crc.png", + "verdict": "refused" + }, + { + "file": "filter-5.png", + "verdict": "refused" + }, + { + "file": "rgba.png", + "verdict": "refused" + }, + { + "file": "window-past-32k.png", + "verdict": "refused" + }, + { + "file": "bytes-after-stream.png", + "verdict": "refused" + }, + { + "file": "stream-checksum.png", + "verdict": "refused" + } + ] +} diff --git a/wai/pack-conformance/stills/filter-5.png b/wai/pack-conformance/stills/filter-5.png new file mode 100644 index 0000000000000000000000000000000000000000..e3a252b2c94ba3609a31cc807f346ca38ba3e10d GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQdXWWjv*C{$+2fsg%mH$T>d@t z)3fyX;(DpNZ(@U1nms-CiT8HJKkX~h8`7rC4!eKkAr P&=dwwS3j3^P6N@hL?415;erV2#8G(6Pa{_fm{%o!l_dkAeD#OuG;oW6vrjLP! OFnGH9xvXd@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I E0O_J8w*UYD literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/palette-after-data.png b/wai/pack-conformance/stills/palette-after-data.png new file mode 100644 index 0000000000000000000000000000000000000000..32dbb385872dc6560cb097ba38540b4811cb3e2d GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQdXWWjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I gXbM|^Plzic6Eh1d+tlEE51mdKI;Vst0JkD6m;e9( literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/palette.png b/wai/pack-conformance/stills/palette.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e70bc0fed2183aca485110e5b4720c94c9fc54 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQfvV}A+C%}%q*;IQ-kw8fFf3& zE{-7;jL8vaQ-u^S%v}CG^V75R`Qm!18gF8QR+>FM_KEj)#Xs#U(grD0W{2Ise<6EI g(37**ETbPYD29vsBtHIm251U{r>mdKI;Vst04Gr_m;e9( literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/plain.png b/wai/pack-conformance/stills/plain.png new file mode 100644 index 0000000000000000000000000000000000000000..c066a0d5e80278b2a6833e2409bb46af1015b86e GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQdXWWjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I PXbOX;tDnm{r-UW|#%C$1 literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/rgba.png b/wai/pack-conformance/stills/rgba.png new file mode 100644 index 0000000000000000000000000000000000000000..8724d0995cfd52c3a53e3213d4cff07c63ec211b GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!3HFw&5r&6QdXWWjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu_oKK^+I PXbOX;tDnm{r-UW|(wZsK literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/short-row.png b/wai/pack-conformance/stills/short-row.png new file mode 100644 index 0000000000000000000000000000000000000000..1a044cd671d6881204c3cc1eff14b64b17c4d9f3 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQkI@Bjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qZzrl#ojHFd9xH~ O2!p4qpUXO@geCxqvM5Rb literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/stream-checksum.png b/wai/pack-conformance/stills/stream-checksum.png new file mode 100644 index 0000000000000000000000000000000000000000..fd2662ca66a99ccb51039c8bd9b6755926ce614d GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQdXWWjv*C{$q{E$g%mH$T>d@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu`(JvkW# PG=;&_)z4*}Q$iB}xos%E literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/stills/unknown-critical.png b/wai/pack-conformance/stills/unknown-critical.png new file mode 100644 index 0000000000000000000000000000000000000000..d4555b35caa654956fe2c44f5bf28906108b7d17 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQjCE?!6A&P_3sjZ94k*3$B+uf zd@t z)3fyX;(DnXZ(@U1nms-CiT8HJKkX~h1}Rf!huyz_A$v>Ele5<>qaQOUhKu{iys^vx Pn!@1e>gTe~DWM4fzg;MF literal 0 HcmV?d00001 diff --git a/wai/wai-pack/src/golden.rs b/wai/wai-pack/src/golden.rs index 604c1f64e..4016991f7 100644 --- a/wai/wai-pack/src/golden.rs +++ b/wai/wai-pack/src/golden.rs @@ -1201,6 +1201,86 @@ pub fn v1_receipts() -> (Files, Value) { (files, json!({"cases": cases})) } +// ─────────────────────────────── stills ─────────────────────────────── + +/// `stills/`: PNG files a still's reader (§5.7, "any PNG of 8-bit RGB without interlace") +/// reads or refuses, each with its verdict and, read, the SHA-256 of its RGB8 samples. +/// They are the module's still of a 5 × 3 picture rebuilt chunk by chunk: with a +/// suggested palette, ancillary chunks, the image data split in two; and with an +/// interlace, bytes after IEND, a short row, a palette after the image data, an unknown +/// critical chunk, no IEND, a chunk between the image data, a CRC, a sixth filter, +/// another colour type, a zlib window past 32 KiB, bytes after the zlib stream, and a +/// wrong Adler-32. +pub fn stills() -> (Files, Value) { + use crate::png::{adler32, chunk, deflate_fixed}; + let (w, h) = (5u32, 3u32); + let rgb: Vec = (0..3 * w * h).map(|i| (crate::fixture::noise(9, i, 0, 0) % 256) as u8).collect(); + let rows = |filter: u8, cut: usize| -> Vec { + let stride = 3 * w as usize; + let mut raw = Vec::new(); + for row in rgb.chunks_exact(stride) { + raw.push(filter); + raw.extend_from_slice(row); + } + raw.truncate(raw.len() - cut); + raw + }; + let zlib = |raw: &[u8]| [&[0x78u8, 0x01][..], &deflate_fixed(raw), &adler32(raw).to_be_bytes()].concat(); + let ihdr = |w: u32, h: u32, colour: u8, interlace: u8| [&w.to_be_bytes()[..], &h.to_be_bytes(), &[8, colour, 0, 0, interlace]].concat(); + let png = |chunks: &[(&[u8; 4], Vec)]| { + let mut out = vec![0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; + for (k, d) in chunks { + chunk(&mut out, k, d); + } + out + }; + let z = zlib(&rows(0, 0)); + let (z1, z2) = z.split_at(z.len() / 2); + let good_ihdr = ihdr(w, h, 2, 0); + let plte = vec![1u8, 2, 3, 4, 5, 6]; + let text = b"Comment\0a still".to_vec(); + // CINFO 8, its check bits recomputed: a stream PNG does not allow, though it inflates. + let wide = [&[0x88u8, (31 - (0x88u32 * 256) % 31) as u8 % 31][..], &z[2..]].concat(); + let after = [&z[..], &z[z.len() - 4..]].concat(); + let mut sum = z.clone(); + *sum.last_mut().expect("adler") ^= 1; + let mut bad_crc = png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]); + bad_crc[8 + 8 + 13] ^= 1; // the first byte of IHDR's CRC + let cases: Vec<(&str, Vec, bool)> = vec![ + ("plain", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]), true), + ("palette", png(&[(b"IHDR", good_ihdr.clone()), (b"PLTE", plte.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]), true), + ("ancillary", png(&[(b"IHDR", good_ihdr.clone()), (b"tEXt", text.clone()), (b"IDAT", z.clone()), (b"tEXt", text.clone()), (b"IEND", vec![])]), true), + ("data-split", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z1.to_vec()), (b"IDAT", z2.to_vec()), (b"IEND", vec![])]), true), + ("module-still", crate::png::encode(&rgb, w, h), true), + ("interlace", png(&[(b"IHDR", ihdr(w, h, 2, 1)), (b"IDAT", z.clone()), (b"IEND", vec![])]), false), + ("bytes-after-end", [png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]), vec![0]].concat(), false), + ("short-row", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", zlib(&rows(0, 1))), (b"IEND", vec![])]), false), + ("palette-after-data", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"PLTE", plte), (b"IEND", vec![])]), false), + ("unknown-critical", png(&[(b"IHDR", good_ihdr.clone()), (b"QRST", vec![1]), (b"IDAT", z.clone()), (b"IEND", vec![])]), false), + ("no-end", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone())]), false), + ("chunk-between-data", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z1.to_vec()), (b"tEXt", text), (b"IDAT", z2.to_vec()), (b"IEND", vec![])]), false), + ("crc", bad_crc, false), + ("filter-5", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", zlib(&rows(5, 0))), (b"IEND", vec![])]), false), + ("rgba", png(&[(b"IHDR", ihdr(w, h, 6, 0)), (b"IDAT", z), (b"IEND", vec![])]), false), + ("window-past-32k", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", wide), (b"IEND", vec![])]), false), + ("bytes-after-stream", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", after), (b"IEND", vec![])]), false), + ("stream-checksum", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", sum), (b"IEND", vec![])]), false), + ]; + let mut files = BTreeMap::new(); + let mut out = Vec::new(); + // Each verdict is the case's, and a read file's samples are the picture's: what a + // reader gives is checked against them, never written into them. + for (name, bytes, ok) in cases { + let mut c = json!({"file": format!("{name}.png"), "verdict": if ok { "ok" } else { "refused" }}); + if ok { + c["rgb_sha256"] = json!(crate::sha256_hex(&rgb)); + } + files.insert(format!("stills/{name}.png"), bytes); + out.push(c); + } + (files, json!({"width": w, "height": h, "cases": out})) +} + // ─────────────────────────────── delivery ─────────────────────────────── /// The delivery corpus's plans (§8.9, test 13): the staged golden packed live and sent to @@ -1304,6 +1384,13 @@ pub fn regen(dir: &Path) -> std::io::Result<()> { } write_files(dir, &files)?; write_json(&pm.join("expected.json"), &expected)?; + let (files, expected) = stills(); + let st = dir.join("stills"); + if st.exists() { + std::fs::remove_dir_all(&st)?; + } + write_files(dir, &files)?; + write_json(&st.join("expected.json"), &expected)?; let dl = dir.join("delivery"); std::fs::create_dir_all(&dl)?; write_json(&dl.join("expected.json"), &delivery(dir)?)?; @@ -1389,6 +1476,27 @@ pub fn check(dir: &Path) -> std::io::Result> { if expected_held != want { out.push("catalogs/expected.json: the corpus and this implementation differ".into()); } + // The stills (§5.7): the files, and each one's verdict from this reader. + let mut held = read_tree(&dir.join("stills"))?; + let expected_held: Value = serde_json::from_slice(&held.remove("expected.json").unwrap_or_default()).map_err(io)?; + let (files, want) = stills(); + let files: Files = files.into_iter().map(|(k, v)| (k.trim_start_matches("stills/").to_string(), v)).collect(); + same_tree("stills", &files, &held, &mut out); + if expected_held != want { + out.push("stills/expected.json: the corpus and this implementation differ".into()); + } + for c in want["cases"].as_array().into_iter().flatten() { + let f = c["file"].as_str().unwrap_or(""); + let got = held.get(f).map(|b| crate::png::decode(b).map(|(w, h, px)| (w, h, crate::sha256_hex(&px)))); + let ok = match (got, c["verdict"].as_str()) { + (Some(Ok((w, h, sha))), Some("ok")) => c["rgb_sha256"] == sha && want["width"] == w && want["height"] == h, + (Some(Err(_)), Some("refused")) => true, + _ => false, + }; + if !ok { + out.push(format!("stills/{f}: not read as the corpus says")); + } + } // The delivery cases (§8.9). let held: Value = serde_json::from_slice(&std::fs::read(dir.join("delivery/expected.json"))?).map_err(io)?; if held != delivery(dir)? { diff --git a/wai/wai-pack/src/png.rs b/wai/wai-pack/src/png.rs index d18d872e3..116596242 100644 --- a/wai/wai-pack/src/png.rs +++ b/wai/wai-pack/src/png.rs @@ -19,9 +19,14 @@ //! becomes the latest position of its three bytes. The block ends with symbol 256, and //! the last byte is padded with zero bits. //! -//! The reader takes any PNG of 8-bit RGB without interlace: the chunks, each CRC checked, -//! the first `IHDR`, the `IDAT`s in order, `IEND` last and nothing after it; the zlib -//! stream inflated (any block type) with its Adler-32 checked; and the five filters. +//! The reader takes any PNG of 8-bit RGB without interlace (PNG's chunk rules): every +//! chunk's type four letters and its CRC checked; `IHDR` first, of 8-bit RGB, compression +//! and filter method 0 and no interlace; an optional `PLTE` (a suggested palette, which an +//! RGB picture may carry) before the image data, read past; the `IDAT`s consecutive; +//! ancillary chunks read past; any other critical chunk refused; `IEND` last, empty, and +//! nothing after it. The zlib stream, its window 32 KiB at most, is inflated (any block +//! type) with its Adler-32 checked and no byte of the image data after it; its rows are +//! exactly `1 + 3·width` bytes each, and the five filters are undone. //! //! [packaging]: ../../../extensions/packaging.md @@ -158,7 +163,8 @@ pub fn deflate_fixed(d: &[u8]) -> Vec { b.finish() } -fn chunk(out: &mut Vec, kind: &[u8; 4], data: &[u8]) { +/// One chunk: its length, type, data and CRC. +pub fn chunk(out: &mut Vec, kind: &[u8; 4], data: &[u8]) { out.extend_from_slice(&(data.len() as u32).to_be_bytes()); out.extend_from_slice(kind); out.extend_from_slice(data); @@ -220,6 +226,9 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { let mut ihdr: Option<(u32, u32)> = None; let mut z = Vec::new(); let mut ended = false; + let mut palette = false; + // 0: no IDAT yet; 1: in the IDAT run; 2: past it. + let mut idat = 0u8; while at < b.len() { if ended { return Err("bytes after IEND".into()); @@ -228,9 +237,15 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { let kind: [u8; 4] = b.get(at + 4..at + 8).ok_or("a chunk type past the end")?.try_into().expect("4"); let data = b.get(at + 8..at + 8 + len).ok_or("a chunk past the end")?; let crc = u32::from_be_bytes(b.get(at + 8 + len..at + 12 + len).ok_or("a chunk CRC past the end")?.try_into().expect("4")); + if !kind.iter().all(u8::is_ascii_alphabetic) { + return Err("a chunk type that is not four letters".into()); + } if crc != crc32(&[&kind, data]) { return Err(format!("{}: its CRC", String::from_utf8_lossy(&kind))); } + if idat == 1 && &kind != b"IDAT" { + idat = 2; + } match &kind { b"IHDR" => { if ihdr.is_some() || at != 8 || data.len() != 13 { @@ -245,8 +260,26 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { } ihdr = Some((w, h)); } - b"IDAT" => z.extend_from_slice(data), - b"IEND" => ended = true, + _ if ihdr.is_none() => return Err("a chunk before IHDR".into()), + b"PLTE" => { + if palette || idat != 0 || data.is_empty() || data.len() % 3 != 0 || data.len() > 768 { + return Err("PLTE: more than one, after the image data, or not 1 to 256 entries".into()); + } + palette = true; + } + b"IDAT" => { + if idat == 2 { + return Err("IDAT chunks that are not consecutive".into()); + } + idat = 1; + z.extend_from_slice(data); + } + b"IEND" => { + if !data.is_empty() { + return Err("IEND with data".into()); + } + ended = true; + } _ if kind[0] & 0x20 == 0 => return Err(format!("an unknown critical chunk {}", String::from_utf8_lossy(&kind))), _ => {} } @@ -258,9 +291,21 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { } let stride = 3 * w as usize; let want = (stride + 1) * h as usize; - let raw = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(&z, want).map_err(|e| format!("the zlib stream: {e:?}"))?; - if raw.len() != want || z.len() < 4 || u32::from_be_bytes(z[z.len() - 4..].try_into().expect("4")) != adler32(&raw) { - return Err("the rows are not the picture's size, or their Adler-32".into()); + // The zlib stream: a 32 KiB window at most (PNG's CINFO ≤ 7), its Adler-32 checked, + // every byte of the image data in it, and exactly the rows the picture has. + if z.first().is_none_or(|c| c >> 4 > 7) { + return Err("the zlib stream: no header, or a window past 32 KiB".into()); + } + use miniz_oxide::inflate::core::{decompress, inflate_flags as fl, DecompressorOxide}; + use miniz_oxide::inflate::TINFLStatus; + let mut raw = vec![0u8; want + 1]; + let flags = fl::TINFL_FLAG_PARSE_ZLIB_HEADER | fl::TINFL_FLAG_COMPUTE_ADLER32 | fl::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + let (status, read, written) = decompress(&mut DecompressorOxide::new(), &z, &mut raw, 0, flags); + if status != TINFLStatus::Done { + return Err(format!("the zlib stream: {status:?}")); + } + if read != z.len() || written != want { + return Err("the rows are not the picture's size, or bytes follow the zlib stream".into()); } let mut out = vec![0u8; stride * h as usize]; for y in 0..h as usize { -- 2.54.0 (Apple Git-157) From c6cbb7384386cc565618c50c4aaa2e138a269b58 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 19:47:05 -0400 Subject: [PATCH 09/21] wai-pack live writes as it goes: each object when published, each window when sealed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `wai-pack live` used to gather every object and window in memory and write them all after the end of the stream. It now writes: - each object when it is published; - each window's index, then its receipt, when the window is sealed; - the plan file and LISTING at the end. Each file is written to a hidden temporary file beside it, then renamed into place. If the source fails mid-stream (a short frame or a frame of another size): - every sealed window and the objects it lists are kept; - the objects only unsealed pieces published are removed; - no plan file and no LISTING are written; - it exits 1, naming what it kept. Once an object is written, its bytes are no longer held. A coded piece's stages now carry their entry lengths (`pack::Lens`), so window_index builds an entry without the envelope. The live packer moves each envelope into its publication. The packer hands out sealed windows with `take_sealed()` and keeps none. With --window 0, the packer still holds every piece's entry fields until the end of the stream. The module doc, the CLI help and packaging §9.2 now say exactly what is held. wai::pin::listing_from_sha256 builds the listing from per-file digests (the same lines as listing_from), so LISTING needs no file's bytes. Tests: - A CLI test feeds the stream through a pipe and checks that windows 0 and 1 and their objects are on disk while the stream is still open. - A second CLI test checks that a stream cut inside a frame keeps window 0 and its objects and nothing else. - A unit test checks that no open piece holds an envelope. - A wai-rs test checks that the listing from digests equals the listing from bytes. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/packaging.md | 11 +++ wai/wai-pack/src/bin/wai_pack.rs | 139 +++++++++++++++++++++++-------- wai/wai-pack/src/live.rs | 73 +++++++++++++--- wai/wai-pack/src/pack.rs | 41 +++++++-- wai/wai-pack/tests/staged.rs | 126 ++++++++++++++++++++++++++++ wai/wai-rs/src/pin.rs | 24 +++++- 6 files changed, 359 insertions(+), 55 deletions(-) diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index 63fbf7051..d9e90baa8 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -948,6 +948,17 @@ whose parent is the previous window's (`live-window`; none for the first). A pie objects are published when the frame that decides its end has arrived: its base then, and layer *k* `layer_lag_ms[k]` after it (§8.6, §8.9). +It writes as it goes: each object when it is published, and each window's index and +then its receipt when the window is sealed, every file whole (written beside its place +and renamed into it); the plan file and `LISTING` at the end of the source. An object's +bytes are not held once written. Until its window is sealed a piece is held as its +entries' fields, so with one window over the whole source (the default) every piece's +are held until the end; besides those it holds the frames of the piece being cut and +each piece's first frame, for the plan file. A source that fails before its end (a short +frame, a frame of another size) leaves every sealed window and the objects it lists, +removes the objects only unsealed pieces published, writes no plan file or `LISTING`, +and is an error that names what was kept. + **9.3** A version-1 receipt (`wai.video.package`'s segment map) keeps its bytes and still verifies. Its JSON form is RFC 8785 canonical with `version` 1: `asset_id`, `energy_provenance` (`null` when unmetered) with its evidence, `grant`, `joules_micro`, diff --git a/wai/wai-pack/src/bin/wai_pack.rs b/wai/wai-pack/src/bin/wai_pack.rs index 6af6b8874..4caf201ed 100644 --- a/wai/wai-pack/src/bin/wai_pack.rs +++ b/wai/wai-pack/src/bin/wai_pack.rs @@ -21,10 +21,17 @@ //! (`pack-meter`); it never writes an unmetered package in its place //! wai-pack live --policy --out --key [--signer-id ] [--window ] //! pack a Y4M stream read from standard input as it arrives (packaging §9.2): each -//! piece coded as the segmenter closes it, its objects written to /objects/, and -//! each window of pieces (one window when 0 or absent) sealed as -//! windows//index-.wsix and windows//package.receipt.json, chained to the one -//! before; then plan-.txt and LISTING. No floor: a supplied floor is the on-demand +//! piece coded as the segmenter closes it, and its objects written to /objects/ +//! then; each window of pieces written when it is sealed, as +//! windows//index-.wsix and then windows//package.receipt.json, chained to +//! the one before; at the end of the stream the last window, plan-.txt and +//! LISTING. Every file is written whole (a temporary file renamed into place). An +//! object's bytes are not held once written; with --window 0 (the default) the one +//! window is sealed at the end, so until then every piece's entry fields are held +//! (packaging §9.2). A source that fails mid-stream (a short frame, a frame of +//! another size) keeps every sealed window and the objects it lists, removes the +//! objects only unsealed pieces published, writes no plan file or LISTING, and +//! exits 1 saying what it kept. No floor: a supplied floor is the on-demand //! package's (§7.3). is new or empty //! wai-pack verify //! verify a package (packaging §9): every reason, or its counts and pack digest @@ -227,6 +234,78 @@ fn cmd_listing(args: &[String]) -> Result<(), String> { std::io::stdout().write_all(&l).map_err(|e| e.to_string()) } +/// What `wai-pack live` has written: each file by its listing path with its SHA-256 (the +/// listing is built from these at the end), each object's first piece, and the pieces the +/// windows written so far hold. +struct LiveOut<'a> { + dir: &'a Path, + comp: String, + listed: std::collections::BTreeMap, + first_piece: std::collections::BTreeMap, + windows: u64, + sealed_pieces: u64, +} + +impl LiveOut<'_> { + /// Write a file of the package whole or not at all: its bytes to a hidden temporary + /// file beside it, then renamed into place. + fn write(&mut self, path: &str, bytes: &[u8]) -> Result<(), String> { + let at = self.dir.join(path); + let parent = at.parent().ok_or_else(|| format!("{path}: no directory"))?; + std::fs::create_dir_all(parent).map_err(|e| format!("{}: {e}", parent.display()))?; + let name = at.file_name().and_then(|n| n.to_str()).ok_or_else(|| format!("{path}: no file name"))?; + let tmp = parent.join(format!(".{name}.tmp")); + std::fs::write(&tmp, bytes).map_err(|e| format!("{}: {e}", tmp.display()))?; + std::fs::rename(&tmp, &at).map_err(|e| format!("{}: {e}", at.display()))?; + self.listed.insert(path.to_string(), wai::pin::sha256_hex(bytes)); + Ok(()) + } + + /// Each object as it is published; one already written (a repeated piece's) once. + fn publish(&mut self, published: Vec) -> Result<(), String> { + for x in published { + println!("piece {} layer {} at frame {}: {}", x.piece, x.layer, x.at_frame, hex(&x.hash)); + let path = wai_pack::objects::object_path(&x.hash); + if !self.listed.contains_key(&path) { + self.write(&path, &x.envelope)?; + } + self.first_piece.entry(path).or_insert(x.piece); + } + Ok(()) + } + + /// Each window as it is sealed: its index, then its receipt. + fn seal(&mut self, windows: Vec) -> Result<(), String> { + for w in windows { + let n = w.number; + self.write(&format!("windows/{n}/{}", wai::package::index_file(&self.comp)), &w.index)?; + self.write(&format!("windows/{n}/package.receipt.json"), w.receipt.to_json().as_bytes())?; + println!("window {n}: pieces {} to {}, receipt {}", w.first_piece, w.first_piece + w.pieces - 1, hex(&w.receipt.receipt_hash())); + self.windows = n + 1; + self.sealed_pieces = w.first_piece + w.pieces; + } + Ok(()) + } + + /// The session ended in `why`: every sealed window and the objects they list are + /// kept; the objects only unsealed pieces published are removed, and no plan file or + /// LISTING is written. The error says what was kept. + fn abandon(&mut self, why: String) -> String { + let mut removed = 0; + for (path, &p) in &self.first_piece { + if p >= self.sealed_pieces && std::fs::remove_file(self.dir.join(path)).is_ok() { + removed += 1; + } + } + let kept = self.first_piece.values().filter(|&&p| p < self.sealed_pieces).count(); + let windows = match self.windows { + 0 => "no window".to_string(), + n => format!("windows 0 to {} (pieces 0 to {}) with the {kept} objects they list", n - 1, self.sealed_pieces - 1), + }; + format!("{why}; kept {windows} in {}; removed {removed} objects of pieces no window sealed; wrote no plan file and no LISTING", self.dir.display()) + } +} + fn cmd_live(args: &[String]) -> Result<(), String> { let path = opt(args, "--policy").ok_or("live: --policy")?; let out = opt(args, "--out").ok_or("live: --out")?; @@ -244,38 +323,32 @@ fn cmd_live(args: &[String]) -> Result<(), String> { { return Err("live: pack-source-rate: the source's frame rate is not the component's timebase".into()); } - let mut files: std::collections::BTreeMap> = std::collections::BTreeMap::new(); - let publish = |p: Vec, files: &mut std::collections::BTreeMap>| { - for x in p { - println!("piece {} layer {} at frame {}: {}", x.piece, x.layer, x.at_frame, hex(&x.hash)); - files.insert(wai_pack::objects::object_path(&x.hash), x.envelope); - } + let mut w = LiveOut { + dir: Path::new(out), + comp: lp.policy().components[0].id.clone(), + listed: Default::default(), + first_piece: Default::default(), + windows: 0, + sealed_pieces: 0, }; - while let Some(f) = r.next_frame() { - let f = f.map_err(|e| e.to_string())?; - let p = lp.push(&f.rgb).map_err(|e| e.to_string())?; - publish(p, &mut files); - } - let comp = lp.policy().components[0].id.clone(); - let (p, windows, starts) = lp.finish().map_err(|e| e.to_string())?; - publish(p, &mut files); - for (n, w) in windows.iter().enumerate() { - files.insert(format!("windows/{n}/{}", wai::package::index_file(&comp)), w.index.clone()); - files.insert(format!("windows/{n}/package.receipt.json"), w.receipt.to_json().into_bytes()); - println!("window {n}: receipt {}", hex(&w.receipt.receipt_hash())); - } - files.insert(format!("plan-{comp}.txt"), wai_pack::pack::plan_file(&starts).into_bytes()); - let view: std::collections::BTreeMap = files.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect(); - let listing = wai::pin::listing_from(&view).map_err(|e| e.to_string())?; - files.insert("LISTING".into(), listing); - for (p, b) in &files { - let at = Path::new(out).join(p); - if let Some(d) = at.parent() { - std::fs::create_dir_all(d).map_err(|e| format!("{}: {e}", d.display()))?; + let mut frames = || -> Result<(), String> { + while let Some(f) = r.next_frame() { + let f = f.map_err(|e| e.to_string())?; + let p = lp.push(&f.rgb).map_err(|e| e.to_string())?; + w.publish(p)?; + w.seal(lp.take_sealed())?; } - std::fs::write(&at, b).map_err(|e| format!("{}: {e}", at.display()))?; + Ok(()) + }; + if let Err(e) = frames() { + return Err(w.abandon(format!("live: {e}"))); } - Ok(()) + let (p, windows, starts) = lp.finish().map_err(|e| w.abandon(format!("live: {e}")))?; + w.publish(p).map_err(|e| w.abandon(format!("live: {e}")))?; + w.seal(windows).map_err(|e| w.abandon(format!("live: {e}")))?; + w.write(&format!("plan-{}.txt", w.comp), wai_pack::pack::plan_file(&starts).as_bytes())?; + let listing = wai::pin::listing_from_sha256(&w.listed).map_err(|e| e.to_string())?; + w.write("LISTING", &listing) } fn cmd_sim(args: &[String]) -> Result<(), String> { diff --git a/wai/wai-pack/src/live.rs b/wai/wai-pack/src/live.rs index 382156889..b6751aa72 100644 --- a/wai/wai-pack/src/live.rs +++ b/wai/wai-pack/src/live.rs @@ -14,6 +14,16 @@ //! base then, and layer `k` `layer_lag_ms[k]` after it (§8.6), which [`Published`] states //! for the delivery simulator (`crate::delivery`). //! +//! What it holds. Each object's bytes go to the caller in its [`Published`] and are not +//! kept: an open piece keeps, per object, its entry's fields (hash, prefix digest, +//! capability, lengths, layer, tier, deadline, SSE), from which its window's index is +//! built. Besides those it holds the frames from the first of the piece being cut (at +//! most the policy's longest piece plus `lookahead_frames`), each piece's first frame +//! for the plan file (eight bytes a piece, for the session), and the windows sealed and +//! not yet taken ([`LivePacker::take_sealed`]). With windows of `n` pieces the open +//! records are at most `n` pieces'; with one window over the session (`window_pieces` +//! 0) they are every piece's, sealed by [`LivePacker::finish`]. +//! //! [packaging]: ../../../extensions/packaging.md use crate::pack::{CodedPiece, Coder, PackError}; @@ -64,11 +74,15 @@ pub struct Published { /// the plan (each piece's first frame). pub type Finished = (Vec, Vec, Vec); -/// A window: its stage index and the receipt that seals it. +/// A window: its stage index and the receipt that seals it, and the pieces it holds. #[derive(Debug, Clone)] pub struct LiveWindow { pub index: Vec, pub receipt: PackageReceiptV2, + /// Its window number from 0, its first piece and its pieces. + pub number: u64, + pub first_piece: u64, + pub pieces: u64, } /// The live packer of a one-component video policy. @@ -88,7 +102,9 @@ pub struct LivePacker { first_entry: u64, prev_index: [u8; 32], prev_receipt: Option<[u8; 32]>, + /// Windows sealed and not yet taken, and how many were sealed in all. windows: Vec, + sealed: u64, } impl LivePacker { @@ -124,6 +140,7 @@ impl LivePacker { prev_index: [0u8; 32], prev_receipt: None, windows: Vec::new(), + sealed: 0, }) } @@ -139,9 +156,10 @@ impl LivePacker { let comp = &self.policy.components[0]; let coder = Coder { policy: &self.policy, comp, w: self.w, h: self.h }; let from = (piece.start - self.held_from) as usize; - let cp = coder.code(&piece, &self.held[from..from + piece.len as usize])?; + let mut cp = coder.code(&piece, &self.held[from..from + piece.len as usize])?; let number = self.first_piece + self.open.len() as u64; - for s in &cp.stages { + // Each object's bytes go to the caller; the piece keeps its entry's fields. + for s in &mut cp.stages { out.push(Published { piece: number, start: piece.start, @@ -152,7 +170,7 @@ impl LivePacker { capability: s.capability.clone(), deadline_ms: s.deadline_ms, hash: s.hash, - envelope: s.envelope.clone(), + envelope: std::mem::take(&mut s.envelope), }); } // The frames before the next piece are no longer needed. @@ -203,29 +221,31 @@ impl LivePacker { parent, ) .map_err(|r| PackError::new(r.code, r.detail))?; + let window = LiveWindow { index: bytes, receipt, number: self.sealed, first_piece: self.first_piece, pieces: self.open.len() as u64 }; self.first_piece += self.open.len() as u64; self.first_entry += w.index.entries.len() as u64; self.prev_index = w.index.digest(); - self.prev_receipt = Some(receipt.receipt_hash()); + self.prev_receipt = Some(window.receipt.receipt_hash()); self.open.clear(); - self.windows.push(LiveWindow { index: bytes, receipt }); + self.sealed += 1; + self.windows.push(window); Ok(()) } + /// The windows sealed since the last call, in order; the packer keeps none of them. + pub fn take_sealed(&mut self) -> Vec { + std::mem::take(&mut self.windows) + } + /// The end of the source: the last pieces coded and published, the last window - /// sealed. Returns the published objects, every window, and the plan (each piece's - /// first frame). + /// sealed. Returns the published objects, the windows not yet taken (every window, + /// if none was), and the plan (each piece's first frame). pub fn finish(mut self) -> Result { let pieces = self.seg.take().expect("open until finish").finish(); let out = self.code(pieces)?; self.close_window()?; Ok((out, self.windows, self.starts)) } - - /// The windows sealed so far. - pub fn windows(&self) -> &[LiveWindow] { - &self.windows - } } /// What a live and an on-demand pack of one source share (test 12): of `files`, the plan @@ -253,3 +273,30 @@ pub fn primary_files(files: &std::collections::BTreeMap>) -> Res } Ok(out) } + +#[cfg(test)] +mod tests { + use super::*; + + /// What a live packer holds (§9.2): with one window over the session, every open + /// piece's records and none of its objects' bytes, which went out in its + /// publications; and a sealed window, once taken, not at all. + #[test] + fn a_published_object_is_not_held() { + let g = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../pack-conformance/packs/video-staged-01"); + let policy = std::fs::read(g.join("policy.toml")).expect("the golden's policy"); + let (hd, frames) = crate::source::y4m::read_all(&crate::fixture::generate("video-staged-01").expect("a fixture")).expect("its frames"); + let key = SigningKey::from_bytes(&[7; 32]); + let mut lp = LivePacker::new(&policy, hd.w, hd.h, LiveOpts::new(key, "test")).expect("a live packer"); + let mut published = 0u64; + for f in &frames { + published += lp.push(&f.rgb).expect("a frame").iter().map(|p| p.envelope.len() as u64).sum::(); + } + assert!(lp.open.len() >= 4, "every piece but the last is open: {}", lp.open.len()); + assert!(published > 0); + assert!(lp.open.iter().flat_map(|cp| &cp.stages).all(|s| s.envelope.is_empty() && s.lens.byte_len > 0), "a published object's bytes are held"); + lp.close_window().expect("a window"); + assert_eq!(lp.take_sealed().len(), 1); + assert!(lp.take_sealed().is_empty() && lp.open.is_empty(), "a taken window, or its pieces, are held"); + } +} diff --git a/wai/wai-pack/src/pack.rs b/wai/wai-pack/src/pack.rs index b0265e78e..e9415dbb5 100644 --- a/wai/wai-pack/src/pack.rs +++ b/wai/wai-pack/src/pack.rs @@ -236,9 +236,29 @@ const CAP_INT_MOTION: &str = "wai.video.int_motion"; const CAP_INT_REFINE: &str = "wai.video.int_refine"; const CAP_PNG: &str = "wai.image.png"; -/// One object of a piece, with what its entry states (§6.2). +/// An object's lengths as its entry states them (§6.2): the whole envelope, and its +/// manifest and payload by [`object_lengths`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Lens { + pub byte_len: u64, + pub manifest_len: u32, + pub payload_len: u64, +} + +impl Lens { + /// The lengths of an object the packer wrote; one it cannot count is `pack-encode`. + pub fn of(env: &[u8]) -> Result { + let (manifest_len, payload_len) = object_lengths(env).map_err(|e| PackError::new("pack-encode", format!("an object the packer wrote is not countable: {e}")))?; + Ok(Lens { byte_len: env.len() as u64, manifest_len, payload_len }) + } +} + +/// One object of a piece, with what its entry states (§6.2). Its envelope may have been +/// handed on (a live packer writes each object as it is published and keeps only this +/// record): the entry is built from `hash` and `lens`, never from `envelope`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Stage { + pub lens: Lens, pub envelope: Vec, pub hash: [u8; 32], pub capability: String, @@ -264,7 +284,7 @@ pub struct CodedPiece { impl CodedPiece { /// The bytes of each layer's object: what the class plan reads (§8.1). pub fn layer_bytes(&self) -> Vec { - self.stages.iter().filter(|s| matches!(s.role, Role::Base | Role::Refinement)).map(|s| s.envelope.len() as u64).collect() + self.stages.iter().filter(|s| matches!(s.role, Role::Base | Role::Refinement)).map(|s| s.lens.byte_len).collect() } } @@ -336,6 +356,7 @@ impl<'a> Coder<'a> { sse: crate::encode::sse(&recon, src), deadline_ms: 0, flags: 0, + lens: Lens::of(&base_env)?, envelope: base_env, }]; let deadline = u32::try_from(pp.deadline_ms).map_err(|_| enc("a deadline past 2^32 - 1".into()))?; @@ -369,6 +390,7 @@ impl<'a> Coder<'a> { sse: crate::encode::sse(&next, src), deadline_ms: deadline, flags: 0, + lens: Lens::of(&env)?, envelope: env, }); below = next; @@ -389,7 +411,8 @@ fn sink_config() -> wai::staged::ChainConfig { } } -/// A window's stage index (§6) over coded pieces, and its objects by content hash. +/// A window's stage index (§6) over coded pieces, and its objects by content hash (those +/// whose stage still holds its envelope). pub struct Window { pub index: StageIndex, pub objects: BTreeMap<[u8; 32], Vec>, @@ -420,7 +443,6 @@ pub fn window_index(policy: &Policy, comp: &crate::policy::Component, pieces: &[ (strings.len() - 1) as u16 } }; - let (man, payload_len) = object_lengths(&s.envelope).map_err(|e| PackError::new("pack-encode", format!("an object the packer wrote is not countable: {e}")))?; let mut flags = s.flags; if entries.iter().any(|x| x.content_hash == s.hash) { flags |= wsix::ENTRY_DUP; @@ -429,7 +451,7 @@ pub fn window_index(policy: &Policy, comp: &crate::policy::Component, pieces: &[ let refines = if s.role == Role::Refinement { entries.len() as u32 - 1 } else { wsix::NO_REFINES }; entries.push(EntryRec { content_hash: s.hash, - byte_len: s.envelope.len() as u64, + byte_len: s.lens.byte_len, piece: number, refines, capability: cap, @@ -440,10 +462,12 @@ pub fn window_index(policy: &Policy, comp: &crate::policy::Component, pieces: &[ deadline_ms: s.deadline_ms, prefix_digest: s.prefix_digest, sse: s.sse, - manifest_len: man, - payload_len, + manifest_len: s.lens.manifest_len, + payload_len: s.lens.payload_len, }); - objects.insert(s.hash, s.envelope.clone()); + if !s.envelope.is_empty() { + objects.insert(s.hash, s.envelope.clone()); + } } piece_recs.push(PieceRec { start: cp.piece.start, @@ -562,6 +586,7 @@ pub fn pack(policy_bytes: &[u8], sources: &BTreeMap>, opts: &mut sse: wsix::SSE_UNKNOWN, deadline_ms: 0, flags: wsix::ENTRY_DERIVED_FROM, + lens: Lens::of(&env)?, envelope: env, }); coded.push(cp); diff --git a/wai/wai-pack/tests/staged.rs b/wai/wai-pack/tests/staged.rs index d867e4ede..0f73afd9e 100644 --- a/wai/wai-pack/tests/staged.rs +++ b/wai/wai-pack/tests/staged.rs @@ -230,6 +230,132 @@ fn live_over_a_pipe_is_on_demand() { let _ = std::fs::remove_dir_all(&dir); } +/// The staged fixture as a Y4M stream: its bytes, the header's length, and each frame's +/// length (its `FRAME` line and samples). +fn y4m_stream() -> (Vec, usize, usize) { + let y = wai_pack::fixture::generate(PACK).unwrap(); + let (_, frames) = wai_pack::source::y4m::read_all(&y).unwrap(); + let header = y.iter().position(|&b| b == b'\n').unwrap() + 1; + assert_eq!((y.len() - header) % frames.len(), 0); + let frame = (y.len() - header) / frames.len(); + (y, header, frame) +} + +fn live_child(dir: &Path, window: &str) -> std::process::Child { + let g = corpus().join("packs").join(PACK); + std::process::Command::new(env!("CARGO_BIN_EXE_wai-pack")) + .args(["live", "--policy", g.join("policy.toml").to_str().unwrap(), "--out", dir.to_str().unwrap(), "--key", g.join("key.hex").to_str().unwrap(), "--window", window]) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap() +} + +/// The objects a window's index lists as bases and refinements, by path. +fn listed_objects(files: &BTreeMap>, window: u64) -> Vec { + let ix = StageIndex::parse(&files[&format!("windows/{window}/index-v.wsix")]).unwrap(); + ix.entries.iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)).map(|e| wai_pack::objects::object_path(&e.content_hash)).collect() +} + +/// Every name below `dir`, hidden ones too: a temporary file left behind shows here. +fn every_name(dir: &Path) -> Vec { + let mut out = Vec::new(); + for e in std::fs::read_dir(dir).unwrap() { + let e = e.unwrap(); + let name = e.file_name().into_string().unwrap(); + if e.file_type().unwrap().is_dir() { + out.extend(every_name(&e.path()).into_iter().map(|n| format!("{name}/{n}"))); + } else { + out.push(name); + } + } + out +} + +/// `wai-pack live` writes as it goes (packaging §9.2): with the stream still open, each +/// window sealed so far is on disk with its receipt and every object it lists, and +/// nothing the end of the stream writes is; at the end, no file written early has +/// changed, and LISTING lists every file. +#[test] +fn live_writes_each_window_as_it_is_sealed() { + use std::io::Write as _; + let dir = std::env::temp_dir().join(format!("wai-pack-live-early-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let (y, header, frame) = y4m_stream(); + let mut child = live_child(&dir, "2"); + let mut stdin = child.stdin.take().unwrap(); + // Window 1 holds pieces 2 and 3; piece 3 closes when frame 146 arrives (test 12). + let upto = header + 151 * frame; + stdin.write_all(&y[..upto]).unwrap(); + stdin.flush().unwrap(); + // Wait for the packer with the stream open. The bound only keeps a broken packer from + // hanging the test; nothing is asserted about how long it takes. + let receipt1 = dir.join("windows/1/package.receipt.json"); + let mut polls = 0; + while !receipt1.exists() { + assert!(child.try_wait().unwrap().is_none(), "the packer ended with the stream open"); + assert!(polls < 6000, "window 1 is not written while the stream is open"); + std::thread::sleep(std::time::Duration::from_millis(100)); + polls += 1; + } + let early = held(&dir); + for n in 0..2u64 { + let ix = &early[&format!("windows/{n}/index-v.wsix")]; + let r = wai::package::PackageReceiptV2::from_json(std::str::from_utf8(&early[&format!("windows/{n}/package.receipt.json")]).unwrap()).unwrap(); + r.verify_detailed(&[ix]).unwrap(); + for o in listed_objects(&early, n) { + assert!(early.contains_key(&o), "window {n}: {o} not written with it"); + } + } + assert!(!early.keys().any(|p| p.starts_with("windows/2/") || p == "LISTING" || p.starts_with("plan-")), "{:?}", early.keys().collect::>()); + stdin.write_all(&y[upto..]).unwrap(); + drop(stdin); + let out = child.wait_with_output().unwrap(); + assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr)); + let done = held(&dir); + for (p, b) in &early { + assert_eq!(done.get(p), Some(b), "{p} changed after it was written"); + } + let mut rest = done.clone(); + let listing = rest.remove("LISTING").unwrap(); + assert_eq!(listing, listing_of(&rest), "LISTING lists every file"); + assert!(done.contains_key("windows/2/package.receipt.json") && done.contains_key("plan-v.txt")); + assert!(every_name(&dir).iter().all(|n| !n.split('/').any(|s| s.starts_with('.'))), "a temporary file was left: {:?}", every_name(&dir)); + let _ = std::fs::remove_dir_all(&dir); +} + +/// A stream cut short inside a frame: `wai-pack live` exits 1 and says what it kept. The +/// window sealed before the cut stays, with its receipt and every object it lists; the +/// objects of the piece no window sealed (the still, published at frame 121) are +/// removed; no plan file or LISTING is written. +#[test] +fn a_truncated_stream_keeps_its_sealed_windows() { + use std::io::Write as _; + let dir = std::env::temp_dir().join(format!("wai-pack-live-cut-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let (y, header, frame) = y4m_stream(); + let mut child = live_child(&dir, "2"); + let mut stdin = child.stdin.take().unwrap(); + stdin.write_all(&y[..header + 126 * frame + frame / 2]).unwrap(); + drop(stdin); + let out = child.wait_with_output().unwrap(); + let err = String::from_utf8_lossy(&out.stderr); + assert_eq!(out.status.code(), Some(1), "{err}"); + assert!(err.contains("kept windows 0 to 0 (pieces 0 to 1)"), "{err}"); + let kept = held(&dir); + let r = wai::package::PackageReceiptV2::from_json(std::str::from_utf8(&kept["windows/0/package.receipt.json"]).unwrap()).unwrap(); + r.verify_detailed(&[&kept["windows/0/index-v.wsix"]]).unwrap(); + let mut want: Vec = listed_objects(&kept, 0); + want.extend(["windows/0/index-v.wsix".to_string(), "windows/0/package.receipt.json".to_string()]); + want.sort(); + want.dedup(); + assert_eq!(kept.keys().cloned().collect::>(), want, "the sealed window and its objects, and nothing else"); + let removed: usize = err.split("removed ").nth(1).and_then(|s| s.split(' ').next()).and_then(|n| n.parse().ok()).unwrap(); + assert!(removed > 0, "the still's objects were written, then removed: {err}"); + let _ = std::fs::remove_dir_all(&dir); +} + /// Test 12's counter-case: with one frame less of lookahead than the on-demand cut, the /// live packer cuts `cuts-01`'s flash, so the two plans part. #[test] diff --git a/wai/wai-rs/src/pin.rs b/wai/wai-rs/src/pin.rs index dfbba4eb7..9c2fa314b 100644 --- a/wai/wai-rs/src/pin.rs +++ b/wai/wai-rs/src/pin.rs @@ -157,12 +157,22 @@ pub fn parse_listing(bytes: &[u8]) -> Result, PinRefusal /// Write the listing of the files `files` (path → bytes). The paths must obey the /// listing rules; the lines come out sorted. pub fn listing_from(files: &BTreeMap) -> Result, PinRefusal> { + listing_from_sha256(&files.iter().map(|(p, b)| (p.clone(), sha256_hex(b))).collect()) +} + +/// The listing [`listing_from`] writes, of files given by their SHA-256 (path → 64 +/// lowercase hexadecimal digits): for a writer that hashed each file as it wrote it and +/// no longer holds the bytes. +pub fn listing_from_sha256(files: &BTreeMap) -> Result, PinRefusal> { let mut paths: Vec<&str> = files.keys().map(String::as_str).collect(); paths.sort_unstable_by(|a, b| a.as_bytes().cmp(b.as_bytes())); check_paths(&paths)?; let mut out = Vec::new(); for p in paths { - out.extend_from_slice(sha256_hex(files[p]).as_bytes()); + if !is_sha256_hex(&files[p]) { + return Err(malformed(format!("{p}: its digest is not 64 lowercase hexadecimal digits"))); + } + out.extend_from_slice(files[p].as_bytes()); out.extend_from_slice(b" "); out.extend_from_slice(p.as_bytes()); out.push(b'\n'); @@ -518,6 +528,18 @@ mod tests { assert!(line.contains("supersedes \"a"), "{line}"); } + /// A listing from digests is the listing from bytes, and a digest outside WAI's + /// grammar is refused, not written. + #[test] + fn a_listing_from_digests_is_the_listing_from_bytes() { + let files: BTreeMap = [("z".to_string(), &b"z"[..]), ("a/c.onnx".into(), b"c"), ("a.b".into(), b"a")].into_iter().collect(); + let digests: BTreeMap = files.iter().map(|(p, b)| (p.clone(), sha256_hex(b))).collect(); + assert_eq!(listing_from_sha256(&digests).unwrap(), listing_from(&files).unwrap()); + let mut upper = digests.clone(); + upper.insert("z".into(), sha256_hex(b"z").to_uppercase()); + assert!(listing_from_sha256(&upper).is_err()); + } + #[test] fn listing_rules() { let ok: Vec = [line(b"a", "a.b"), line(b"c", "a/c.onnx"), line(b"z", "z")].concat(); -- 2.54.0 (Apple Git-157) From b6858ea485e787a416763e7e015105fd23397ab0 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 19:52:14 -0400 Subject: [PATCH 10/21] pack-conformance: a live-window corpus, checked by both implementations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The corpus had no live windows, so pack_verify.py never read a live-window receipt, a non-zero `prev_index_digest` or a `first_entry` past zero. live/ holds the staged golden's source packed live with its key, in windows of two pieces: each window's index and receipt, and expected.json with each window's place in the chain, index digest and receipt hash. pack_verify.py reads each window with its own index reader and §6.3 rules, and verifies the receipt (§9.1, strict Ed25519). It then checks the chain: - the parent receipt (`live-window`); - `prev_index_digest`; - `first_piece` and `first_entry`; - the timeline; - the signer is the golden's key. It also checks that each window's pieces, entries (but flags and window-relative `refines`), class plan and plan digest are the golden's. wai-pack's conformance check writes the windows again, compares their bytes, and verifies the same chain. Co-Authored-By: Claude Opus 5.5 --- wai/pack-conformance/README.md | 14 ++- wai/pack-conformance/live/expected.json | 31 ++++++ .../live/windows/0/index-v.wsix | Bin 0 -> 1120 bytes .../live/windows/0/package.receipt.json | 1 + .../live/windows/1/index-v.wsix | Bin 0 -> 880 bytes .../live/windows/1/package.receipt.json | 1 + .../live/windows/2/index-v.wsix | Bin 0 -> 704 bytes .../live/windows/2/package.receipt.json | 1 + wai/pack-conformance/pack_verify.py | 69 ++++++++++++ wai/wai-pack/src/golden.rs | 99 ++++++++++++++++++ 10 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 wai/pack-conformance/live/expected.json create mode 100644 wai/pack-conformance/live/windows/0/index-v.wsix create mode 100644 wai/pack-conformance/live/windows/0/package.receipt.json create mode 100644 wai/pack-conformance/live/windows/1/index-v.wsix create mode 100644 wai/pack-conformance/live/windows/1/package.receipt.json create mode 100644 wai/pack-conformance/live/windows/2/index-v.wsix create mode 100644 wai/pack-conformance/live/windows/2/package.receipt.json diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index 45d943957..6fc664508 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -67,6 +67,14 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). `"signature": "refused"`. - `catalogs/` — stage indexes no golden package has (refinements on more than one deadline), with the MoQ catalog each gives (§8.4). +- `live/` — the staged golden's source packed live (§9.2) with its key, in windows of + two pieces: each window's stage index and receipt (`windows//`), and + `expected.json`: each window's first piece and entry, its pieces, its index digest and + its receipt hash. Each window verifies against its receipt; the first starts the + chain, and each later one names the receipt before it as its `live-window` parent, + the index before it as its `prev_index_digest`, and continues its pieces and entries. + Its pieces, bases and refinements are the golden's, entry for entry (a live pack has + no floor). - `stills/` — PNG files a still's reader reads or refuses (§5.7: any PNG of 8-bit RGB without interlace), each a generated 5 × 3 picture written chunk by chunk, and `expected.json`: each file's verdict and, read, the SHA-256 of its RGB8 samples. Read: @@ -128,7 +136,8 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. `decode_int` to the encoder's reconstruction and recomputes its SSE. It packs each golden again and compares every file, verifies the committed package (`wai-pack verify`), gives every mutation and every package mutation its - reason, and reads every still to its verdict. `cargo test` + reason, reads every still to its verdict, and checks the live windows' chain. + `cargo test` runs the same check, with the scene cuts of each fixture stated as tests (`tests/scenecut.rs`). - `pack_verify.py`, Python standard library and, to sign what it re-derives, `cryptography` (TOML through `tomllib`, so 3.11 or @@ -154,7 +163,8 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. §4.1 check. Every HTTP playlist of a golden is read by `../http-conformance/http_verify.py`'s union, floor and adjacency checks (http-delivery §4.2), and every file of `stills/` by its own PNG reader, to the - corpus's verdict. From the regenerated fixture it re-derives what only the source decides: + corpus's verdict. Each window of `live/` it reads as an index and a receipt, checks + the chain, and compares with the golden's index. From the regenerated fixture it re-derives what only the source decides: the pieces, their rules, moments, ladders and class plan, each base's SSE and step and the encoder's choices, each still's bytes, and each refinement layer's bytes with its own rANS coder and table fit; and it checks the supplied floor against the policy's diff --git a/wai/pack-conformance/live/expected.json b/wai/pack-conformance/live/expected.json new file mode 100644 index 000000000..83f381b36 --- /dev/null +++ b/wai/pack-conformance/live/expected.json @@ -0,0 +1,31 @@ +{ + "pack": "video-staged-01", + "component": "v", + "window_pieces": 2, + "windows": [ + { + "window": 0, + "first_piece": 0, + "pieces": 2, + "first_entry": 0, + "index_digest": "66b9399454a9937a52eef21bb7c452c8a03257d707d4a8238e99db447926aca0", + "receipt_hash": "c8143d2452499a4d3772e40c528255eae90e781511995a76b1cabd12c87b5950" + }, + { + "window": 1, + "first_piece": 2, + "pieces": 2, + "first_entry": 6, + "index_digest": "b31b0d7e1faab1b7331bf26f8400ca848692aba315e2e8083086d1523baac510", + "receipt_hash": "03e9c415609e457da2a3d47298d91c541b36f03b634fb56060c9a4da1c2d8f13" + }, + { + "window": 2, + "first_piece": 4, + "pieces": 1, + "first_entry": 10, + "index_digest": "61c64b3027c30891039da2d2b8e30744d83bd6f5b695d6b00ea616c575ceab94", + "receipt_hash": "11621610dbfff4321e11dc4ff8ba32cb2cb9e61fb17707a114bea4ec85d84e6c" + } + ] +} diff --git a/wai/pack-conformance/live/windows/0/index-v.wsix b/wai/pack-conformance/live/windows/0/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..4162eb3fa27e9c58ce9e18e9b0c97505dbacc949 GIT binary patch literal 1120 zcmWF!_KaX;U|^60Vi3&)#B5N^45X#srp*+;dA(1HKXvKK8Mgyo_Sx?JVRlCA0sp5p z)34Qq1C@{jOq5ULO`qJGIO$r(v{r_&grqo5Dc#*WzBjR~``LKAkO!AxMoEu0%U>00R;FRogesZvdvqt_3Ih=*JtK2>nlY3cWQB3)l+o!n6<%T{bT>Z>b8MN z28K5&PzIE$dbR7%oPbN6Tfaq>G^}VZ`RV=g-O=rA2Qy5U&fXWf;LTwU6z76Nu&i8t2upVcqF(vG% z=G_e4`bk;At!Z{v?1Cd^jye-xR@d@~t=~`=pO>+C-nSg-KveZ09?*U!nEi3@%8$r) z=k9oWu%V<}uzR(*_m3U7>>MVtaTsJNH2j_1Ud@hT{~3Q&^R%b7)fyQHT}$5Z<3#_7 z8y605P0^a(@`2ajoYuxN6Pd(Es$lKlv}6tK%(>V1jYlM}u4L%> z^{*=f`S}m;SyNNMusIrR04)3!pyokLWnf6ni!VB{Lo9ZF{nJO_szWRQmM9P0mwWMV1rN$wH>?4 zZ!#2?$|}m+y>wfpxZ8Ec#!|!8X1O=@H~AKY!<0i+Gcb5VSsGB9k%5sB}6tK z%(>V1jYlM}u4L%>^{*=f`S}m;SyNNMusIrRz&5Cb3Q+T)T!z%V_@Wa##A4@1e&)*A zvn18R=J1t@ZSH6TdPvKrD2IhWeZ`n9UIS>#4K(oVtd!oJEgsy!HF@r9iuh z`>JNt>$|GDH*O!{e3?HNq7G(1$X=lROfdV+FT}Ch1_p+gIzQ-|>LZiNtrpRYz#T6>50jeybR@unR=PIiRr0&1$pTrU~XAvN@~7dW?o5rZhlE-ejd7L MQEFOdUMj?X01^q`G5`Po literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/live/windows/1/package.receipt.json b/wai/pack-conformance/live/windows/1/package.receipt.json new file mode 100644 index 000000000..7db9eb4ad --- /dev/null +++ b/wai/pack-conformance/live/windows/1/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"b31b0d7e1faab1b7331bf26f8400ca848692aba315e2e8083086d1523baac510","media_class":"video","stage_root":"c64c85665b4723be6efbd42ecd2c69e201a8d12c7eb0f2f3739186f5fdc368ed"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"live-window","parent_receipt_hash":"c8143d2452499a4d3772e40c528255eae90e781511995a76b1cabd12c87b5950","policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"03e9c415609e457da2a3d47298d91c541b36f03b634fb56060c9a4da1c2d8f13","sig":"fb568c45c8645e5213aed52462bbcdf1354f2b55fc0cd134ce563ab2b5721c2f5346111392ef46d2b193aacaa4553cae1a202e6149fcd1abdea9e743d0037a00","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":81674,"total_bytes":81674,"total_entries":4,"unique_objects":4,"version":2,"work_units":4} \ No newline at end of file diff --git a/wai/pack-conformance/live/windows/2/index-v.wsix b/wai/pack-conformance/live/windows/2/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..c8bf7b0ee083e3be4d0047fb2e13c8a092ee1f3f GIT binary patch literal 704 zcmWF!_KaX;U|^60Vh|0&%s|WrrKR7d%@n_Ry-$iib?M3(w*y}G+3x*ec1G&~|ED$6 zuhoTbmgcRKU$t?&vGk|>7KT$TZIf0n7Jc-B!=UYAkoBsg0xUptK!6KEF+|_Jdf9wo zu6emhhqZV0*FfvkZ#7X3CO_ZAcB!5@!Tt%V3{LexSvF7_=r%@>nILz9!2C_gZ1aC- z1+eN>?t1sGCPG9YLGWtz%Ol?`b7J?-WLjBO4N(Vk$A2IIv6&#k3}Gh^Pl^94F)Lyn zE5}rMIlZn!^G&X-`gZT5x9T}r*EMT@zc&M0wGFC%M-`L-rDp7o-n&Jv^Yf&RrSrCA zZVY-FEgsqYBvtXP_7={We*$Iy+d^Clvmd05m4T5FWWNH?Gji`1m`!@3b=X$_3y2l0URGr{b?z-Fyfpq%EpSzS2hVuNah#H5nMOB1AqjC06EJ4Hkd%Pp3nBxP{|{y`GL$hfl$5bC std::io::Result { Ok(json!({"pack": STAGED_PACK, "class": DELIVERY_CLASS, "label": "simulated", "cases": cases})) } +// ─────────────────────────────── live windows ─────────────────────────────── + +/// The pieces a window of the live corpus holds. +pub const LIVE_WINDOW_PIECES: u64 = 2; + +/// `live/`: the staged golden's source packed live (§9.2) with its key, in windows of +/// [`LIVE_WINDOW_PIECES`] pieces: each window's stage index and the receipt that seals it +/// (`windows//`), and `expected.json`: each window's first piece and entry, its pieces, +/// its index digest and its receipt hash. +pub fn live_windows(dir: &Path) -> std::io::Result<(Files, Value)> { + let g = dir.join("packs").join(STAGED_PACK); + let policy = std::fs::read(g.join("policy.toml"))?; + let key = read_key(&g.join("key.hex"))?; + let id = wai::signer_id::did_key(&key.verifying_key().to_bytes()); + let src = crate::fixture::generate(STAGED_PACK).ok_or_else(|| io("no fixture"))?; + let (hd, frames) = crate::source::y4m::read_all(&src).map_err(io)?; + let mut o = crate::live::LiveOpts::new(key, id); + o.window_pieces = LIVE_WINDOW_PIECES; + let mut lp = crate::live::LivePacker::new(&policy, hd.w, hd.h, o).map_err(io)?; + let comp = lp.policy().components[0].id.clone(); + let mut windows = Vec::new(); + for f in &frames { + lp.push(&f.rgb).map_err(io)?; + windows.extend(lp.take_sealed()); + } + windows.extend(lp.finish().map_err(io)?.1); + let mut files = BTreeMap::new(); + let mut out = Vec::new(); + for w in &windows { + let ix = StageIndex::parse(&w.index).map_err(io)?; + files.insert(format!("live/windows/{}/{}", w.number, wai::package::index_file(&comp)), w.index.clone()); + files.insert(format!("live/windows/{}/package.receipt.json", w.number), w.receipt.to_json().into_bytes()); + out.push(json!({ + "window": w.number, + "first_piece": w.first_piece, + "pieces": w.pieces, + "first_entry": ix.header.first_entry, + "index_digest": hex(&ix.digest()), + "receipt_hash": hex(&w.receipt.receipt_hash()), + })); + } + Ok((files, json!({"pack": STAGED_PACK, "component": comp, "window_pieces": LIVE_WINDOW_PIECES, "windows": out}))) +} + +/// The chain of held live windows, as §9.2 states it: each receipt verifies against its +/// index; the first window's parent is none and its `prev_index_digest` zero; each +/// later one's parent is the window before's receipt, its `prev_index_digest` that +/// window's index digest, and its first piece and entry the next of the chain's. +fn live_chain(held: &Files, expected: &Value, out: &mut Vec) { + let comp = expected["component"].as_str().unwrap_or(""); + let mut prev: Option<(PackageReceiptV2, StageIndex)> = None; + for w in expected["windows"].as_array().into_iter().flatten() { + let n = w["window"].as_u64().unwrap_or(u64::MAX); + let index = held.get(&format!("windows/{n}/{}", wai::package::index_file(comp))); + let receipt = held.get(&format!("windows/{n}/package.receipt.json")).and_then(|b| std::str::from_utf8(b).ok()).and_then(|t| PackageReceiptV2::from_json(t).ok()); + let (Some(index), Some(r)) = (index, receipt) else { + out.push(format!("live window {n}: its index or receipt is not held")); + return; + }; + let Ok(ix) = StageIndex::parse(index) else { + out.push(format!("live window {n}: its index does not parse")); + return; + }; + if let Err(e) = r.verify_detailed(&[index]) { + out.push(format!("live window {n}: its receipt does not verify: {e}")); + } + let ok = match &prev { + None => r.parent == Parent::None && ix.header.prev_index_digest == [0; 32] && ix.header.first_piece == 0 && ix.header.first_entry == 0, + Some((pr, pix)) => { + r.parent == Parent::LiveWindow(pr.receipt_hash()) + && ix.header.prev_index_digest == pix.digest() + && ix.header.first_piece == pix.header.first_piece + pix.pieces.len() as u64 + && ix.header.first_entry == pix.header.first_entry + pix.entries.len() as u64 + } + }; + if !ok { + out.push(format!("live window {n}: not chained to the window before")); + } + prev = Some((r, ix)); + } +} + // ─────────────────────────────── regen and check ─────────────────────────────── fn write_files(dir: &Path, files: &Files) -> std::io::Result<()> { @@ -1391,6 +1473,13 @@ pub fn regen(dir: &Path) -> std::io::Result<()> { } write_files(dir, &files)?; write_json(&st.join("expected.json"), &expected)?; + let (files, expected) = live_windows(dir)?; + let lv = dir.join("live"); + if lv.exists() { + std::fs::remove_dir_all(&lv)?; + } + write_files(dir, &files)?; + write_json(&lv.join("expected.json"), &expected)?; let dl = dir.join("delivery"); std::fs::create_dir_all(&dl)?; write_json(&dl.join("expected.json"), &delivery(dir)?)?; @@ -1497,6 +1586,16 @@ pub fn check(dir: &Path) -> std::io::Result> { out.push(format!("stills/{f}: not read as the corpus says")); } } + // The live windows (§9.2): the files, and their chain. + let mut held = read_tree(&dir.join("live"))?; + let expected_held: Value = serde_json::from_slice(&held.remove("expected.json").unwrap_or_default()).map_err(io)?; + let (files, want) = live_windows(dir)?; + let files: Files = files.into_iter().map(|(k, v)| (k.trim_start_matches("live/").to_string(), v)).collect(); + same_tree("live", &files, &held, &mut out); + if expected_held != want { + out.push("live/expected.json: the corpus and this implementation differ".into()); + } + live_chain(&held, &want, &mut out); // The delivery cases (§8.9). let held: Value = serde_json::from_slice(&std::fs::read(dir.join("delivery/expected.json"))?).map_err(io)?; if held != delivery(dir)? { -- 2.54.0 (Apple Git-157) From 510eaca0a2bdf0f6beffea5c49cbd53ae7c6c1b3 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 19:58:07 -0400 Subject: [PATCH 11/21] stage_diff: wide stills, so every deflate distance code is exercised stage_diff's stills were at most 48 columns wide, so the still writer's matches never reached past distance code 22. Every sixth still is now a picture 1024 to 10923 columns wide whose rows repeat, some with a few bytes changed. The match to the row above, one filtered row back, then uses distance codes 23 to 29. At 10923 columns that row is past the 32768-byte window, so the writer must fall back to nearer matches or literals. A run with nine or more wide stills fails if any of codes 23 to 29 goes unused. Co-Authored-By: Claude Opus 5.5 --- wai/pack-conformance/stage_diff.py | 50 ++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/wai/pack-conformance/stage_diff.py b/wai/pack-conformance/stage_diff.py index 8292c90bb..75f5386f5 100644 --- a/wai/pack-conformance/stage_diff.py +++ b/wai/pack-conformance/stage_diff.py @@ -8,8 +8,12 @@ pack_verify.py, and the two results must be equal: - `refine`: a refinement layer (§5.4) over random frames of 1×1 to 7×6, one to four of them, the stage below near the source or anywhere, at steps from 1 to 255: the WIR1 payload byte for byte and each reconstructed frame; -- `still`: a still (§5.7) of a random, flat, striped or repeating picture up to 48×40: - the PNG byte for byte, and its decode; +- `still`: a still (§5.7) of a random, flat, striped or repeating picture up to 48×40, + and every sixth one a picture 1024 to 10923 columns wide whose rows repeat (some with a + few bytes changed), so the row above is a match one filtered row back: every deflate + distance code from 23 to 29 is used, and at 10923 columns the row above is past the + 32768-byte window. The PNG byte for byte, and its decode. A run with nine or more wide + pictures must use distance codes 23 to 29; - `plan`: a random policy (bases, refinement chains, a still, classes with rates and floors, moments, rules with ladder subsets, maximum layers and deadlines, layer lags) over random pieces with random layer sizes: the policy's verdict, and per piece its @@ -25,6 +29,7 @@ pack_verify.py, and the two results must be equal: Exit status 0 only if every case agrees. """ import hashlib +import itertools import json import os import random @@ -57,7 +62,29 @@ def refine_py(c): return {"payload": payload.hex(), "recon": [hashlib.sha256(f).hexdigest() for f in recon]} +# Widths whose filtered row (3·w + 1 bytes) is a distance of codes 23, 24, 25, 26, 27, +# 28, 29 and 29 (RFC 1951 §3.2.5), and one past the 32768-byte window. +WIDE = [1024, 1366, 2048, 2731, 4096, 5462, 8192, 10922, 10923] +STILLS = itertools.count() +DISTANCE_CODES = set() + + +def wide_still_case(rng, w): + h = rng.randint(2, 3) + row = [rng.randrange(256) for _ in range(3 * w)] + rgb = [] + for _ in range(h): + r = list(row) + for _ in range(rng.choice([0, 0, 1, 5])): + r[rng.randrange(len(r))] = rng.randrange(256) + rgb += r + return {"op": "still", "w": w, "h": h, "rgb": rgb} + + def still_case(rng): + k = next(STILLS) + if k % 6 == 5: + return wide_still_case(rng, WIDE[(k // 6) % len(WIDE)]) w, h = rng.randint(1, 48), rng.randint(1, 40) kind = rng.randrange(4) if kind == 0: @@ -74,6 +101,17 @@ def still_case(rng): return {"op": "still", "w": w, "h": h, "rgb": rgb} +class CountedBits(PV.DeflateBits): + """pack_verify.py's bit writer, noting each distance code (the only 5-bit code).""" + def code(self, c, n): + if n == 5: + DISTANCE_CODES.add(c) + super().code(c, n) + + +PV.DeflateBits = CountedBits + + def still_py(c): png = PV.png_still(bytes(c["rgb"]), c["w"], c["h"]) _, _, back = PV.png_frame(png) @@ -251,7 +289,13 @@ def main(argv): fails += 1 if fails <= 5: print(f"FAIL {c['op']}: {json.dumps(c)[:300]}\n wai-pack: {json.dumps(rust)[:400]}\n pack_verify.py: {json.dumps(mine)[:400]}") - print(f"stage_diff: {len(cases) - fails}/{len(cases)} agree ({', '.join(f'{k} {v}' for k, v in sorted(seen.items()))})") + wide = sum(1 for c, _ in cases if c["op"] == "still" and c["w"] >= 1024) + missing = sorted(set(range(23, 30)) - DISTANCE_CODES) + if wide >= len(WIDE) and missing: + fails += 1 + print(f"FAIL {wide} wide stills, and distance codes {missing} never used") + print(f"stage_diff: {len(cases) - fails}/{len(cases)} agree ({', '.join(f'{k} {v}' for k, v in sorted(seen.items()))}; " + f"{wide} stills 1024 or more wide, distance codes {min(DISTANCE_CODES, default='-')} to {max(DISTANCE_CODES, default='-')} used)") return 0 if fails == 0 else 1 -- 2.54.0 (Apple Git-157) From 01730c68d36a6cf8de7b17a29d00bab4448bed41 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 20:04:33 -0400 Subject: [PATCH 12/21] staged-measure: the delivery simulator differentially, in CI sim_verify.py reproduced the committed simulator cases, but nothing ran the two simulators side by side on inputs outside the corpus. Those inputs include a refinement with its own delivery timeout, which the corpus has few of. sim_diff.py generates seeded random models and plans. A model has groups of a base and up to three layers, each layer one object or one per unit; a refinement either has its own timeout (zero, small, large or null) or takes the plan's. A plan varies: - live or on demand; - fifo or strict priority; - subgroup or datagram; - base and refinement timeouts; - a layer cap; - one or two workers; - the decode order; - what happens to late refinements. Each case runs through `wai_stage_measure sim` and through sim_verify.py. The two must agree on the refusal, or on the whole result, the stall bound and the longest stall. Where the bound applies, every stall must be within it. It started as a review probe; the hard-coded paths are removed. wai-conformance now runs it with two fixed seeds next to sim_verify.py. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-conformance.yml | 14 +++ wai/staged-measure-conformance/README.md | 6 + wai/staged-measure-conformance/sim_diff.py | 122 +++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 wai/staged-measure-conformance/sim_diff.py diff --git a/.github/workflows/wai-conformance.yml b/.github/workflows/wai-conformance.yml index 2cc9cfe29..5fbbf0e29 100644 --- a/.github/workflows/wai-conformance.yml +++ b/.github/workflows/wai-conformance.yml @@ -587,6 +587,20 @@ jobs: working-directory: wai shell: bash run: python3 staged-measure-conformance/sim_verify.py staged-measure-conformance + # The simulator differentially, outside the corpus: seeded random models (a refinement + # with its own delivery timeout among them) and plans, each simulated by + # wai_stage_measure sim and by sim_verify.py, the refusal or the whole result, the + # stall bound and the longest stall equal, and every stall within the bound where it + # applies. Two fixed seeds, so a run is reproducible. + - name: sim_diff.py, wai_stage_measure against sim_verify.py + working-directory: wai + shell: bash + run: | + set -euo pipefail + (cd wai-rs && cargo build --no-default-features --features stage_measure --bin wai_stage_measure) + for seed in 1 2; do + python3 staged-measure-conformance/sim_diff.py wai-rs/target/debug/wai_stage_measure "$seed" 1500 + done # chain_differential.py signs receipt chains around jwp-receipts §3 step 3 and §6.1 # (two keys, two tracks, both payloads, replays, forks, gaps, the guard and the # freshness bound), and chain_gap_differential.py genuine chains up to 90 groups diff --git a/wai/staged-measure-conformance/README.md b/wai/staged-measure-conformance/README.md index 474b75ca2..891e2a01f 100644 --- a/wai/staged-measure-conformance/README.md +++ b/wai/staged-measure-conformance/README.md @@ -115,6 +115,12 @@ steps (the curve of §6.3). `wai-rs`: Appendix B implemented from the text, with each fixture's model taken from `measure_verify.py`'s own evaluation. It reproduces every result, checks every claim, and requires a counter-case for each part of the bound a schedule can lose. +- `sim_diff.py [seed] [count]` runs the simulator differentially: + seeded random models (groups of a base and up to three layers, each layer one object + or one per unit, a refinement with its own delivery timeout or the plan's) under + random plans, each simulated by `wai_stage_measure sim` and by `sim_verify.py`. The + two must agree on the refusal, or on the whole result, the stall bound (§10.4) and the + longest stall; and where the bound applies, every stall must be within it. - `measure_verify.py .`: pure Python with the standard library, sharing no code with `wai-rs`. It reimplements, from the extension and SPEC: the byte rule and the formats it counts; each base's units from its case's stage 0; every layer applied in layer diff --git a/wai/staged-measure-conformance/sim_diff.py b/wai/staged-measure-conformance/sim_diff.py new file mode 100644 index 000000000..70cb51d35 --- /dev/null +++ b/wai/staged-measure-conformance/sim_diff.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +"""The delivery simulator, differentially: `wai_stage_measure sim` (wai-rs's +`stage_measure::sim`) against sim_verify.py, which shares no code with it, on seeded +random models and plans (extensions/staged-measurement.md §10, Appendix B). + +A model is one to five groups, each a base over one to four units and up to three +refinement layers, each layer one object over the group or one per unit. A refinement +object may carry its own delivery timeout (`timeout_us`: zero, small or large, or null) +or take the plan's. A plan is live or on demand, fifo or strict priority, by subgroup or +datagram, with or without a base and a refinement timeout, a layer cap, one or two +decode workers, base-first or fifo decoding, and late refinements discarded or +presented. For each case the two must agree on the refusal, or on the whole result, +the stall bound (§10.4) and the longest stall. Where the bound applies, every stall +must be within it. + + python3 sim_diff.py [seed] [count] + +Exit status 0 only if every case agrees and no stall exceeds its bound. +""" +import json +import os +import random +import subprocess +import sys +import tempfile + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import measure_verify as MV # noqa: E402 +import sim_verify as SV # noqa: E402 + + +def model(rng): + unit = rng.choice([1000, 20000, 40000]) + objs, u = [], 0 + for g in range(rng.randint(1, 5)): + n = rng.randint(1, 4) + cap = (u + n) * unit + rng.choice([0, 0, 5000]) + objs.append({"id": f"g{g}b", "base": g, "layer": 0, "units": [u, n], "bytes": rng.randint(1, 20000), "capture_us": cap}) + for k in range(1, rng.randint(1, 4)): + spans = [(u + i, 1) for i in range(n)] if rng.random() < 0.5 else [(u, n)] + for (f, c) in spans: + o = {"id": f"g{g}l{k}u{f}", "base": g, "layer": k, "units": [f, c], "bytes": rng.randint(1, 9000), "capture_us": cap} + r = rng.random() + if r < 0.6: + o["timeout_us"] = rng.choice([0, 1, 1000, 20000, 60000, 300000]) + elif r < 0.7: + o["timeout_us"] = None + objs.append(o) + u += n + return {"format": "wai-sim-model/1", "unit_us": unit, "objects": objs} + + +def plan(rng): + live = rng.random() < 0.5 + return {"plan": "wai-sim/1", "mode": "live" if live else "vod", + "link": {"rtt_us": rng.choice([0, 1000, 40000]), "bytes_per_ms": rng.choice([10, 150, 2500, 20000]), + "chunk_bytes": rng.choice([1200, 1500, 64000])}, + "sender": {"schedule": rng.choice(["fifo", "strict-priority"]), "carriage": rng.choice(["subgroup", "subgroup", "datagram"]), + "layer_cap": rng.choice([None, None, 1, 2]), + "delivery_timeout_us": {"base": rng.choice([None, None, None, 50000]), + "refinement": rng.choice([None, None, 30000, 100000])}, + "vod_window_us": None if live else rng.choice([None, 100000])}, + "sink": {"workers": rng.choice([1, 2]), "decode_order": rng.choice(["base-first", "base-first", "fifo"]), + "decode_us": {"base": rng.choice([1, 4000]), "refinement": rng.choice([1, 2000])}, + "playout_start_us": rng.choice([None, 215000]) if live else None, + "late_refinement": rng.choice(["discard", "present"])}} + + +def main(argv): + if len(argv) < 2: + print(__doc__) + return 2 + binary = argv[1] + seed = int(argv[2]) if len(argv) > 2 else 1 + count = int(argv[3]) if len(argv) > 3 else 1000 + rng = random.Random(seed) + agree = refused = bounded = 0 + fails = [] + with tempfile.TemporaryDirectory() as d: + mp, pp = os.path.join(d, "model.json"), os.path.join(d, "plan.json") + for i in range(count): + m, p = model(rng), plan(rng) + with open(mp, "w") as f: + json.dump(m, f) + with open(pp, "w") as f: + json.dump(p, f) + r = subprocess.run([binary, "sim", mp, pp], capture_output=True) + try: + res = SV.simulate(json.loads(json.dumps(m)), p) + py = {"stall_bound_us": SV.bound(m, p), "max_stall_us": SV.max_stall(res), "result": res} + except ValueError as e: + py = None + why = str(e) + if r.returncode != 0 or py is None: + if r.returncode != 0 and py is None: + refused += 1 + else: + fails.append(f"case {i}: wai_stage_measure {'refused' if r.returncode else 'simulated'}, " + f"sim_verify.py {'refused (' + why + ')' if py is None else 'simulated'}: " + f"{r.stderr.decode()[-200:]}") + continue + g = json.loads(r.stdout) + out = [] + MV.diff(py, {"stall_bound_us": g["stall_bound_us"], "max_stall_us": g["max_stall_us"], "result": g["result"]}, f"case {i}", out) + if out: + fails.append("; ".join(out[:3])) + continue + agree += 1 + if py["stall_bound_us"] is not None: + bounded += 1 + if py["max_stall_us"] is None or py["max_stall_us"] > py["stall_bound_us"]: + fails.append(f"case {i}: a stall of {py['max_stall_us']} past the bound {py['stall_bound_us']}") + for f in fails[:5]: + print(f"FAIL {f}") + print(f"sim_diff seed {seed}: {agree} agree, {refused} refused by both, {len(fails)} fail; " + f"the stall bound applied to {bounded}") + return 0 if not fails else 1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) -- 2.54.0 (Apple Git-157) From 35b80d6e628575744110c8a3ed61ef920d058571 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Thu, 8 Oct 2026 20:05:32 -0400 Subject: [PATCH 13/21] =?UTF-8?q?changelog:=20W4-4=20round=202=20=E2=80=94?= =?UTF-8?q?=20live=20writes=20as=20it=20goes,=20PNG=20rules,=20repeated=20?= =?UTF-8?q?objects=20over=20HTTP,=20one=20stall=20bound?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- wai/CHANGELOG.md | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index 667baebbb..00f234e89 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -31,7 +31,27 @@ wire format may change and such changes are recorded here. - **Live windows** (§9.2). `wai-pack live` packs a Y4M stream from standard input as it arrives, with the coder the on-demand packer uses, in windows sealed by version-2 receipts chained `live-window`; its bases and refinements are the on-demand package's, - byte for byte. `wai-pack listing --primary` lists what the two share. + byte for byte. `wai-pack listing --primary` lists what the two share. It writes as it + goes: each object when it is published and each window when it is sealed, every file + whole, and the plan file and `LISTING` at the end. It does not keep an object's bytes + once written. A source that fails mid-stream keeps every sealed window and the objects + it lists, removes the rest, and exits 1 saying what it kept. + `wai::pin::listing_from_sha256` writes a listing from per-file digests. +- **Stills read by PNG's rules** (§5.7). Both reference readers take any PNG of 8-bit RGB + without interlace and agree on what that admits. They read a suggested palette, + ancillary chunks and split image data. They refuse an interlace, bytes after `IEND` + or after the zlib stream, a short row, a zlib window past 32 KiB, and every other + chunk fault (`stills/`). A verifier refuses a piece longer than the policy's + `max_ms` before it decodes a frame. It hashes a still's frame held for its piece + without repeating the frame, so a declared duration never sets what it allocates. +- **A repeated object over HTTP** ([http-delivery](extensions/http-delivery.md) §4.2). + A repeated object contributes once, with its layers, and a layer joins every + occurrence of its base. `http_verify.py` and `wai-rs` now agree on a playlist's + codecs and floor where a piece repeats. `pack_verify.py` checks every golden playlist + with `http_verify.py`'s union, floor and adjacency checks. +- **One stall bound.** `stage_measure::sim::stall_bound_for` is the only bound: a + model's, which takes each refinement's own timeout. wai-web's simulator prints it, as + the native tool does. - **Delivery, simulated** (§8.9). `wai_pack::delivery` and `wai-pack sim` run staged-measurement's simulator over what a live packer publishes and what a class subscribes to: per-track delivery timeouts, §8.5's priorities, the subscriber's cap, @@ -42,9 +62,12 @@ wire format may change and such changes are recorded here. - **The corpus**: the golden `video-staged-01` (a moving shot, a bumper played twice, a still shot, a second moving shot; two refinement layers, a still, the event's deadline, three classes), its supplied floor and how it was written (`supplied_floor.py`), - `staged-mutations/`, and `delivery/`. `stage_diff.py` runs refinement layers, stills, - piece plans and deliveries differentially against `pack_verify.py`, which re-derives - every layer and still from the source byte for byte. `pack-refinement-unsupported` and + `staged-mutations/`, `delivery/`, `stills/` and `live/` (the golden packed live in + windows of two pieces, whose chain `pack_verify.py` checks). `stage_diff.py` runs + refinement layers, stills (wide ones among them, so every deflate distance code is + used), piece plans and deliveries differentially against `pack_verify.py`, which + re-derives every layer and still from the source byte for byte. `sim_diff.py` runs the + delivery simulator differentially against `sim_verify.py`. `pack-refinement-unsupported` and `pack-rule-ladder-unsupported` are gone. - The base golden `video-base-01` keeps its objects, index and receipt; its MoQ catalog and its `LISTING` change. -- 2.54.0 (Apple Git-157) From 07a34c7e3609a85a13eb853d25e0fec9914f3c9a Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 13:42:54 -0400 Subject: [PATCH 14/21] wai-pack: a still's reader inflates as zlib does; png_diff runs both readers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reader used miniz_oxide, which accepts a dynamic block whose lone distance code is longer than one bit. zlib's inflate, and so pack_verify.py, refuses that block. A golden resealed with such a still verified in wai-pack and was refused by pack_verify. Both readers now refuse incomplete code sets exactly as zlib does: the only incomplete literal/length or distance code allowed is one whose longest code is one bit. The reader now uses its own inflater (png::inflate_zlib), written from RFC 1950/1951 to zlib's inflate rules: - the header's check bits, method, window and preset-dictionary flag; - the stored-block complement; - the symbol counts; - a complete code-length code; - repeats with a length before them and within the code lengths; - a code for the end of the block; - over-subscribed and incomplete codes; - a code with no symbol, or a distance past the bytes written, refused where it is read; - the Adler-32, with nothing after it. miniz_oxide is kept only as a dev-dependency: the writer's round-trip test reads its stills back with an inflater independent of the reader's. packaging §5.7 now states these rules instead of "any PNG reader", and says that a still the verifier does not read is prefix-digest. stills/ gains the same dynamic block twice: a lone one-bit distance code (read) and a lone two-bit one (refused, still-incomplete-dist). png_diff.py drives wai-pack's reader (a new stage-json op, read-png) against pack_verify.py's. The inputs are: - zlib's own streams at every level, strategy and window; - the packager's own stills; - dynamic blocks built on each code-set rule (22 kinds, each checked against the verdict the rules give it); - mutations of all of these. wai-pack.yml runs it in debug and release. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-pack.yml | 15 +- wai/extensions/packaging.md | 15 +- wai/pack-conformance/README.md | 18 +- wai/pack-conformance/png_diff.py | 535 ++++++++++++++++++ wai/pack-conformance/stills/expected.json | 9 + .../stills/lone-one-bit-distance.png | Bin 0 -> 186 bytes .../stills/still-incomplete-dist.png | Bin 0 -> 186 bytes wai/wai-pack/Cargo.toml | 5 +- wai/wai-pack/src/conformance.rs | 9 + wai/wai-pack/src/golden.rs | 88 ++- wai/wai-pack/src/lib.rs | 8 + wai/wai-pack/src/png.rs | 269 ++++++++- 12 files changed, 948 insertions(+), 23 deletions(-) create mode 100644 wai/pack-conformance/png_diff.py create mode 100644 wai/pack-conformance/stills/lone-one-bit-distance.png create mode 100644 wai/pack-conformance/stills/still-incomplete-dist.png diff --git a/.github/workflows/wai-pack.yml b/.github/workflows/wai-pack.yml index 0d3ff8617..2ad39bf8f 100644 --- a/.github/workflows/wai-pack.yml +++ b/.github/workflows/wai-pack.yml @@ -11,8 +11,8 @@ name: wai-pack # from the extension text and sharing no code with wai-pack, the golden packages # verified and re-derived from their sources among it (every refinement layer and # still byte for byte, every delivery case through the staged-measurement simulator's -# Python twin); and the differentials: source readers, policies, scene cuts, and the -# staged packer (layers, stills, piece plans, deliveries). +# Python twin); and the differentials: source readers, policies, scene cuts, the +# staged packer (layers, stills, piece plans, deliveries), and a still's reader. # - media: the golden's floor, its CMAF initialization segment and fragments, read and # decoded by a media prober (test 14). # - licences: every crate in wai-pack's dependency graph is under a licence deny.toml @@ -222,6 +222,17 @@ jobs: set -euo pipefail python3 ../pack-conformance/stage_diff.py target/debug/wai-pack 1 1000 python3 ../pack-conformance/stage_diff.py target/release/wai-pack 2 3000 + # A still's reader differentially: the zlib library's own streams, dynamic blocks + # built on each of RFC 1951's and zlib's code-set rules, and their alterations, every + # verdict of wai-pack's reader equal to pack_verify.py's, which inflates with + # Python's zlib (debug and release). + - name: png_diff.py, wai-pack against pack_verify.py + shell: bash + working-directory: wai/wai-pack + run: | + set -euo pipefail + python3 ../pack-conformance/png_diff.py target/debug/wai-pack 1 2000 + python3 ../pack-conformance/png_diff.py target/release/wai-pack 2 4000 media: name: wai-pack · the floor through a media prober diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index d9e90baa8..3aa744084 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -414,7 +414,20 @@ pure function of the frame, so an independent implementation writes the same fil of its three bytes. The block ends with symbol 256, and the last byte is padded with zero bits. -A verifier decodes a still with any PNG reader of 8-bit RGB without interlace. +A verifier reads a still as a PNG of 8-bit RGB without interlace, by PNG's chunk rules: +every chunk's type four letters and its CRC checked; `IHDR` first; an optional `PLTE` +(a suggested palette) before the image data; the `IDAT`s consecutive; ancillary chunks +read past; any other critical chunk refused; `IEND` last, empty, and nothing after it. +It inflates the zlib stream as the zlib library's inflate does, so two verifiers agree +on every stream: the header's check bits, method 8, a window of at most 32 KiB and no +preset dictionary; a stored block's length and its complement agree; at most 286 +literal/length and 30 distance codes, a complete code-length code, a repeat with a +length before it and within the code lengths, and a code for the end of the block; no +Huffman code over-subscribed, and none incomplete but a literal/length or distance code +whose longest code is one bit (a lone one-bit code, or none); a code with no symbol, or +a distance past the bytes written, refused where it is read; the Adler-32, and nothing +after it. The rows are exactly `1 + 3·width` bytes each, under the five filters. A still +a verifier does not read is `prefix-digest` (§11.3). ## 6. Stage index diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index 6fc664508..1d59c378b 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -82,7 +82,9 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). image data split across two chunks. Refused: an interlace, bytes after `IEND`, a short row, a palette after the image data, an unknown critical chunk, no `IEND`, a chunk between the image data, a chunk's CRC, a sixth filter, another colour type, a zlib - window past 32 KiB, bytes after the zlib stream, and its Adler-32. + window past 32 KiB, bytes after the zlib stream, and its Adler-32. And one dynamic + block of literals twice: with a lone distance code of one bit, read (the one + incomplete code set the zlib library takes), and of two bits, refused. - `staged-mutations/` — the staged golden altered and re-sealed with its key, in the layout of `package-mutations/`: a refinement on another deadline than its plan's, a piece missing its top layer, a still where the policy's rule now gives the piece the @@ -197,6 +199,20 @@ Files are compared as bytes; `.gitattributes` turns off line-ending conversion. python3 pack-conformance/stage_diff.py 1 3000 +- `png_diff.py` runs a still's reader differentially: streams the zlib library writes + (every level and strategy, windows from 512 bytes to 32 KiB) and the packager's own + stills; dynamic blocks built on each of RFC 1951's and zlib's code-set rules (complete + codes, no distance code, a lone one-bit distance code used and misused, a lone longer + one, over-subscribed and incomplete codes of each kind, repeats with nothing before + them or past the code lengths, no end-of-block code, too many symbols, a distance past + the bytes written, stored blocks with a wrong complement, fixed blocks with symbols + they cannot use, several blocks of mixed kinds); and those altered. Each verdict, and + each read picture's SHA-256, from `wai-pack stage-json`'s `read-png` must be + `pack_verify.py`'s, which inflates with Python's zlib; and each construction must have + the verdict the rules give it. + + python3 pack-conformance/png_diff.py 1 4000 + - `supplied_floor.py` is how a golden's supplied floor was written (an AV1 encoder over each piece of the generated fixture, the policy's listing set to it); it is not a step of verification, since an encoder's output is pinned by the listing, not re-derived. diff --git a/wai/pack-conformance/png_diff.py b/wai/pack-conformance/png_diff.py new file mode 100644 index 000000000..06e951b3d --- /dev/null +++ b/wai/pack-conformance/png_diff.py @@ -0,0 +1,535 @@ +#!/usr/bin/env python3 +"""A still's reader, differentially: wai-pack's PNG reader against pack_verify.py's, which +reads the zlib stream with Python's zlib module (extensions/packaging.md §5.7). + +The inputs, seeded: + +- streams the zlib library writes, at every level and strategy and at windows from + 512 bytes to 32 KiB, and the reference packager's own stills; +- dynamic blocks built here, each code set chosen to sit on one of RFC 1951's and zlib's + rules: complete codes; no distance code at all; a lone distance code of one bit (the + one incomplete set zlib takes), used and misused; a lone distance code of 2 to 15 bits; + over-subscribed and incomplete literal/length, distance and code-length codes; a + repeat with no length before it, or past the code lengths; no code for the end of the + block; too many length or distance symbols; a distance past the bytes written; stored + blocks with a wrong complement; fixed blocks with the symbols they cannot use; and + several blocks of mixed kinds; +- and every one of those altered: bits flipped, cut short, bytes appended, the header's + bytes and the Adler-32 changed, chunks dropped, repeated, moved or added, the IHDR's + fields and a chunk's length changed. + +Each one's verdict and, read, its samples' SHA-256 must be the same from both readers, +and the constructions whose verdict the rules decide must have it. + + python3 png_diff.py [seed] [count] + +Exit status 0 only if every input agrees. +""" +import hashlib +import heapq +import json +import os +import random +import struct +import subprocess +import sys +import zlib + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import pack_verify as PV # noqa: E402 + +SIG = b"\x89PNG\r\n\x1a\n" +ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15] + + +def chunk(kind, data): + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data)) + + +def png_of(w, h, z, extra=()): + ihdr = struct.pack(">II", w, h) + bytes([8, 2, 0, 0, 0]) + return SIG + chunk(b"IHDR", ihdr) + b"".join(chunk(k, d) for k, d in extra) + chunk(b"IDAT", z) + chunk(b"IEND", b"") + + +def picture(rng): + """A picture's filtered rows: random, flat or striped samples, each row with a filter.""" + w, h = rng.randint(1, 40), rng.randint(1, 12) + kind = rng.randrange(3) + raw = bytearray() + for y in range(h): + raw.append(rng.randrange(5)) + for x in range(3 * w): + raw.append(rng.randrange(256) if kind == 0 else (7 if kind == 1 else (x // 3 * 11 + y) % 256)) + return w, h, bytes(raw) + + +class Bits: + def __init__(self): + self.out, self.acc, self.n = bytearray(), 0, 0 + + def put(self, v, n): + for i in range(n): + self.acc |= ((v >> i) & 1) << self.n + self.n += 1 + if self.n == 8: + self.out.append(self.acc) + self.acc, self.n = 0, 0 + + def code(self, c, n): + for i in reversed(range(n)): + self.put((c >> i) & 1, 1) + + def align(self): + if self.n: + self.out.append(self.acc) + self.acc, self.n = 0, 0 + + +def canonical(lengths): + """RFC 1951 §3.2.2's codes of `lengths` (an over-subscribed set gives codes anyway).""" + count = [0] * 16 + for x in lengths: + count[x] += 1 + count[0] = 0 + nxt, code = [0] * 16, 0 + for b in range(1, 16): + code = (code + count[b - 1]) << 1 + nxt[b] = code + out = [] + for x in lengths: + out.append(nxt[x] if x else 0) + if x: + nxt[x] += 1 + return out + + +def huffman(freq, n, limit): + """Code lengths for symbols 0..n-1 from their counts (a symbol of count 0 gets none): + Huffman's, or equal lengths where Huffman's pass `limit`. Complete for two or more + symbols; one symbol gets one bit.""" + used = [s for s in range(n) if freq.get(s)] + lengths = [0] * n + if len(used) == 1: + lengths[used[0]] = 1 + return lengths + heap = [(freq[s], i, [s]) for i, s in enumerate(used)] + heapq.heapify(heap) + k = len(heap) + while len(heap) > 1: + a, b = heapq.heappop(heap), heapq.heappop(heap) + for s in a[2] + b[2]: + lengths[s] += 1 + heapq.heappush(heap, (a[0] + b[0], k, a[2] + b[2])) + k += 1 + if max(lengths) > limit: + # Equal lengths, then the shortest codes split off until the set is complete. + bits = max(1, (len(used) - 1).bit_length()) + lengths = [bits if s in used else 0 for s in range(n)] + spare = (1 << bits) - len(used) + for s in used: + if spare == 0: + break + lengths[s] -= 1 + spare -= 1 + return lengths + + +LEN_BASE, LEN_EXTRA = PV.LEN_BASE, PV.LEN_EXTRA +DIST_BASE, DIST_EXTRA = PV.DIST_BASE, PV.DIST_EXTRA + + +def tokens(raw, dists, rng): + """`raw` as literals and matches, each match at a distance of a symbol in `dists`.""" + out, i = [], 0 + while i < len(raw): + best = None + if dists and rng.random() < 0.8: + for s in dists: + for d in {DIST_BASE[s], DIST_BASE[s] + (1 << DIST_EXTRA[s]) - 1}: + if d <= i: + m = 0 + while m < 258 and i + m < len(raw) and raw[i + m - d] == raw[i + m]: + m += 1 + if m >= 3 and (best is None or m > best[0]): + best = (m, d, s) + if best: + out.append(("m", best[0], best[1], best[2])) + i += best[0] + else: + out.append(("l", raw[i])) + i += 1 + return out + + +def length_symbol(m): + li = max(k for k, x in enumerate(LEN_BASE) if x <= m) + return 257 + li, m - LEN_BASE[li], LEN_EXTRA[li] + + +def write_tokens(b, toks, lit, lc, dist, dc, bad_dist_bit=False): + misused = False + for t in toks: + if t[0] == "l": + b.code(lc[t[1]], lit[t[1]]) + continue + _, m, d, s = t + sym, extra, nbits = length_symbol(m) + b.code(lc[sym], lit[sym]) + b.put(extra, nbits) + if bad_dist_bit and not misused: + b.put(1, 1) # the unassigned half of a lone one-bit code + misused = True + continue + b.code(dc[s], dist[s]) + b.put(d - DIST_BASE[s], DIST_EXTRA[s]) + b.code(lc[256], lit[256]) + + +def over(lengths): + """A complete set made over-subscribed: a code for a symbol that had none, or, with + every symbol coded, its longest code a bit shorter.""" + free = [x for x in range(len(lengths)) if not lengths[x]] + if free: + lengths[free[0]] = 1 + else: + s = max(range(len(lengths)), key=lambda x: lengths[x]) + lengths[s] -= 1 + + +def dynamic_block(b, toks, last, variant, rng): + """One dynamic block of `toks` with the code sets `variant` asks for.""" + freq = {256: 1} + dfreq = {} + for t in toks: + s = t[1] if t[0] == "l" else length_symbol(t[1])[0] + freq[s] = freq.get(s, 0) + 1 + if t[0] == "m": + dfreq[t[3]] = dfreq.get(t[3], 0) + 1 + if len(freq) == 1: + freq[0] = 1 + nlen = max(257, max(freq) + 1) + lit = huffman(freq, nlen, 15) + if variant == "dist-none": + ndist, dist = 1, [0] + elif dfreq: + ndist = max(dfreq) + 1 + dist = huffman(dfreq, ndist, 15) + else: + ndist, dist = 1, [0] + used_d = [s for s in range(ndist) if dist[s]] + if variant.startswith("dist-lone-") and len(used_d) == 1: + dist[used_d[0]] = 1 if variant in ("dist-lone-1", "dist-lone-1-misused") else rng.randint(2, 15) + elif variant == "dist-over": + dist = dist + [1, 1, 1] + ndist = len(dist) + elif variant == "dist-incomplete" and used_d: + # One code a bit longer: the set falls short of complete, its longest past one bit. + s = min(used_d, key=lambda x: dist[x]) + dist[s] += 1 + elif variant == "lit-incomplete": + s = min((x for x in range(nlen) if lit[x]), key=lambda x: lit[x]) + lit[s] += 1 + elif variant == "lit-over": + # A code for a symbol that had none, on a set that was complete. + over(lit) + elif variant == "no-eob": + lit[256] = 0 + ndist = min(ndist, 30) + dist = dist[:ndist] + lc, dc = canonical(lit), canonical(dist) + seq = lit + dist + # The code lengths, run-length coded at random (§3.2.7). + syms, i = [], 0 + if variant == "rep16-first": + syms.append((16, 0, 2)) + while i < len(seq): + x = seq[i] + run = 1 + while i + run < len(seq) and seq[i + run] == x: + run += 1 + if x == 0 and run >= 11 and rng.random() < 0.7: + n = min(run, 138) + syms.append((18, n - 11, 7)) + i += n + elif x == 0 and run >= 3 and rng.random() < 0.7: + n = min(run, 10) + syms.append((17, n - 3, 3)) + i += n + elif i > 0 and seq[i - 1] == x and run >= 3 and rng.random() < 0.7: + n = min(run, 6) + syms.append((16, n - 3, 2)) + i += n + else: + syms.append((x, 0, 0)) + i += 1 + if variant == "rep-past": + syms.append((17, 7, 3)) + cfreq = {} + for s, _, _ in syms: + cfreq[s] = cfreq.get(s, 0) + 1 + if len(cfreq) == 1: + cfreq[0 if 0 not in cfreq else 1] = 1 + cl = huffman(cfreq, 19, 7) + if variant == "cl-incomplete": + s = min((x for x in range(19) if 0 < cl[x] < 7), key=lambda x: cl[x]) + cl[s] += 1 + elif variant == "cl-over": + over(cl) + cc = canonical(cl) + ncode = max(4, max(k for k, s in enumerate(ORDER) if cl[s]) + 1) + b.put(last, 1) + b.put(2, 2) + hlit = nlen - 257 if variant != "hlit-big" else rng.choice([30, 31]) + hdist = ndist - 1 if variant != "hdist-big" else rng.choice([30, 31]) + b.put(hlit, 5) + b.put(hdist, 5) + b.put(ncode - 4, 4) + for s in ORDER[:ncode]: + b.put(cl[s], 3) + for s, extra, nbits in syms: + b.code(cc[s], cl[s]) + b.put(extra, nbits) + write_tokens(b, toks, lit, lc, dist, dc, bad_dist_bit=variant == "dist-lone-1-misused") + + +def fixed_block(b, toks, last, variant): + lit = [8] * 144 + [9] * 112 + [7] * 24 + [8] * 8 + lc = canonical(lit) + dist, dc = [5] * 32, canonical([5] * 32) + b.put(last, 1) + b.put(1, 2) + if variant == "fixed-lit-286": + b.code(lc[286], 8) + elif variant == "fixed-dist-30" and any(t[0] == "m" for t in toks): + k = next(i for i, t in enumerate(toks) if t[0] == "m") + toks = toks[:k] + [("m", toks[k][1], toks[k][2], 30)] + toks[k + 1:] + dist_base = DIST_BASE + [0, 0] + for t in toks: + if t[0] == "l": + b.code(lc[t[1]], 8 if t[1] < 144 else 9) + else: + sym, extra, nbits = length_symbol(t[1]) + b.code(lc[sym], lit[sym]) + b.put(extra, nbits) + b.code(dc[t[3]], 5) + if t[3] < 30: + b.put(t[2] - dist_base[t[3]], DIST_EXTRA[t[3]]) + b.code(lc[256], 7) + return + write_tokens(b, toks, lit, lc, dist, dc) + + +def stored_block(b, data, last, variant): + b.put(last, 1) + b.put(0, 2) + b.align() + n = len(data) + nn = (~n) & 0xFFFF if variant != "stored-complement" else n + b.out += struct.pack("= 2 else [0, len(raw)] + parts = [raw[:cut[0]], raw[cut[0]:cut[1]], raw[cut[1]:]] + for k, part in enumerate(parts): + last = int(k == 2) + kind = rng.randrange(3) + if kind == 0: + stored_block(b, part, last, "stored") + else: + # Matches only within this part, so a distance never reaches into another. + toks = tokens(part, list(range(4)), rng) + if kind == 1: + fixed_block(b, toks, last, "") + else: + dynamic_block(b, toks, last, "complete", rng) + else: + dists = [] + if variant not in ("dist-none",): + dists = [rng.randrange(4)] if variant.startswith("dist-lone-") else rng.sample(range(8), rng.randint(1, 4)) + toks = tokens(raw, dists, rng) + if variant.startswith("dist-lone-") or variant in ("dist-over", "dist-incomplete"): + if not any(t[0] == "m" for t in toks): + return None + if variant == "far": + toks = [("m", 3, 1, 0)] + toks + dynamic_block(b, toks, 1, "dist-lone-L" if variant == "dist-lone-L" else variant, rng) + b.align() + z = b"\x78\x01" + bytes(b.out) + struct.pack(">I", zlib.adler32(raw)) + return png_of(w, h, z) + + +def zlib_streams(rng): + out = [] + for _ in range(40): + w, h, raw = picture(rng) + co = zlib.compressobj(rng.choice([0, 1, 6, 9]), zlib.DEFLATED, rng.choice([9, 10, 12, 15]), 9, + rng.choice([zlib.Z_DEFAULT_STRATEGY, zlib.Z_FIXED, zlib.Z_HUFFMAN_ONLY, zlib.Z_RLE])) + out.append(png_of(w, h, co.compress(raw) + co.flush())) + rgb = bytes(rng.randrange(256) if rng.random() < 0.5 else 9 for _ in range(3 * w * h)) + out.append(PV.png_still(rgb, w, h)) + return out + + +def chunks_of(b): + at, out = 8, [] + while at + 12 <= len(b): + n = struct.unpack_from(">I", b, at)[0] + if at + 12 + n > len(b): + break + out.append((b[at + 4:at + 8], b[at + 8:at + 8 + n])) + at += 12 + n + return out + + +def build(chs): + return SIG + b"".join(chunk(k, d) for k, d in chs) + + +def mutate(b, rng): + if len(b) < 2: + return b + bytes([rng.randrange(256)]) + chs = chunks_of(b) + op = rng.randrange(12) + if op == 0 or not chs: + b = bytearray(b) + for _ in range(rng.randint(1, 3)): + i = rng.randrange(len(b)) + b[i] ^= 1 << rng.randrange(8) + return bytes(b) + if op == 1: + return b[:rng.randrange(len(b))] + if op == 2: + i, j = rng.randrange(len(chs)), rng.randrange(len(chs)) + chs[i], chs[j] = chs[j], chs[i] + return build(chs) + if op == 3: + chs.insert(rng.randrange(len(chs) + 1), chs[rng.randrange(len(chs))]) + return build(chs) + if op == 4: + del chs[rng.randrange(len(chs))] + return build(chs) + if op == 5: + kind = rng.choice([b"tEXt", b"PLTE", b"IDAT", b"IEND", b"IHDR", b"abCD", b"ABCD", b"gAMA"]) + data = bytes(rng.randrange(256) for _ in range(rng.choice([0, 1, 3, 6, 13, 768, 771]))) + chs.insert(rng.randrange(len(chs) + 1), (kind, data)) + return build(chs) + idats = [i for i, (k, _) in enumerate(chs) if k == b"IDAT"] + if op in (6, 7, 8, 9) and idats: + z = bytearray(b"".join(chs[i][1] for i in idats)) + if op == 6 and z: + for _ in range(rng.randint(1, 4)): + i = rng.randrange(len(z)) + z[i] ^= 1 << rng.randrange(8) + elif op == 7: + z = z[:rng.randrange(len(z) + 1)] + elif op == 8: + z += bytes(rng.randrange(256) for _ in range(rng.randint(1, 6))) + elif op == 9 and len(z) >= 2: + z[rng.randrange(2)] = rng.randrange(256) + if rng.random() < 0.7: + flg = z[1] & 0xE0 + z[1] = flg | (31 - (z[0] * 256 + flg) % 31) % 31 + z = bytes(z) + cut = rng.randrange(len(z) + 1) + rest = [c for i, c in enumerate(chs) if i not in idats] + pos = idats[0] + return build(rest[:pos] + [(b"IDAT", z[:cut]), (b"IDAT", z[cut:])] + rest[pos:]) + if op == 10: + for i, (k, d) in enumerate(chs): + if k == b"IHDR" and len(d) == 13: + d = bytearray(d) + d[rng.randrange(13)] = rng.choice([0, 1, 2, 3, 6, 8, 16, 255]) + chs[i] = (k, bytes(d)) + return build(chs) + return b + bytes(rng.randrange(256) for _ in range(rng.randint(1, 12))) + + +def py_verdict(b): + try: + w, h, px = PV.png_frame(b) + return {"verdict": "ok", "w": w, "h": h, "rgb_sha256": hashlib.sha256(px).hexdigest()} + except ValueError: + return {"verdict": "refused"} + + +def main(argv): + if len(argv) < 2: + print(__doc__) + return 2 + binary = argv[1] + seed = int(argv[2]) if len(argv) > 2 else 1 + count = int(argv[3]) if len(argv) > 3 else 2000 + rng = random.Random(seed) + cases = [] # (bytes, variant or None) + for f in sorted(os.listdir(os.path.join(HERE, "stills"))): + if f.endswith(".png"): + cases.append((open(os.path.join(HERE, "stills", f), "rb").read(), None)) + cases += [(b, None) for b in zlib_streams(rng)] + names = sorted(VARIANTS) + while len(cases) < count // 2: + v = names[len(cases) % len(names)] + b = construct(rng, v) + if b is not None: + cases.append((b, v)) + base = [b for b, _ in cases] + while len(cases) < count: + b = rng.choice(base) + for _ in range(rng.choice([1, 1, 2, 3])): + b = mutate(b, rng) + cases.append((b, None)) + lines = "".join(json.dumps({"op": "read-png", "hex": b.hex()}) + "\n" for b, _ in cases) + out = subprocess.run([binary, "stage-json"], input=lines.encode(), capture_output=True, check=True).stdout.decode().splitlines() + if len(out) != len(cases): + print(f"FAIL wai-pack answered {len(out)} of {len(cases)} lines") + return 1 + fails, seen = 0, {"ok": 0, "refused": 0} + by_variant = {} + for (b, v), got in zip(cases, out): + rust, mine = json.loads(got), py_verdict(b) + seen[mine["verdict"]] += 1 + if v is not None: + by_variant[v] = by_variant.get(v, 0) + 1 + why = None + if rust != mine: + why = f"wai-pack {json.dumps(rust)}, pack_verify.py {json.dumps(mine)}" + elif v is not None and VARIANTS[v] != mine["verdict"]: + why = f"the construction {v} should be {VARIANTS[v]}, both readers say {mine['verdict']}" + if why: + fails += 1 + if fails <= 5: + print(f"FAIL {v or 'input'} {b[:64].hex()}…: {why}") + missing = [v for v in names if v not in by_variant] + if missing: + fails += 1 + print(f"FAIL constructions never built: {missing}") + print(f"png_diff seed {seed}: {len(cases) - fails}/{len(cases)} agree ({seen['ok']} read, {seen['refused']} refused; " + f"{sum(by_variant.values())} constructions of {len(by_variant)} kinds)") + return 0 if fails == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/wai/pack-conformance/stills/expected.json b/wai/pack-conformance/stills/expected.json index e8129f558..1a84e6819 100644 --- a/wai/pack-conformance/stills/expected.json +++ b/wai/pack-conformance/stills/expected.json @@ -27,6 +27,15 @@ "verdict": "ok", "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" }, + { + "file": "lone-one-bit-distance.png", + "verdict": "ok", + "rgb_sha256": "dda77728ad8fa93b3f7ee3446053cba09376c63660e90b064b42a68e43850dfb" + }, + { + "file": "still-incomplete-dist.png", + "verdict": "refused" + }, { "file": "interlace.png", "verdict": "refused" diff --git a/wai/pack-conformance/stills/lone-one-bit-distance.png b/wai/pack-conformance/stills/lone-one-bit-distance.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc6b5ac41b10afd7128e947565c296e3d3e8813 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!2~3)gaoYzQjMN2jv*C{yayN^1Q-|^7ObKS z{9<1q)#b{pc&YE5=FXpQc5V std::io::Result> { /// - `refine` `{h, w, q, below, src}`: the WIR1 layer that brings each frame of `below` /// toward `src` (§5.4): `{"payload": hex, "recon": [SHA-256 of each frame]}`. /// - `still` `{w, h, rgb}`: the still (§5.7), `{"png": hex}`, and its decode's SHA-256. +/// - `read-png` `{hex}`: what a still's reader (§5.7) makes of the bytes: +/// `{"verdict": "ok", "w", "h", "rgb_sha256"}`, or `{"verdict": "refused"}`. /// - `plan` `{policy, pieces: [{start, duration, kind}], layer_bytes: [[…]]}`: the policy's /// verdict, and per piece its rules and moments, its ladder's rung ids, its deadline and /// each class's plan (§5.7, §8.1). @@ -562,6 +564,13 @@ fn stage_op(v: &Value) -> Result { let (_, _, back) = crate::png::decode(&png)?; Ok(json!({"png": hex(&png), "decoded_sha256": sha256_hex(&back)})) } + Some("read-png") => { + let b = crate::unhex(v["hex"].as_str().ok_or("hex: not a string")?).ok_or("hex: not hexadecimal")?; + Ok(match crate::png::decode(&b) { + Ok((w, h, rgb)) => json!({"verdict": "ok", "w": w, "h": h, "rgb_sha256": sha256_hex(&rgb)}), + Err(_) => json!({"verdict": "refused"}), + }) + } Some("plan") => { let text = v["policy"].as_str().ok_or("policy: not a string")?; let p = match Policy::parse(text) { diff --git a/wai/wai-pack/src/golden.rs b/wai/wai-pack/src/golden.rs index ebf2a75de..ebdc34350 100644 --- a/wai/wai-pack/src/golden.rs +++ b/wai/wai-pack/src/golden.rs @@ -1203,14 +1203,92 @@ pub fn v1_receipts() -> (Files, Value) { // ─────────────────────────────── stills ─────────────────────────────── +/// `raw` as a deflate stream of one final dynamic block holding only literals and the +/// end of the block, then the Adler-32 of `raw`. The literal/length code is complete +/// (symbols 0 to 253 of eight bits; 254, 255, 256 and 257 of nine), and the distance +/// code is one code of `dist_bits` bits, used by no symbol. The code-length code gives +/// lengths 1, 2, 8 and 9 two bits each. +fn dynamic_literals(raw: &[u8], dist_bits: u8) -> Vec { + struct W { + out: Vec, + acc: u32, + n: u32, + } + impl W { + fn put(&mut self, v: u32, n: u32) { + for i in 0..n { + self.acc |= ((v >> i) & 1) << self.n; + self.n += 1; + if self.n == 8 { + self.out.push(self.acc as u8); + self.acc = 0; + self.n = 0; + } + } + } + /// A Huffman code, most significant bit first. + fn code(&mut self, c: u32, n: u32) { + for i in (0..n).rev() { + self.put((c >> i) & 1, 1); + } + } + } + let mut lit = [8u8; 258]; + lit[254..].fill(9); + // Canonical codes (RFC 1951 §3.2.2). + let codes = |lengths: &[u8]| -> Vec { + let mut count = [0u32; 16]; + lengths.iter().for_each(|&l| count[l as usize] += 1); + count[0] = 0; + let mut next = [0u32; 16]; + let mut code = 0; + for b in 1..16 { + code = (code + count[b - 1]) << 1; + next[b] = code; + } + lengths.iter().map(|&l| if l == 0 { 0 } else { let c = next[l as usize]; next[l as usize] += 1; c }).collect() + }; + let lc = codes(&lit); + // The code-length code: symbols 1, 2, 8 and 9 of two bits; in RFC 1951's order the + // last of them (1) is the 18th. + const ORDER: [usize; 19] = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + let mut cl = [0u8; 19]; + for s in [1, 2, 8, 9] { + cl[s] = 2; + } + let cc = codes(&cl); + let mut w = W { out: Vec::new(), acc: 0, n: 0 }; + w.put(1, 1); + w.put(2, 2); + w.put(258 - 257, 5); + w.put(0, 5); + w.put(18 - 4, 4); + for &s in ORDER.iter().take(18) { + w.put(cl[s] as u32, 3); + } + for &l in lit.iter().chain([dist_bits].iter()) { + w.code(cc[l as usize], cl[l as usize] as u32); + } + for &b in raw { + w.code(lc[b as usize], lit[b as usize] as u32); + } + w.code(lc[256], lit[256] as u32); + if w.n > 0 { + w.out.push(w.acc as u8); + } + w.out.extend_from_slice(&crate::png::adler32(raw).to_be_bytes()); + w.out +} + /// `stills/`: PNG files a still's reader (§5.7, "any PNG of 8-bit RGB without interlace") /// reads or refuses, each with its verdict and, read, the SHA-256 of its RGB8 samples. /// They are the module's still of a 5 × 3 picture rebuilt chunk by chunk: with a /// suggested palette, ancillary chunks, the image data split in two; and with an /// interlace, bytes after IEND, a short row, a palette after the image data, an unknown /// critical chunk, no IEND, a chunk between the image data, a CRC, a sixth filter, -/// another colour type, a zlib window past 32 KiB, bytes after the zlib stream, and a -/// wrong Adler-32. +/// another colour type, a zlib window past 32 KiB, bytes after the zlib stream, a +/// wrong Adler-32, and a dynamic block whose lone distance code is two bits long (the +/// same block with a one-bit code is read). pub fn stills() -> (Files, Value) { use crate::png::{adler32, chunk, deflate_fixed}; let (w, h) = (5u32, 3u32); @@ -1244,6 +1322,10 @@ pub fn stills() -> (Files, Value) { let after = [&z[..], &z[z.len() - 4..]].concat(); let mut sum = z.clone(); *sum.last_mut().expect("adler") ^= 1; + // One dynamic block of literals: a lone distance code of one bit is the one incomplete + // code zlib takes; of two bits, it is refused (RFC 1951 §3.2.7, zlib's inflate). + let lone_one = [&[0x78u8, 0x01][..], &dynamic_literals(&rows(0, 0), 1)].concat(); + let lone_two = [&[0x78u8, 0x01][..], &dynamic_literals(&rows(0, 0), 2)].concat(); let mut bad_crc = png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]); bad_crc[8 + 8 + 13] ^= 1; // the first byte of IHDR's CRC let cases: Vec<(&str, Vec, bool)> = vec![ @@ -1252,6 +1334,8 @@ pub fn stills() -> (Files, Value) { ("ancillary", png(&[(b"IHDR", good_ihdr.clone()), (b"tEXt", text.clone()), (b"IDAT", z.clone()), (b"tEXt", text.clone()), (b"IEND", vec![])]), true), ("data-split", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z1.to_vec()), (b"IDAT", z2.to_vec()), (b"IEND", vec![])]), true), ("module-still", crate::png::encode(&rgb, w, h), true), + ("lone-one-bit-distance", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", lone_one), (b"IEND", vec![])]), true), + ("still-incomplete-dist", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", lone_two), (b"IEND", vec![])]), false), ("interlace", png(&[(b"IHDR", ihdr(w, h, 2, 1)), (b"IDAT", z.clone()), (b"IEND", vec![])]), false), ("bytes-after-end", [png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", z.clone()), (b"IEND", vec![])]), vec![0]].concat(), false), ("short-row", png(&[(b"IHDR", good_ihdr.clone()), (b"IDAT", zlib(&rows(0, 1))), (b"IEND", vec![])]), false), diff --git a/wai/wai-pack/src/lib.rs b/wai/wai-pack/src/lib.rs index 5556fc9aa..dfa81609d 100644 --- a/wai/wai-pack/src/lib.rs +++ b/wai/wai-pack/src/lib.rs @@ -46,6 +46,14 @@ pub fn hex(b: &[u8]) -> String { }) } +/// The bytes of an even-length hexadecimal string, either case; `None` otherwise. +pub fn unhex(s: &str) -> Option> { + if !s.len().is_multiple_of(2) || !s.bytes().all(|c| c.is_ascii_hexdigit()) { + return None; + } + (0..s.len()).step_by(2).map(|i| s.get(i..i + 2).and_then(|d| u8::from_str_radix(d, 16).ok())).collect() +} + /// SHA-256 of `b`, as lowercase hex. pub fn sha256_hex(b: &[u8]) -> String { use sha2::Digest as _; diff --git a/wai/wai-pack/src/png.rs b/wai/wai-pack/src/png.rs index 116596242..67ab20250 100644 --- a/wai/wai-pack/src/png.rs +++ b/wai/wai-pack/src/png.rs @@ -24,9 +24,11 @@ //! and filter method 0 and no interlace; an optional `PLTE` (a suggested palette, which an //! RGB picture may carry) before the image data, read past; the `IDAT`s consecutive; //! ancillary chunks read past; any other critical chunk refused; `IEND` last, empty, and -//! nothing after it. The zlib stream, its window 32 KiB at most, is inflated (any block -//! type) with its Adler-32 checked and no byte of the image data after it; its rows are -//! exactly `1 + 3·width` bytes each, and the five filters are undone. +//! nothing after it. The zlib stream is inflated by the module's own inflater, written +//! to the zlib library's rules ([`inflate_zlib`]: no over-subscribed code, and no +//! incomplete one but a lone one-bit literal/length or distance code), with its Adler-32 +//! checked and no byte of the image data after it; its rows are exactly `1 + 3·width` +//! bytes each, and the five filters are undone. //! //! [packaging]: ../../../extensions/packaging.md @@ -293,19 +295,9 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { let want = (stride + 1) * h as usize; // The zlib stream: a 32 KiB window at most (PNG's CINFO ≤ 7), its Adler-32 checked, // every byte of the image data in it, and exactly the rows the picture has. - if z.first().is_none_or(|c| c >> 4 > 7) { - return Err("the zlib stream: no header, or a window past 32 KiB".into()); - } - use miniz_oxide::inflate::core::{decompress, inflate_flags as fl, DecompressorOxide}; - use miniz_oxide::inflate::TINFLStatus; - let mut raw = vec![0u8; want + 1]; - let flags = fl::TINFL_FLAG_PARSE_ZLIB_HEADER | fl::TINFL_FLAG_COMPUTE_ADLER32 | fl::TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; - let (status, read, written) = decompress(&mut DecompressorOxide::new(), &z, &mut raw, 0, flags); - if status != TINFLStatus::Done { - return Err(format!("the zlib stream: {status:?}")); - } - if read != z.len() || written != want { - return Err("the rows are not the picture's size, or bytes follow the zlib stream".into()); + let raw = inflate_zlib(&z, want)?; + if raw.len() != want { + return Err("the rows are not the picture's size".into()); } let mut out = vec![0u8; stride * h as usize]; for y in 0..h as usize { @@ -329,6 +321,251 @@ pub fn decode(b: &[u8]) -> Result<(u32, u32, Vec), String> { Ok((w, h, out)) } +// ─────────────────────────────── inflate ─────────────────────────────── +// +// The reader's inflater, written from RFC 1950 and RFC 1951 with the rules of the zlib +// library's inflate (the one a PNG reader is built on, and the one the Python verifier +// calls), so the two accept the same streams: +// +// - the header: its check bits, method 8, a window of at most 32 KiB, no preset +// dictionary; the Adler-32 at the end, big-endian; nothing after it; +// - a stored block's length and its one's complement agree; +// - a dynamic block has at most 286 literal/length and 30 distance codes; its +// code-length code is complete; a repeat has a length before it and stays within +// the code lengths; the end-of-block symbol has a code; +// - the literal/length and distance codes are not over-subscribed, and are complete +// unless their longest code is one bit long (a single one-bit code, or none at all): +// the only incomplete code sets zlib takes; +// - a code with no symbol (in an incomplete set, or symbols 286 and 287 and distances +// 30 and 31 of the fixed codes) is refused where it is read, and so is a distance +// past the bytes written. + +struct BitReader<'a> { + b: &'a [u8], + at: usize, + bit: u32, +} + +impl BitReader<'_> { + fn bit(&mut self) -> Result { + let byte = *self.b.get(self.at).ok_or("the zlib stream ends early")?; + let v = (byte >> self.bit) & 1; + self.bit += 1; + if self.bit == 8 { + self.bit = 0; + self.at += 1; + } + Ok(v as u32) + } + + /// `n` bits, least significant first. + fn bits(&mut self, n: u32) -> Result { + let mut v = 0; + for i in 0..n { + v |= self.bit()? << i; + } + Ok(v) + } + + fn to_byte(&mut self) { + if self.bit != 0 { + self.bit = 0; + self.at += 1; + } + } +} + +/// A canonical Huffman code (RFC 1951 §3.2.2): how many codes of each length, and the +/// symbols in code order. +struct Code { + count: [u16; 16], + symbol: Vec, +} + +/// The code of `lengths`, and how far from complete it is: 0 complete, above 0 +/// incomplete, below 0 over-subscribed; and its longest length. +fn code_of(lengths: &[u8]) -> (Code, i32, usize) { + let mut count = [0u16; 16]; + for &l in lengths { + count[l as usize] += 1; + } + let mut left: i32 = 1; + let mut longest = 0; + for (len, &c) in count.iter().enumerate().skip(1) { + left = (left << 1) - c as i32; + if c > 0 { + longest = len; + } + if left < 0 { + break; + } + } + let mut symbol = Vec::with_capacity(lengths.len()); + for len in 1..16 { + for (s, &l) in lengths.iter().enumerate() { + if l as usize == len { + symbol.push(s as u16); + } + } + } + count[0] = 0; + (Code { count, symbol }, left, longest) +} + +fn decode_symbol(r: &mut BitReader, c: &Code) -> Result { + let (mut code, mut first, mut index) = (0i32, 0i32, 0i32); + for len in 1..16 { + code |= r.bit()? as i32; + let n = c.count[len] as i32; + if code - n < first { + return Ok(c.symbol[(index + code - first) as usize]); + } + index += n; + first = (first + n) << 1; + code <<= 1; + } + Err("the zlib stream: a code with no symbol".into()) +} + +/// The literal/length and distance codes of a dynamic block, under zlib's rules. +fn dynamic_codes(r: &mut BitReader) -> Result<(Code, Code), String> { + const ORDER: [usize; 19] = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + let nlen = r.bits(5)? as usize + 257; + let ndist = r.bits(5)? as usize + 1; + let ncode = r.bits(4)? as usize + 4; + if nlen > 286 || ndist > 30 { + return Err("the zlib stream: too many length or distance symbols".into()); + } + let mut cl = [0u8; 19]; + for &i in ORDER.iter().take(ncode) { + cl[i] = r.bits(3)? as u8; + } + let (clc, left, _) = code_of(&cl); + if left != 0 { + return Err("the zlib stream: a code-length code that is not complete".into()); + } + let mut lengths = vec![0u8; nlen + ndist]; + let mut have = 0; + while have < nlen + ndist { + let s = decode_symbol(r, &clc)?; + let (value, times) = match s { + 0..=15 => (s as u8, 1), + 16 => { + let prev = *lengths[..have].last().ok_or("the zlib stream: a repeat with no length before it")?; + (prev, 3 + r.bits(2)? as usize) + } + 17 => (0, 3 + r.bits(3)? as usize), + _ => (0, 11 + r.bits(7)? as usize), + }; + if have + times > nlen + ndist { + return Err("the zlib stream: a repeat past the code lengths".into()); + } + lengths[have..have + times].fill(value); + have += times; + } + if lengths[256] == 0 { + return Err("the zlib stream: no code for the end of the block".into()); + } + let (lit, left, longest) = code_of(&lengths[..nlen]); + if left < 0 || (left > 0 && longest > 1) { + return Err("the zlib stream: an over-subscribed or incomplete literal/length code".into()); + } + let (dist, left, longest) = code_of(&lengths[nlen..]); + if left < 0 || (left > 0 && longest > 1) { + return Err("the zlib stream: an over-subscribed or incomplete distance code".into()); + } + Ok((lit, dist)) +} + +/// The zlib stream `z` inflated, refused as zlib's inflate refuses it, and refused +/// when it would give more than `limit` bytes or when bytes follow its Adler-32. +pub fn inflate_zlib(z: &[u8], limit: usize) -> Result, String> { + let (cmf, flg) = match z { + [c, f, ..] => (*c as u32, *f as u32), + _ => return Err("the zlib stream: no header".into()), + }; + if (cmf * 256 + flg) % 31 != 0 || cmf & 15 != 8 || cmf >> 4 > 7 || flg & 0x20 != 0 { + return Err("the zlib stream: its header (check bits, method 8, a window of at most 32 KiB, no dictionary)".into()); + } + let mut r = BitReader { b: z, at: 2, bit: 0 }; + let mut out: Vec = Vec::new(); + let too_long = || "the zlib stream gives more than the picture's rows".to_string(); + loop { + let last = r.bit()?; + match r.bits(2)? { + 0 => { + r.to_byte(); + let n = z.get(r.at..r.at + 4).ok_or("the zlib stream ends early")?; + let (len, nlen) = (u16::from_le_bytes([n[0], n[1]]), u16::from_le_bytes([n[2], n[3]])); + if len != !nlen { + return Err("the zlib stream: a stored block's length and its complement disagree".into()); + } + let data = z.get(r.at + 4..r.at + 4 + len as usize).ok_or("the zlib stream ends early")?; + if out.len() + data.len() > limit { + return Err(too_long()); + } + out.extend_from_slice(data); + r.at += 4 + len as usize; + } + t @ (1 | 2) => { + let (lit, dist) = if t == 1 { + let mut l = [8u8; 288]; + l[144..256].fill(9); + l[256..280].fill(7); + (code_of(&l).0, code_of(&[5u8; 32]).0) + } else { + dynamic_codes(&mut r)? + }; + loop { + let s = decode_symbol(&mut r, &lit)? as usize; + if s < 256 { + if out.len() == limit { + return Err(too_long()); + } + out.push(s as u8); + continue; + } + if s == 256 { + break; + } + let li = s - 257; + if li >= 29 { + return Err("the zlib stream: a length symbol past 285".into()); + } + let len = (LEN_BASE[li] + r.bits(LEN_EXTRA[li])?) as usize; + let di = decode_symbol(&mut r, &dist)? as usize; + if di >= 30 { + return Err("the zlib stream: a distance symbol past 29".into()); + } + let d = (DIST_BASE[di] + r.bits(DIST_EXTRA[di])?) as usize; + if d > out.len() { + return Err("the zlib stream: a distance past the bytes written".into()); + } + if out.len() + len > limit { + return Err(too_long()); + } + for _ in 0..len { + out.push(out[out.len() - d]); + } + } + } + _ => return Err("the zlib stream: block type 3".into()), + } + if last == 1 { + break; + } + } + r.to_byte(); + let sum = z.get(r.at..r.at + 4).ok_or("the zlib stream ends before its Adler-32")?; + if u32::from_be_bytes(sum.try_into().expect("4")) != adler32(&out) { + return Err("the zlib stream: its Adler-32".into()); + } + if r.at + 4 != z.len() { + return Err("bytes follow the zlib stream".into()); + } + Ok(out) +} + #[cfg(test)] mod tests { use super::*; -- 2.54.0 (Apple Git-157) From e3375530f6ecea55f149496e7d9d76bcae89434c Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 14:13:41 -0400 Subject: [PATCH 15/21] =?UTF-8?q?packaging=20=C2=A74.4:=20a=20piece=20is?= =?UTF-8?q?=20at=20most=20ten=20minutes=20and=202^24=20units;=20floors=20c?= =?UTF-8?q?ounted=20before=20decoding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new max_ms check bounded nothing, because a policy could set max_ms up to 2^53 − 1. A still's piece could then state 2^32 − 1 frames. Rust hashed once per stated frame (about 29 minutes), and Python built a list of n references (about 34 GB). - **§3.4 item 5 and §4.4.** max_ms is at most 600000, and at most 2^24 units on every component's timebase. Both are policy-piece-bounds, in both readers. §4.4 gives the reason: a verifier takes a piece whole, so the longest piece bounds its work. Ten minutes is far past any piece a delivery cuts, and 2^24 units bounds the work on any timebase, which may have up to 2^32 − 1 units a second. - **§11.3, before any frame is decoded.** Each piece's floor object, where its framing reads, must hold one unit per frame of its piece (floor-object-mismatch). Both verifiers make this check first. - **Held stills.** Python holds a still's frame and its count (Held) and hashes the digest a block at a time. Rust's held_digest also feeds a block at a time. - **Corpus.** - refusals: max_ms 600001; and the default max_ms on a timebase of 2^32 − 1 units a second. - policy/max-piece.toml: accepted at both bounds. - staged-mutations max-ms-past-cap (policy-piece-bounds). - staged-mutations still-at-cap: the still given the longest piece the policy allows, its manifest naming the frames, refused by the floor count in both verifiers. - **policy_diff.** Its random policies now reach both bounds. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/packaging.md | 15 ++- wai/pack-conformance/README.md | 8 +- wai/pack-conformance/pack_verify.py | 51 ++++++++-- wai/pack-conformance/policy/expected.json | 5 + wai/pack-conformance/policy/max-piece.toml | 19 ++++ wai/pack-conformance/policy_diff.py | 4 +- wai/pack-conformance/refusals/expected.json | 8 ++ .../refusals/policy/piece-bounds-max-ms.toml | 18 ++++ .../refusals/policy/piece-bounds-units.toml | 17 ++++ .../staged-mutations/expected.json | 18 ++++ .../staged-mutations/max-ms-past-cap/LISTING | 41 ++++++++ .../max-ms-past-cap/policy.toml | 94 ++++++++++++++++++ .../staged-mutations/still-at-cap/LISTING | 35 +++++++ .../still-at-cap/dash/classic.mpd | 20 ++++ .../still-at-cap/dash/mobile.mpd | 29 ++++++ .../staged-mutations/still-at-cap/dash/tv.mpd | 39 ++++++++ .../still-at-cap/hls/classic/floor-v.m3u8 | 14 +++ .../still-at-cap/hls/classic/master.m3u8 | 5 + .../still-at-cap/hls/legacy/master.m3u8 | 5 + .../still-at-cap/hls/mobile/master.m3u8 | 6 ++ .../still-at-cap/hls/mobile/wai-v.m3u8 | 15 +++ .../still-at-cap/hls/mobile/wai.m3u8 | 7 ++ .../still-at-cap/hls/tv/master.m3u8 | 6 ++ .../still-at-cap/hls/tv/wai-v.m3u8 | 17 ++++ .../still-at-cap/hls/tv/wai.m3u8 | 7 ++ .../still-at-cap/index-v.wsix | Bin 0 -> 1704 bytes .../still-at-cap/moq/catalog.json | 1 + ...afc12f658a8a6427ee1261f05204e8b4126adb.wai | Bin 0 -> 977 bytes .../still-at-cap/package.receipt.json | 1 + .../staged-mutations/still-at-cap/plan-v.txt | 3 + .../staged-mutations/still-at-cap/policy.json | 1 + .../staged-mutations/still-at-cap/policy.toml | 94 ++++++++++++++++++ .../still-at-cap/stages/legacy.json | 1 + .../still-at-cap/stages/mobile.json | 1 + .../still-at-cap/stages/tv.json | 1 + wai/wai-pack/src/golden.rs | 41 ++++++++ wai/wai-pack/src/pack.rs | 10 +- wai/wai-pack/src/policy.rs | 13 ++- wai/wai-pack/src/verify.rs | 23 +++++ 39 files changed, 676 insertions(+), 17 deletions(-) create mode 100644 wai/pack-conformance/policy/max-piece.toml create mode 100644 wai/pack-conformance/refusals/policy/piece-bounds-max-ms.toml create mode 100644 wai/pack-conformance/refusals/policy/piece-bounds-units.toml create mode 100644 wai/pack-conformance/staged-mutations/max-ms-past-cap/LISTING create mode 100644 wai/pack-conformance/staged-mutations/max-ms-past-cap/policy.toml create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/dash/classic.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/dash/mobile.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/floor-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/legacy/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/master.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/moq/catalog.json create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/plan-v.txt create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/policy.json create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/policy.toml create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/stages/legacy.json create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/still-at-cap/stages/tv.json diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index 3aa744084..a6321d9bd 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -149,7 +149,8 @@ first that fails is the refusal: a reference never names two things (`policy-duplicate-id`). 3. `lead` names a component (`policy-lead`). 4. `segmentation.closed` is true: every piece is closed, §4.1 (`policy-open-gop`). -5. `1 ≤ min_ms ≤ target_ms ≤ max_ms` (`policy-piece-bounds`). +5. `1 ≤ min_ms ≤ target_ms ≤ max_ms ≤ 600000`, and `max_ms` is at most `2^24` units + on every component's timebase (§4.4) (`policy-piece-bounds`). 6. Every rung names a component (`policy-ladder-component`). 7. A rung carries `refines` exactly when its role is `refinement` (`policy-refines-role`); `refines` names a rung (`policy-dangling-refines`); no @@ -246,6 +247,15 @@ A duration in milliseconds converts to timebase units as `floor(ms · num / (1000 · den))`; a result of 0 becomes 1, and one above `2^64 − 1` becomes `2^64 − 1`. +A piece is at most ten minutes long (`max_ms` at most 600000) and at most `2^24` units +on its component's timebase (§3.4 item 5). A verifier takes a piece whole: it decodes +every frame of a base, holds a still's one frame for every frame of its piece and hashes +each (§5.7, §6.5), and reads one floor unit per frame (§7.2). So the longest piece a +policy allows is the most work a verifier does for one piece, and a package states it. +Ten minutes is far past any piece a delivery cuts (the default `max_ms` is four +seconds); `2^24` units bounds that work on any timebase, since a timebase may have up to +`2^32 − 1` units a second. + **4.5** *Components.* Every component of a package shares one presentation timeline that starts at 0. The policy names one **lead** component; every other component cuts its pieces at the lead's boundary instants, each converted to its own timebase as @@ -1083,6 +1093,9 @@ failed check names is not applied: (`plan-mismatch`); - every entry's object is present (`object-missing`) and keeps the object rules of §6.3, its counted bytes included; + - before any frame is decoded, each piece's floor object, where the policy has a floor + rung and the object's payload reads as §7.2's framing, holds one unit per frame of + its piece (`floor-object-mismatch`); - every decode-equivalent prefix decodes to its `prefix_digest` (`prefix-digest`): each piece's base by its capability's decode (a still held for its piece, §5.7), and each refinement applied in layer order by a staged-delivery §4 sink under the diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index 1d59c378b..e173a1b6b 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -13,7 +13,8 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). override changes it, §3.2), and the pairs `eq--a.toml` and `eq--b.toml`, two spellings of one policy that must have one digest: every default omitted or written out; standard or inline - tables, dotted keys, member order, string escapes, integer notation and comments. + tables, dotted keys, member order, string escapes, integer notation and comments; + and `max-piece.toml`, the longest piece a policy may allow (§4.4), at both bounds. - `refusals/` — `policy/*.toml`, one fault each (the byte layer's among them: a byte-order mark, bytes that are not UTF-8, an integer at 2^63, nesting to depth 33 and 80), and `source/*` (Y4M and WAV), and `expected.json`: each one's reason (§12). Every @@ -92,7 +93,10 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). and a still's digest of its one frame rather than its frame held for the piece (each `prefix-digest`), and a piece and a still each longer than the policy's `max_ms` (`index-policy-mismatch`, refused before a frame is decoded, so a still's declared - duration never sets what a verifier allocates). + duration never sets what a verifier allocates); a policy past §4.4's longest piece + (`max_ms` 600001, `policy-piece-bounds`); and the still given the longest piece a + policy allows (`max_ms` ten minutes), its manifest naming those frames, refused by its + floor's unit count before any frame is decoded (`floor-object-mismatch`). - `delivery/` — `expected.json`: the staged golden packed live and delivered to a `tv` subscriber over a simulated link (§8.9), under §8.5's priorities, without them, and capped at layer 1: each piece's presented layer, base arrival and dropped layers, and diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index c374b3573..3779b5218 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -62,6 +62,7 @@ from their specifications, reads the capability registry from its published expo Exit status 0 only if every vector verifies. Python 3.11 or later (tomllib). """ import hashlib +import itertools import json import os import re @@ -476,6 +477,10 @@ def constrain(P, lag): no("policy-open-gop") if not (1 <= s["min_ms"] <= s["target_ms"] <= s["max_ms"]): no("policy-piece-bounds") + # §4.4: at most ten minutes, and at most 2^24 units on every component's timebase. + if s["max_ms"] > 600000 or any(units_from_ms(s["max_ms"], c["timebase"]["num"], c["timebase"]["den"]) > 1 << 24 + for c in P["component"]): + no("policy-piece-bounds") comps = {c["id"] for c in P["component"]} if any(r["component"] not in comps for r in P["ladder"]): no("policy-ladder-component") @@ -2823,14 +2828,33 @@ def seg_params(P, tb): "window": s["cut_window"], "lookahead": s["lookahead_frames"]} +class Held: + """A still's one frame held for `n` frames (§5.7): its length is `n` and it iterates + the frame `n` times, holding neither `n` copies nor `n` references.""" + def __init__(self, frame, n): + self.frame, self.n = frame, n + + def __len__(self): + return self.n + + def __iter__(self): + return itertools.repeat(self.frame, self.n) + + def prefix_digest_of(frames): """SPEC §7's clip digest, each frame's SHA-256 fed in turn: a still's frame, held, is - hashed once and its digest fed for every frame (§5.7).""" - h, last, d = hashlib.sha256(), None, None + hashed once and its digest fed for every frame, a block of them at a time (§5.7).""" + h = hashlib.sha256() + if isinstance(frames, Held): + d = hashlib.sha256(frames.frame).digest() + block, left = d * 1024, frames.n + while left >= 1024: + h.update(block) + left -= 1024 + h.update(d * left) + return h.digest() for f in frames: - if f is not last: - last, d = f, hashlib.sha256(f).digest() - h.update(d) + h.update(hashlib.sha256(f).digest()) return h.digest() @@ -3125,6 +3149,17 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): rep.check(False, f"{t}: entry {i}: the object rules (§6.3) ({x.code})") if len(mans) != len(E): return named | unreached(files, c['id']) + # Before any frame is decoded (§11.3): each piece's floor object, where the policy has a + # floor rung and the payload reads as §7.2's framing, holds one unit per frame. + if any(r["component"] == c["id"] and r["role"] == "floor" for r in P["ladder"]): + own = owners(ix) + for k, pc in enumerate(pieces): + fl = [i for i in range(len(E)) if own[i] == k and E[i]["role"] == FLOOR] + fp = floor_payload(objs[fl[0]][12 + E[fl[0]]["man_len"]:]) if len(fl) == 1 else None + if fp is not None and len(fp[5]) != pc["duration"]: + rep.check(False, f"{t}: piece {k}: {len(fp[5])} floor units for {pc['duration']} frames, before any frame " + f"is decoded (floor-object-mismatch)") + return named | unreached(files, c['id']) size = None own = owners(ix) stage = {} # entry index → the frames of the prefix it completes, decoded @@ -3136,10 +3171,10 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): if e["role"] == BASE: try: if e["cap"] == "wai.image.png": - # §5.7: a still, its one frame held for the piece (n is within max_ms, - # which the index was held to, and the list holds n references). + # §5.7: a still, its one frame held for the piece: the frame and its + # count, not a list of n frames. pw, ph, rgb = png_frame(payload) - wv, frames = None, [rgb] * n + wv, frames = None, Held(rgb, n) else: wv = IPV.parse_wiv1(payload, 0) frames, pw, ph = IPV.decode_wiv1_k0(wv), wv["w"], wv["h"] diff --git a/wai/pack-conformance/policy/expected.json b/wai/pack-conformance/policy/expected.json index bcf864418..fac9bf491 100644 --- a/wai/pack-conformance/policy/expected.json +++ b/wai/pack-conformance/policy/expected.json @@ -25,6 +25,11 @@ "canonical": "{\"asset_id\":\"pack-fixture-01\",\"class\":[{\"floor_only\":false,\"id\":\"mobile\",\"max_kbps\":900,\"max_layer\":1},{\"floor_only\":false,\"id\":\"tv\",\"max_layer\":2},{\"floor_only\":true,\"id\":\"legacy\",\"max_layer\":0}],\"classify\":{\"motion_high_milli\":9000,\"static_mad_milli\":300,\"static_permille\":5,\"synthetic_bins\":12},\"component\":[{\"id\":\"v\",\"media\":\"video\",\"source\":\"src/video.y4m\",\"timebase\":{\"den\":1,\"num\":25}},{\"id\":\"a\",\"media\":\"audio\",\"source\":\"src/audio.wav\",\"timebase\":{\"den\":1,\"num\":48000}}],\"defaults\":{\"deadline_ms\":1500},\"floor\":{\"extra_keyframes\":false,\"supplied_listing\":\"0000000000000000000000000000000000000000000000000000000000000000\"},\"ladder\":[{\"capability\":\"wai.video.int_motion\",\"component\":\"v\",\"id\":\"b0\",\"params\":{\"block\":16,\"max_sse_px_milli\":40000,\"mv_precision\":2,\"q_candidates\":[16,12,8],\"search\":7},\"role\":\"base\"},{\"capability\":\"wai.video.int_refine\",\"component\":\"v\",\"id\":\"r1\",\"params\":{\"q\":6},\"refines\":\"b0\",\"role\":\"refinement\"},{\"capability\":\"wai.video.int_refine\",\"component\":\"v\",\"id\":\"r2\",\"params\":{\"q\":3},\"refines\":\"r1\",\"role\":\"refinement\"},{\"capability\":\"wai.image.png\",\"component\":\"v\",\"id\":\"still\",\"params\":{},\"role\":\"base\"},{\"capability\":\"wai.video.av1\",\"component\":\"v\",\"id\":\"f0\",\"params\":{\"source\":\"supplied\"},\"role\":\"floor\"},{\"capability\":\"wai.audio.flac\",\"component\":\"a\",\"id\":\"ab\",\"params\":{\"source\":\"encode\"},\"role\":\"base\"}],\"lead\":\"v\",\"live\":{\"layer_lag_ms\":[0,200,400]},\"moment\":[{\"from_ms\":30000,\"id\":\"m-goal\",\"tags\":[\"event\"],\"to_ms\":34000}],\"royalty_free_only\":true,\"rule\":[{\"id\":\"static\",\"then\":{\"ladder\":[\"still\",\"f0\"]},\"when\":{\"piece_kind\":[\"static\"]}},{\"id\":\"motion\",\"then\":{\"max_layer\":1},\"when\":{\"piece_kind\":[\"motion_high\"]}},{\"id\":\"event\",\"then\":{\"deadline_ms\":800,\"max_layer\":2},\"when\":{\"moment_tag\":[\"event\"]}}],\"schema\":\"wai.pack.policy/1\",\"segmentation\":{\"closed\":true,\"cut_abs_permille\":300,\"cut_rel_permille\":3000,\"cut_window\":8,\"lookahead_frames\":6,\"max_ms\":4000,\"min_ms\":1000,\"scene_cut\":\"wai.pack.scenecut/1\",\"target_ms\":2000}}", "digest": "1997e19e06f6bf191d5de605ba1a4719a3372d5e7a168b5364cc82638d9dd0e9" }, + { + "file": "max-piece.toml", + "canonical": "{\"asset_id\":\"max-piece\",\"class\":[],\"classify\":{\"motion_high_milli\":9000,\"static_mad_milli\":300,\"static_permille\":5,\"synthetic_bins\":12},\"component\":[{\"id\":\"v\",\"media\":\"video\",\"timebase\":{\"den\":600,\"num\":16777216}}],\"defaults\":{\"deadline_ms\":1500},\"floor\":{\"extra_keyframes\":false},\"ladder\":[{\"capability\":\"wai.video.int_motion\",\"component\":\"v\",\"id\":\"b0\",\"params\":{\"block\":16,\"max_sse_px_milli\":0,\"mv_precision\":2,\"q_candidates\":[8],\"search\":7},\"role\":\"base\"}],\"lead\":\"v\",\"live\":{\"layer_lag_ms\":[0]},\"moment\":[],\"royalty_free_only\":false,\"rule\":[],\"schema\":\"wai.pack.policy/1\",\"segmentation\":{\"closed\":true,\"cut_abs_permille\":300,\"cut_rel_permille\":3000,\"cut_window\":8,\"lookahead_frames\":6,\"max_ms\":600000,\"min_ms\":1000,\"scene_cut\":\"wai.pack.scenecut/1\",\"target_ms\":2000}}", + "digest": "9167074601943ba9c5042ae719712c8119a728e2710a355371b2602c2c112e67" + }, { "file": "eq-defaults-a.toml", "canonical": "{\"asset_id\":\"r\",\"class\":[],\"classify\":{\"motion_high_milli\":9000,\"static_mad_milli\":300,\"static_permille\":5,\"synthetic_bins\":12},\"component\":[{\"id\":\"v\",\"media\":\"video\",\"timebase\":{\"den\":1,\"num\":25}}],\"defaults\":{\"deadline_ms\":1500},\"floor\":{\"extra_keyframes\":false},\"ladder\":[{\"capability\":\"wai.video.int_motion\",\"component\":\"v\",\"id\":\"b0\",\"params\":{\"block\":16,\"max_sse_px_milli\":0,\"mv_precision\":2,\"q_candidates\":[8],\"search\":7},\"role\":\"base\"}],\"lead\":\"v\",\"live\":{\"layer_lag_ms\":[0]},\"moment\":[],\"royalty_free_only\":true,\"rule\":[],\"schema\":\"wai.pack.policy/1\",\"segmentation\":{\"closed\":true,\"cut_abs_permille\":300,\"cut_rel_permille\":3000,\"cut_window\":8,\"lookahead_frames\":6,\"max_ms\":4000,\"min_ms\":1000,\"scene_cut\":\"wai.pack.scenecut/1\",\"target_ms\":2000}}", diff --git a/wai/pack-conformance/policy/max-piece.toml b/wai/pack-conformance/policy/max-piece.toml new file mode 100644 index 000000000..912b8aaff --- /dev/null +++ b/wai/pack-conformance/policy/max-piece.toml @@ -0,0 +1,19 @@ +# Accepted: the longest piece a policy may allow (§4.4): max_ms of ten minutes, which is +# exactly 2^24 units on a timebase of 16777216 units in 600 seconds. +schema = "wai.pack.policy/1" +asset_id = "max-piece" +lead = "v" + +[[component]] +id = "v" +media = "video" +timebase = { num = 16777216, den = 600 } + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" + +[segmentation] +max_ms = 600000 diff --git a/wai/pack-conformance/policy_diff.py b/wai/pack-conformance/policy_diff.py index c0dc9c0f1..9f01e22cf 100644 --- a/wai/pack-conformance/policy_diff.py +++ b/wai/pack-conformance/policy_diff.py @@ -126,11 +126,11 @@ def random_policy(rng): for c in comps: body += ["[[component]]", f'id = "{c}"', "media = " + val(q + biased(["video", "audio", "bogus"]) + q)] if rng.random() > 0.05: - num, den = val(str(biased([25, 0, 48000]))), val(str(biased([1, 1001, 0]))) + num, den = val(str(biased([25, 0, 48000, 4294967295, 16777216]))), val(str(biased([1, 1001, 0, 600]))) body.append("timebase = { num = " + num + ", den = " + den + " }") if rng.random() < 0.6: body.append("[segmentation]") - for k, choices in [("target_ms", [2000, 500, 0]), ("min_ms", [1000, 3000, 0]), ("max_ms", [4000, 100]), + for k, choices in [("target_ms", [2000, 500, 0]), ("min_ms", [1000, 3000, 0]), ("max_ms", [4000, 100, 600000, 600001, 349525]), ("cut_abs_permille", [300, 0, 1001]), ("cut_window", [8, 0, 2, 3, 65]), ("lookahead_frames", [6, 65]), ("closed", ["true", "false"])]: if rng.random() < 0.4: diff --git a/wai/pack-conformance/refusals/expected.json b/wai/pack-conformance/refusals/expected.json index dc59a8eff..b066bda6b 100644 --- a/wai/pack-conformance/refusals/expected.json +++ b/wai/pack-conformance/refusals/expected.json @@ -80,6 +80,14 @@ "file": "policy/open-gop.toml", "reason": "policy-open-gop" }, + { + "file": "policy/piece-bounds-max-ms.toml", + "reason": "policy-piece-bounds" + }, + { + "file": "policy/piece-bounds-units.toml", + "reason": "policy-piece-bounds" + }, { "file": "policy/piece-bounds.toml", "reason": "policy-piece-bounds" diff --git a/wai/pack-conformance/refusals/policy/piece-bounds-max-ms.toml b/wai/pack-conformance/refusals/policy/piece-bounds-max-ms.toml new file mode 100644 index 000000000..0605072d7 --- /dev/null +++ b/wai/pack-conformance/refusals/policy/piece-bounds-max-ms.toml @@ -0,0 +1,18 @@ +# Refused: max_ms past ten minutes (§4.4), the largest allowed plus one. +schema = "wai.pack.policy/1" +asset_id = "r" +lead = "v" + +[[component]] +id = "v" +media = "video" +timebase = { num = 25, den = 1 } + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" + +[segmentation] +max_ms = 600001 diff --git a/wai/pack-conformance/refusals/policy/piece-bounds-units.toml b/wai/pack-conformance/refusals/policy/piece-bounds-units.toml new file mode 100644 index 000000000..a16d7e1c8 --- /dev/null +++ b/wai/pack-conformance/refusals/policy/piece-bounds-units.toml @@ -0,0 +1,17 @@ +# Refused: the default max_ms (4000) is past 2^24 units on a timebase of 2^32 − 1 units a +# second (§4.4). +schema = "wai.pack.policy/1" +asset_id = "r" +lead = "v" + +[[component]] +id = "v" +media = "video" +timebase = { num = 4294967295, den = 1 } + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" + diff --git a/wai/pack-conformance/staged-mutations/expected.json b/wai/pack-conformance/staged-mutations/expected.json index 0df2d7815..cd1c7169e 100644 --- a/wai/pack-conformance/staged-mutations/expected.json +++ b/wai/pack-conformance/staged-mutations/expected.json @@ -42,6 +42,24 @@ "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" ] }, + { + "name": "max-ms-past-cap", + "reason": "policy-piece-bounds", + "remove": [] + }, + { + "name": "still-at-cap", + "reason": "floor-object-mismatch", + "remove": [ + "floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s", + "floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s", + "objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai", + "objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai", + "objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai", + "objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai", + "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" + ] + }, { "name": "still-held-digest", "reason": "prefix-digest", diff --git a/wai/pack-conformance/staged-mutations/max-ms-past-cap/LISTING b/wai/pack-conformance/staged-mutations/max-ms-past-cap/LISTING new file mode 100644 index 000000000..a4821b213 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/max-ms-past-cap/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +1ea26eaea415f8b459f5994a950e14016a501c2464b9ed8d77db800e16da31d2 hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +424e86d090780853634a8b95c8a75e9a012fc28e74e0fede44127823cc3e5efe index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +6204e0570e77912b82c4b0bbbb51d3612334367b2ab62021a93578043e296ec9 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +36b5c54344949f8fda383212f94d78cabadcaf22be64b30f1ed8544c1a247588 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +9c4eda5ea5972cbca98b3b89503235fcd807450c129545ca86c322337e27d872 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/max-ms-past-cap/policy.toml b/wai/pack-conformance/staged-mutations/max-ms-past-cap/policy.toml new file mode 100644 index 000000000..9dd229e9e --- /dev/null +++ b/wai/pack-conformance/staged-mutations/max-ms-past-cap/policy.toml @@ -0,0 +1,94 @@ +schema = "wai.pack.policy/1" +asset_id = "video-staged-01" +royalty_free_only = true +lead = "v" + +[[component]] +id = "v" +media = "video" +source = "video-staged-01.y4m" + +[component.timebase] +num = 25 +den = 1 + +[segmentation] +target_ms = 2000 +min_ms = 1000 +max_ms = 600001 + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" +params = { block = 16, search = 4, mv_precision = 1, q_candidates = [24, 16], max_sse_px_milli = 60000 } + +[[ladder]] +id = "r1" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "b0" +params = { q = 12 } + +[[ladder]] +id = "r2" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "r1" +params = { q = 4 } + +[[ladder]] +id = "still" +component = "v" +capability = "wai.image.png" +role = "base" + +[[ladder]] +id = "f0" +component = "v" +capability = "wai.video.av1" +role = "floor" +params = { source = "supplied" } + +[floor] +supplied_listing = "f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150" + +[live] +layer_lag_ms = [0, 200, 400] + +[[class]] +id = "tv" +max_layer = 2 + +[[class]] +id = "mobile" +max_layer = 1 +max_kbps = 330 + +[[class]] +id = "legacy" +floor_only = true + +[[moment]] +id = "m-event" +from_ms = 5600 +to_ms = 8000 +tags = ["event"] + +[[rule]] +id = "static" +when = { piece_kind = ["static"] } +then = { ladder = ["still", "f0"] } + +[[rule]] +id = "motion" +when = { piece_kind = ["motion_high"] } +then = { max_layer = 1 } + +[[rule]] +id = "event" +when = { moment_tag = ["event"] } +then = { deadline_ms = 800 } diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/LISTING b/wai/pack-conformance/staged-mutations/still-at-cap/LISTING new file mode 100644 index 000000000..fd40874c5 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/LISTING @@ -0,0 +1,35 @@ +27ba282182985cfc6a7811ce3ee7d7da6733f14148cec48bc8cef3dfd6bcf12b dash/classic.mpd +05f95a5c55497ebfe5146485460f1964924abfb82031bfb6bc439652d92bc403 dash/mobile.mpd +a1b5ae6179b1ccf1b839e5beb537fb04d53d435401bcdc55948f0b5880636839 dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +ac98cd1c948a6fe21d7402ee885fed6db86d60a33b328a98770c07a9df44e5ec hls/classic/floor-v.m3u8 +e3e203490a794965fe4bb8ab9ffe75615dc53fddc18f705ed5fca542558ac370 hls/classic/master.m3u8 +e3e203490a794965fe4bb8ab9ffe75615dc53fddc18f705ed5fca542558ac370 hls/legacy/master.m3u8 +a7974d0968cc8dcd30703ddf53e149c7c6a5996bcf1d77cd043439dc40aefbac hls/mobile/master.m3u8 +501762490c55c1d183634385532bac19b719b9d486dcf03fa9d702c73502ec97 hls/mobile/wai-v.m3u8 +31284c7b277203d780c46aaa97e00e475cf32223e9871fb5ffd2471cdbac9a64 hls/mobile/wai.m3u8 +a7974d0968cc8dcd30703ddf53e149c7c6a5996bcf1d77cd043439dc40aefbac hls/tv/master.m3u8 +99b9aabe5eb6aac46b6ae7a233ff99468f477a0e1e51d6a38a6a5abd5f0f62ee hls/tv/wai-v.m3u8 +fd7f47142bf62014377c02de762f2b76c26e681a908f1713e2ba53d90de51377 hls/tv/wai.m3u8 +040159d92dc28a379dc8636c11c158ef65a99349cc8be5114166d4245ff6e007 index-v.wsix +22652936803efca38577556f8a2cc61b4a8dde41c9737d8379855a84d6fb7aa8 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +660655a910324053f875b527d757be9351b79fcab94b2cb81b8a4024d8a528cc objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +c0ecb3d5882897fc1df3f49856422fb1490ad30f40e3504f382e2dbcfaae2e40 package.receipt.json +6261e5b48f122bd416b9f71b2416fba9f1e3c17b67682ba6a3f26e0a27ef9f5e plan-v.txt +be4b23903fd6785aecac915be7a2f87a31ad02240e17849d73a9c240a84c3814 policy.json +86c0015adc05f80f474c3dfa5a235e28a006496772c1956f98786783afc57f9b policy.toml +f17595ba6c62ba2524e0d899c67d1dcb0bd3be00947e26917dd25c358eb22aba stages/legacy.json +fa1e2dd8fa9d1aee45af6a96e70fa94302b1f80212293a6de20ce94979ef6e57 stages/mobile.json +0d7f00e4854fc3c5b75644f422d00577ecb2354153ace0e87da6256d3fcee8ef stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/dash/classic.mpd b/wai/pack-conformance/staged-mutations/still-at-cap/dash/classic.mpd new file mode 100644 index 000000000..07664a4c5 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/dash/classic.mpd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/dash/mobile.mpd b/wai/pack-conformance/staged-mutations/still-at-cap/dash/mobile.mpd new file mode 100644 index 000000000..0339b7d5d --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/dash/mobile.mpd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/dash/tv.mpd b/wai/pack-conformance/staged-mutations/still-at-cap/dash/tv.mpd new file mode 100644 index 000000000..307e34f2c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/dash/tv.mpd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/floor-v.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/floor-v.m3u8 new file mode 100644 index 000000000..7f2e4b468 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/floor-v.m3u8 @@ -0,0 +1,14 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:600 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-MAP:URI="../../floor/v/init.mp4" +#EXTINF:2.000, +../../floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +#EXTINF:1.000, +../../floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +#EXTINF:600.000, +../../floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/master.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/master.m3u8 new file mode 100644 index 000000000..7614a173f --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/classic/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=67,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/legacy/master.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/legacy/master.m3u8 new file mode 100644 index 000000000..7614a173f --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/legacy/master.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=67,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/master.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/master.m3u8 new file mode 100644 index 000000000..ba0ebb040 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=67,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..eb43b9525 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai-v.m3u8 @@ -0,0 +1,15 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:600 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:600.000, +../../objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai#n=977 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai.m3u8 new file mode 100644 index 000000000..d0eb98cb7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/mobile/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="mobile" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/mobile.json" +#EXT-X-STREAM-INF:BANDWIDTH=323176,AVERAGE-BANDWIDTH=177139,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/master.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/master.m3u8 new file mode 100644 index 000000000..ba0ebb040 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/master.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.multivariant",VALUE="wai.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=8840,AVERAGE-BANDWIDTH=67,CODECS="av01.0.00M.08",RESOLUTION=64x36 +../classic/floor-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..479f2f9c7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai-v.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:600 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:600.000, +../../objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai#n=977 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai.m3u8 new file mode 100644 index 000000000..e60a26af9 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=645600,AVERAGE-BANDWIDTH=465358,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/index-v.wsix b/wai/pack-conformance/staged-mutations/still-at-cap/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..726e2b4f53eb1c172e4e6b79c0fc0c34b38553a0 GIT binary patch literal 1704 zcmWF!_KaX;U|^60Vi3&?#9Tnk0cD@q?0v7!#?0n{>xN@YDO`KBCofd^5NV{!-7UI) zoAEITpfZvG=SLm=7O|P8d0#5Oqy_2jyWTNv%`u-fB9<>%_j*-NWx-{b5mbT&=t3qW zMKE!Y`$3vn7??obfCFzRYlaoXPfQGqjI2O0n4*)RQkgoc=RJc)K#(y9H*#mO}zoYX5zfHDz3$}heBmer$JZ61`i2qJ4POEx~ zt{$^CSge2SKUm*3s4;I+pbRKg^=j9jIRTeCw|$=7vi}ayBeU(Eg=!s6ykX!s-(j!!#T$07Wp$)D$Z zueSzkbcPyJ!j5X*&Cso%loi~XW@p7NIAZ3gGx240Esxmx4R!H(8H?wA%aIO5RS)6; z?Pr47ANQ{Oh-`Q6j<*LJO3DShSBrc9*m29wVImucL6$?ro1@kce!R7o)U zP=rd3@3o4`M>`MlTR)N6)3NTLs-LXd%Nx}$%AA~F?QjAV{%oMo$C*;#O2G_}dJwn_ zwN?w_9JnIwscp4J213`8H~cu!f8xf4!&_6drnh|HH8`iWvCKp!@sTP-Da_R%vykKe z^|50|r405mu`lM_>-)wdl2=zU^!)nQm4W>Hhxe?hDPY(f4K@H4{|Zp^U=B#li!VB{ zLo9ZF6TdPvKrDpC|C}?DPAKt15X*}#LRq&jf(k?+fN2B8 zKPU;o_=DGt4!y&~$vLAeUox==awdrB5#hxu$?M1Bum8h7{C1nD`RG z5ZOzwLu2M&-FE-MS%zKfQcJ>`RM)Ste4O=7KYQ=GJ<#+DvkzoHIsQKb4ZtU$2t)#m zaGyW~f#oBE7c8)WLO|yt@qiqlLl_x=j)0oM$WX?_P*TRmkei>BnUl)KkdvC8m|Q8s zP@b5nSC*NQny;6cR}!C_Uy_-hhb~%_nwFWD%8R5nvCI&{&CE?qPt_~PONZJ60F|(p Ak^lez literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/moq/catalog.json b/wai/pack-conformance/staged-mutations/still-at-cap/moq/catalog.json new file mode 100644 index 000000000..340931feb --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/moq/catalog.json @@ -0,0 +1 @@ +{"tracks":[{"altGroup":1,"isLive":false,"name":"v/l0","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_motion","wai.image.png"],"wai_determinism":"decode-equivalence","wai_receipt_alias":6826975590310646,"wai_receipts":true,"wai_receipts_track":"v/l0/receipts","wai_staged":{"layer":0,"lead_ms":null,"set":"v"}},{"depends":["v/l0"],"isLive":false,"name":"v/l1","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1300,"wai_determinism":"decode-equivalence","wai_receipt_alias":5745064158311842,"wai_receipts":true,"wai_receipts_track":"v/l1/receipts","wai_staged":{"layer":1,"lead_ms":null,"set":"v"}},{"depends":["v/l0","v/l1"],"isLive":false,"name":"v/l2","namespace":"video-staged-01","packaging":"wai","renderGroup":1,"role":"video","wai_capabilities":["wai.video.int_refine"],"wai_delivery_timeout_ms":1100,"wai_determinism":"decode-equivalence","wai_receipt_alias":6697012243957121,"wai_receipts":true,"wai_receipts_track":"v/l2/receipts","wai_staged":{"layer":2,"lead_ms":null,"set":"v"}},{"altGroup":1,"codec":"av01.0.00M.08","height":36,"name":"v/floor","namespace":"video-staged-01","packaging":"cmaf","renderGroup":1,"width":64},{"isLive":false,"name":"v/l0/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l1/receipts","namespace":"video-staged-01","packaging":"wai-receipts"},{"isLive":false,"name":"v/l2/receipts","namespace":"video-staged-01","packaging":"wai-receipts"}],"version":1} diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai b/wai/pack-conformance/staged-mutations/still-at-cap/objects/e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb.wai new file mode 100644 index 0000000000000000000000000000000000000000..66ffb59e76967424b8ddc0be4df5559a65175733 GIT binary patch literal 977 zcmWG{^fX+}z`#(gRGyfrWTj-NXP~5`l$)B8nF!>SWu~O&1NoVGC8>EOK!Kvvf}G6c z#FA8yqWqN9ocN;D!qUv5)LgK9wNi3oL1I#7PG(6ZP&Lp2AeAm6H5DLXSS1*i@xTas9mo(i<3S}Cn4F*mhX$;!~wz`#I9DMQK1*i1*M zT*=DJq_$RynSr4*z|WnRONtBVa$ZjlmmnbR0K_UBOh9s5-s+i<_Ww{ z6*p^ZX)RcDaN)ebvvwjU7&bMWa%9k9{-`*CwZ-A8gIb4c)rSi}z1zH1I@m*8TFO0n z<*ZhuU-{m_jG>hEo8BDjiYvymeyB0Kc6`yBVmjlBaTLo^=BdpL5p0i~6}UMXS2g+w za6gMx;6EW5@=7xIa8bv1y$)8B5`&fLB|TaUmmCxoijcU+%D6ydLT3X1hN@#7hsqS#HMpj*eQIPe zl0Lgzf$M}@X!Dt6`>T%K*p!E)>&6~#Q22g3usYpm%{-thy_k18FwN|J87m+i%4<~Z zaK(7pHDAU{4vq>etJsVjb!6_b8VbH~TsY5gb0ymh#z)Md2X02}!&KC0vw&-ZLPziE-CzwX=54HbWTYT` zf~kwKwLzf2+>zmkoXac8>}BiNJ~e0^Xi~7WxyKsl&~QpF;%iUuW;2FJ2g3yd0*}7y z0RyUl)xqMyE6M2R`HZU=xSM|b*vtKdfivY>!|L=mF%; zah*`Vv>TXuKq)})My1>d7=%c;dNfU)^6vTS38n3gdX|8zBG}9p| zV5R(Ad4?bdvsF?lW^1qZpW|n^xdIqEB?0^lt%WyK1@Bt50Mk5!r>mdKI;Vst0QLuS AH~;_u literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/package.receipt.json b/wai/pack-conformance/staged-mutations/still-at-cap/package.receipt.json new file mode 100644 index 000000000..be16a10c0 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"d63bc24ddb0ee451a141ec642ffa38ca50274d366df66180537466b00841d91c","media_class":"video","stage_root":"1f14fc81730f5f6c334ac4189f74f274deee40959b6c781416803d28a7e2b3d4"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"c8b34bde7e3c363ce045b0c602640abc2b93a120f05932250b8b15afb633c618","receipt_hash":"fca44f050468ff40abf3d5afda9e6f39bb625e830b6cd49201ebb805839f7ef0","sig":"3835f7b7953adda716251a5930ac8da498b2cd14790aebc8ec1cf556e6ce84716e981cd40fec3639532ce2edba13c84878da66c7fb5e137d82c6b962c10e7b05","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":267312,"total_bytes":267312,"total_entries":10,"unique_objects":10,"version":2,"work_units":10} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/plan-v.txt b/wai/pack-conformance/staged-mutations/still-at-cap/plan-v.txt new file mode 100644 index 000000000..941262bd3 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/plan-v.txt @@ -0,0 +1,3 @@ +0 +50 +75 diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/policy.json b/wai/pack-conformance/staged-mutations/still-at-cap/policy.json new file mode 100644 index 000000000..216552ece --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/policy.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","class":[{"floor_only":false,"id":"tv","max_layer":2},{"floor_only":false,"id":"mobile","max_kbps":330,"max_layer":1},{"floor_only":true,"id":"legacy","max_layer":0}],"classify":{"motion_high_milli":9000,"static_mad_milli":300,"static_permille":5,"synthetic_bins":12},"component":[{"id":"v","media":"video","source":"video-staged-01.y4m","timebase":{"den":1,"num":25}}],"defaults":{"deadline_ms":1500},"floor":{"extra_keyframes":false,"supplied_listing":"f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150"},"ladder":[{"capability":"wai.video.int_motion","component":"v","id":"b0","params":{"block":16,"max_sse_px_milli":60000,"mv_precision":1,"q_candidates":[24,16],"search":4},"role":"base"},{"capability":"wai.video.int_refine","component":"v","id":"r1","params":{"q":12},"refines":"b0","role":"refinement"},{"capability":"wai.video.int_refine","component":"v","id":"r2","params":{"q":4},"refines":"r1","role":"refinement"},{"capability":"wai.image.png","component":"v","id":"still","params":{},"role":"base"},{"capability":"wai.video.av1","component":"v","id":"f0","params":{"source":"supplied"},"role":"floor"}],"lead":"v","live":{"layer_lag_ms":[0,200,400]},"moment":[{"from_ms":5600,"id":"m-event","tags":["event"],"to_ms":8000}],"royalty_free_only":true,"rule":[{"id":"static","then":{"ladder":["still","f0"]},"when":{"piece_kind":["static"]}},{"id":"motion","then":{"max_layer":1},"when":{"piece_kind":["motion_high"]}},{"id":"event","then":{"deadline_ms":800},"when":{"moment_tag":["event"]}}],"schema":"wai.pack.policy/1","segmentation":{"closed":true,"cut_abs_permille":300,"cut_rel_permille":3000,"cut_window":8,"lookahead_frames":6,"max_ms":600000,"min_ms":1000,"scene_cut":"wai.pack.scenecut/1","target_ms":2000}} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/policy.toml b/wai/pack-conformance/staged-mutations/still-at-cap/policy.toml new file mode 100644 index 000000000..652ee926f --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/policy.toml @@ -0,0 +1,94 @@ +schema = "wai.pack.policy/1" +asset_id = "video-staged-01" +royalty_free_only = true +lead = "v" + +[[component]] +id = "v" +media = "video" +source = "video-staged-01.y4m" + +[component.timebase] +num = 25 +den = 1 + +[segmentation] +target_ms = 2000 +min_ms = 1000 +max_ms = 600000 + +[[ladder]] +id = "b0" +component = "v" +capability = "wai.video.int_motion" +role = "base" +params = { block = 16, search = 4, mv_precision = 1, q_candidates = [24, 16], max_sse_px_milli = 60000 } + +[[ladder]] +id = "r1" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "b0" +params = { q = 12 } + +[[ladder]] +id = "r2" +component = "v" +capability = "wai.video.int_refine" +role = "refinement" +refines = "r1" +params = { q = 4 } + +[[ladder]] +id = "still" +component = "v" +capability = "wai.image.png" +role = "base" + +[[ladder]] +id = "f0" +component = "v" +capability = "wai.video.av1" +role = "floor" +params = { source = "supplied" } + +[floor] +supplied_listing = "f00d8bf055ef725d99f925cc0c1c97b6b7fc956c82872f63840c246b4b69e150" + +[live] +layer_lag_ms = [0, 200, 400] + +[[class]] +id = "tv" +max_layer = 2 + +[[class]] +id = "mobile" +max_layer = 1 +max_kbps = 330 + +[[class]] +id = "legacy" +floor_only = true + +[[moment]] +id = "m-event" +from_ms = 5600 +to_ms = 8000 +tags = ["event"] + +[[rule]] +id = "static" +when = { piece_kind = ["static"] } +then = { ladder = ["still", "f0"] } + +[[rule]] +id = "motion" +when = { piece_kind = ["motion_high"] } +then = { max_layer = 1 } + +[[rule]] +id = "event" +when = { moment_tag = ["event"] } +then = { deadline_ms = 800 } diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/stages/legacy.json b/wai/pack-conformance/staged-mutations/still-at-cap/stages/legacy.json new file mode 100644 index 000000000..c80f4ce9c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/stages/legacy.json @@ -0,0 +1 @@ +{"class":"legacy","components":{"v":{"pieces":[{"dur":50,"floor_only":true,"p":0,"stages":[],"start":0},{"dur":25,"floor_only":true,"p":1,"stages":[],"start":50},{"dur":15000,"floor_only":true,"p":2,"stages":[],"start":75}]}},"policy_digest":"c8b34bde7e3c363ce045b0c602640abc2b93a120f05932250b8b15afb633c618"} diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/stages/mobile.json b/wai/pack-conformance/staged-mutations/still-at-cap/stages/mobile.json new file mode 100644 index 000000000..c5f8434d7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":15000,"p":2,"stages":[{"bytes":977,"h":"e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb","layer":0}],"start":75}]}},"policy_digest":"c8b34bde7e3c363ce045b0c602640abc2b93a120f05932250b8b15afb633c618"} diff --git a/wai/pack-conformance/staged-mutations/still-at-cap/stages/tv.json b/wai/pack-conformance/staged-mutations/still-at-cap/stages/tv.json new file mode 100644 index 000000000..98ab664c7 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-at-cap/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":15000,"p":2,"stages":[{"bytes":977,"h":"e8eb7b1a422709e42bc81aa6f0afc12f658a8a6427ee1261f05204e8b4126adb","layer":0}],"start":75}]}},"policy_digest":"c8b34bde7e3c363ce045b0c602640abc2b93a120f05932250b8b15afb633c618"} diff --git a/wai/wai-pack/src/golden.rs b/wai/wai-pack/src/golden.rs index ebdc34350..394c0ff72 100644 --- a/wai/wai-pack/src/golden.rs +++ b/wai/wai-pack/src/golden.rs @@ -1063,6 +1063,47 @@ pub fn staged_mutations(g: &Files, key: &SigningKey) -> Vec { f.insert("plan-v.txt".into(), crate::pack::plan_file(&starts).into_bytes()); set_golden_index(&mut f, &x); add("still-past-max", "index-policy-mismatch", sealed(f)); + // A policy past §4.4's longest piece: refused as a policy, before its index is read. + let mut f = g.clone(); + policy_edit(&mut f, "max_ms = 4000", "max_ms = 600001"); + relist(&mut f); + add("max-ms-past-cap", "policy-piece-bounds", f); + // The longest piece the policy allows (max_ms ten minutes) given to the still, as the + // last piece of a package cut after it, its manifest naming the frames: refused by + // the floor's unit count before a frame is decoded (§11.3), in either verifier. + let mut f = g.clone(); + policy_edit(&mut f, "max_ms = 4000", "max_ms = 600000"); + let at_cap = crate::policy::Policy::parse_bytes(&f["policy.toml"]).expect("a policy at the cap"); + let tb = at_cap.components[0].timebase; + let units = u32::try_from(tb.units_from_ms(at_cap.segmentation.max_ms)).expect("within 2^24"); + let mut x = golden_index(&f); + let still = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").map(|k| x.entries[k].piece as usize).expect("the still"); + let keep = (x.pieces[still].first_entry + x.pieces[still].n_entries as u32) as usize; + x.pieces.truncate(still + 1); + x.entries.truncate(keep); + for row in x.plan.iter_mut() { + row.truncate(still + 1); + } + let starts: Vec = x.pieces.iter().map(|p| p.start).collect(); + x.header.plan_digest = wsix::plan_digest(&starts); + x.pieces[still].duration = units; + let kind = crate::classify::PieceKind::from_code(x.pieces[still].kind).expect("a kind"); + let pp = crate::plan::plan_piece(&at_cap, tb, x.pieces[still].start, units as u64, kind); + (x.pieces[still].rules, x.pieces[still].moments) = (pp.rules, pp.moments); + let bytes: Vec = x.piece_entries(still).iter().filter(|e| matches!(e.role, Role::Base | Role::Refinement)).map(|e| e.byte_len).collect(); + for (ci, c) in at_cap.classes.iter().enumerate() { + x.plan[ci][still] = crate::plan::class_plan(c, &bytes, pp.max_layer, units as u64, tb); + } + let named: std::collections::BTreeSet = x.entries.iter().map(|e| objects::object_path(&e.content_hash)).collect(); + f.retain(|k, _| !k.starts_with("objects/") || named.contains(k)); + f.insert("plan-v.txt".into(), crate::pack::plan_file(&starts).into_bytes()); + set_golden_index(&mut f, &x); + let k = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").expect("the still"); + let w = Wai::from_bytes(&f[&objects::object_path(&x.entries[k].content_hash)]).expect("an envelope"); + let target = w.manifest.target.clone(); + let (h, wd) = (target["h"].as_u64().expect("h") as u32, target["w"].as_u64().expect("w") as u32); + replace_object(&mut f, k, objects::base_envelope("video", "wai.image.png", w.payload.clone(), units as u64, h, wd)); + add("still-at-cap", "floor-object-mismatch", sealed(f)); // The still's prefix digest of its one frame, not of the frame held for its piece. let mut f = g.clone(); let mut x = golden_index(&f); diff --git a/wai/wai-pack/src/pack.rs b/wai/wai-pack/src/pack.rs index e9415dbb5..9a5fe6a6c 100644 --- a/wai/wai-pack/src/pack.rs +++ b/wai/wai-pack/src/pack.rs @@ -164,14 +164,18 @@ pub fn clip_digest>(frames: &[F]) -> [u8; 32] { } /// The clip digest of one RGB8 frame held for `n` frames (§5.7): the SHA-256 of `n` -/// copies of the frame's SHA-256, fed one at a time, so no frame is repeated in memory. +/// copies of the frame's SHA-256, fed a block of them at a time, so no frame is repeated +/// in memory. `n` is at most `2^24` (§4.4). pub fn held_digest(frame: &[u8], n: u64) -> [u8; 32] { use sha2::Digest as _; + const BLOCK: u64 = 1024; let one = wai::output_tree::out_digest(frame); + let block = one.repeat(BLOCK as usize); let mut h = sha2::Sha256::new(); - for _ in 0..n { - h.update(one); + for _ in 0..n / BLOCK { + h.update(&block); } + h.update(&block[..(n % BLOCK) as usize * one.len()]); h.finalize().into() } diff --git a/wai/wai-pack/src/policy.rs b/wai/wai-pack/src/policy.rs index fa2185993..56946cc6e 100644 --- a/wai/wai-pack/src/policy.rs +++ b/wai/wai-pack/src/policy.rs @@ -53,6 +53,13 @@ pub const MAX_RUNGS: usize = 64; pub const MAX_CLASSES: usize = 32; pub const MAX_RULES: usize = 32; pub const MAX_MOMENTS: usize = 32; +/// The longest piece a policy may allow ([packaging] §4.4): `max_ms` at most ten minutes, +/// and at most `2^24` units on every component's timebase. A verifier's work for one +/// piece grows with its frames, so this bounds it. +/// +/// [packaging]: ../../../extensions/packaging.md +pub const MAX_PIECE_MS: u64 = 600_000; +pub const MAX_PIECE_UNITS: u64 = 1 << 24; pub const MAX_DEADLINES: usize = 4; /// Why a policy was refused. [`PolicyReason::code`] is the name the conformance corpus @@ -99,7 +106,8 @@ pub enum PolicyReason { Lead, /// `segmentation.closed` is false: every piece must be closed (§4.1). OpenGop, - /// Not `1 ≤ min_ms ≤ target_ms ≤ max_ms`. + /// Not `1 ≤ min_ms ≤ target_ms ≤ max_ms ≤ 600000`, or `max_ms` past `2^24` units + /// on a component's timebase. PieceBounds, /// A rung names a component the policy does not define. LadderComponent, @@ -936,6 +944,9 @@ impl Policy { if !(1 <= s.min_ms && s.min_ms <= s.target_ms && s.target_ms <= s.max_ms) { return e(P::PieceBounds, "segmentation"); } + if s.max_ms > MAX_PIECE_MS || self.components.iter().any(|c| c.timebase.units_from_ms(s.max_ms) > MAX_PIECE_UNITS) { + return e(P::PieceBounds, "segmentation.max_ms: past ten minutes, or past 2^24 units on a component's timebase"); + } let comps: BTreeSet<&str> = self.components.iter().map(|c| c.id.as_str()).collect(); if let Some(r) = self.ladder.iter().find(|r| !comps.contains(r.component.as_str())) { return e(P::LadderComponent, &format!("ladder.{}", r.id)); diff --git a/wai/wai-pack/src/verify.rs b/wai/wai-pack/src/verify.rs index 71b873e8c..296a958b8 100644 --- a/wai/wai-pack/src/verify.rs +++ b/wai/wai-pack/src/verify.rs @@ -361,6 +361,11 @@ fn verify_component( } objects.insert(e.content_hash, b.clone()); } + if errs.is_empty() + && let Some(r) = floor_unit_counts(policy, comp, ix, &objects) + { + return Err(vec![r]); + } if errs.is_empty() { match prefix_digests(ix, &objects) { Ok(d) => dims = d, @@ -459,6 +464,24 @@ fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Res if errs.is_empty() { Ok(dims) } else { Err(errs) } } +/// Before any frame is decoded (§11.3): each piece's floor object, where the policy has a +/// floor rung and the object's payload reads as §7.2's framing, holds one unit per frame +/// of its piece (`floor-object-mismatch`). A floor that does not read is left to +/// [`floor_view`], which runs when everything before it holds. +fn floor_unit_counts(policy: &Policy, comp: &Component, ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Option { + policy.ladder.iter().find(|r| r.component == comp.id && r.role == RungRole::Floor)?; + for p in 0..ix.pieces.len() { + let floors: Vec<&wsix::EntryRec> = ix.piece_entries(p).iter().filter(|e| e.role == Role::Floor).collect(); + let [fe] = floors.as_slice() else { continue }; + let Some(w) = objects.get(&fe.content_hash).and_then(|b| wai::container::Wai::from_bytes(b).ok()) else { continue }; + let Some((units, _)) = crate::floor::av1_units(&w.payload) else { continue }; + if units.len() as u64 != ix.pieces[p].duration as u64 { + return Some(reason("floor-object-mismatch", format!("piece {p}: {} floor units for {} frames, before any frame is decoded", units.len(), ix.pieces[p].duration))); + } + } + None +} + type FloorFiles = (Option, Vec<(String, Vec)>); /// The floor of a component as its objects give it (§7.2, §7.3, §7.6), and the CMAF files -- 2.54.0 (Apple Git-157) From d913dcb62204774bbe9876479ff177c21649ea32 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 14:45:44 -0400 Subject: [PATCH 16/21] =?UTF-8?q?wai-pack:=20both=20verifiers=20compare=20?= =?UTF-8?q?manifests=20with=20=C2=A75.2/=C2=A75.4=20(object-manifest);=20a?= =?UTF-8?q?n=20unreadable=20still=20is=20prefix-digest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Rust verifier never compared a base's manifest with §5.2, while pack_verify.py did, under a refusal that named no §11.3 reason. A still whose manifest had one extra target member was ok in Rust and refused in Python. Python's refusal of a base it cannot decode named no reason either. §11.3 now states one order, and both verifiers follow it. They take every base and refinement in entry order: - a base decodes by its capability, or it is prefix-digest; - its manifest is §5.2's for its piece's frames and frame size (object-manifest, a new §11.3 reason); - a refinement's manifest is §5.4's (object-manifest); - each layer applies to every frame of the stage below, or it is prefix-digest; - a base that does not decode, or a layer that does not apply, ends the pass; - then the decode-equivalent prefixes are compared with their digests. Rust's prefix_digests was restructured into this pass. The new staged mutation cases are: - still-manifest-target (object-manifest); - refinement-manifest-target (object-manifest); - still-incomplete-dist: the still re-coded with a lone two-bit distance code (prefix-digest). The reviewer's still-png-variant and still-manifest-variant packages now get those reasons from both verifiers. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/packaging.md | 17 ++-- wai/pack-conformance/README.md | 5 +- wai/pack-conformance/pack_verify.py | 6 +- .../staged-mutations/expected.json | 21 +++++ .../refinement-manifest-target/LISTING | 41 ++++++++++ .../refinement-manifest-target/dash/tv.mpd | 51 ++++++++++++ .../hls/tv/wai-v.m3u8 | 25 ++++++ .../hls/tv/wai.m3u8 | 7 ++ .../refinement-manifest-target/index-v.wsix | Bin 0 -> 2792 bytes ...dbf53c8e09d44b7c18f373fca930e9f1094e93.wai | Bin 0 -> 101293 bytes .../package.receipt.json | 1 + .../refinement-manifest-target/stages/tv.json | 1 + .../still-incomplete-dist/LISTING | 41 ++++++++++ .../hls/mobile/wai-v.m3u8 | 20 +++++ .../still-incomplete-dist/hls/tv/wai-v.m3u8 | 25 ++++++ .../still-incomplete-dist/index-v.wsix | Bin 0 -> 2792 bytes ...a5ed7a68026623192adb7725face30faf40b65.wai | Bin 0 -> 7266 bytes .../package.receipt.json | 1 + .../still-incomplete-dist/stages/mobile.json | 1 + .../still-incomplete-dist/stages/tv.json | 1 + .../still-manifest-target/LISTING | 41 ++++++++++ .../hls/mobile/wai-v.m3u8 | 20 +++++ .../still-manifest-target/hls/tv/wai-v.m3u8 | 25 ++++++ .../still-manifest-target/index-v.wsix | Bin 0 -> 2792 bytes ...acbc716b2b6b563be5ac275fdf8625459150cb.wai | Bin 0 -> 980 bytes .../package.receipt.json | 1 + .../still-manifest-target/stages/mobile.json | 1 + .../still-manifest-target/stages/tv.json | 1 + wai/wai-pack/src/golden.rs | 37 +++++++++ wai/wai-pack/src/verify.rs | 74 ++++++++++++------ 30 files changed, 432 insertions(+), 32 deletions(-) create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/LISTING create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/dash/tv.mpd create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/objects/0919a83b399562f8f5ab406974dbf53c8e09d44b7c18f373fca930e9f1094e93.wai create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/refinement-manifest-target/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/objects/3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65.wai create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/tv.json create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/LISTING create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/hls/mobile/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/hls/tv/wai-v.m3u8 create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/index-v.wsix create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/package.receipt.json create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/stages/mobile.json create mode 100644 wai/pack-conformance/staged-mutations/still-manifest-target/stages/tv.json diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index a6321d9bd..fb0526342 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -1096,11 +1096,16 @@ failed check names is not applied: - before any frame is decoded, each piece's floor object, where the policy has a floor rung and the object's payload reads as §7.2's framing, holds one unit per frame of its piece (`floor-object-mismatch`); - - every decode-equivalent prefix decodes to its `prefix_digest` (`prefix-digest`): - each piece's base by its capability's decode (a still held for its piece, §5.7), and - each refinement applied in layer order by a staged-delivery §4 sink under the - reference tools' cost limit and chain budget; a layer that does not apply to every - frame of the stage below it is `prefix-digest` too; + - every base and refinement, in entry order: a base decodes by its capability (a + still is a PNG §5.7 reads, its one frame held for the piece), or it is + `prefix-digest`; its manifest is the one §5.2 states for its piece's frames and its + frame size (`object-manifest`); a refinement's manifest is §5.4's + (`object-manifest`), and it applies in layer order, to every frame of the stage + below it, by a staged-delivery §4 sink under the reference tools' cost limit and + chain budget, or it is `prefix-digest`; a base that does not decode, or a layer that + does not apply, ends the check; + - then every decode-equivalent prefix decodes to its `prefix_digest` + (`prefix-digest`); - when all of that holds, the floor: per piece, one floor object of the floor rung's capability whose renderer, settings, framing header, unit count and target are §7.2's (`floor-object-mismatch`) and which presents the piece's base @@ -1156,6 +1161,6 @@ would leave behind the ones it does not write. | `floor-object-mismatch`, `floor-motif-mismatch`, `floor-av1-malformed`, `floor-keyframe-mismatch`, `floor-cmaf-mismatch`, against a package | §11.3 | | `receipt-json`, `receipt-version`, `receipt-index-count`, `receipt-index-digest`, `receipt-stage-root`, `receipt-component`, `receipt-policy-digest`, `receipt-counts`, `receipt-parent-kind`, `receipt-energy`, `receipt-signature` | §9.1 | | `measurement-claim-missing`, `measurement-claim-invalid` | §9.4 | -| `unexpected-file`, `listing-missing`, `listing-mismatch`, `policy-digest-mismatch`, `policy-json-mismatch`, `policy-component-mismatch`, `index-missing`, `index-policy-mismatch`, `plan-mismatch`, `object-missing`, `prefix-digest`, `catalog-mismatch`, `catalog-unexpected`, `object-unlisted` | §11.3 | +| `unexpected-file`, `listing-missing`, `listing-mismatch`, `policy-digest-mismatch`, `policy-json-mismatch`, `policy-component-mismatch`, `index-missing`, `index-policy-mismatch`, `plan-mismatch`, `object-missing`, `object-manifest`, `prefix-digest`, `catalog-mismatch`, `catalog-unexpected`, `object-unlisted` | §11.3 | | `pack-id`, `pack-source-missing`, `pack-source-rate`, `pack-media-unsupported`, `pack-floor-encode-unsupported`, `pack-capability-unsupported` (naming the capability) | §11.1, §11.4 | | `pack-encode`, `pack-piece-length`, `pack-catalog`, `pack-meter`, `pack-measurement-claim`, `pack-listing`, `pack-out-not-empty` | §11.4 | diff --git a/wai/pack-conformance/README.md b/wai/pack-conformance/README.md index e173a1b6b..375ff2408 100644 --- a/wai/pack-conformance/README.md +++ b/wai/pack-conformance/README.md @@ -96,7 +96,10 @@ packages (§6–§11), and their delivery over a simulated link (§8.9). duration never sets what a verifier allocates); a policy past §4.4's longest piece (`max_ms` 600001, `policy-piece-bounds`); and the still given the longest piece a policy allows (`max_ms` ten minutes), its manifest naming those frames, refused by its - floor's unit count before any frame is decoded (`floor-object-mismatch`). + floor's unit count before any frame is decoded (`floor-object-mismatch`); a still + whose manifest's `target` holds a member more, and a top layer whose `target` is not + null (each `object-manifest`); and the still as a PNG §5.7 does not read, its one + dynamic block's lone distance code two bits long (`prefix-digest`). - `delivery/` — `expected.json`: the staged golden packed live and delivered to a `tv` subscriber over a simulated link (§8.9), under §8.5's priorities, without them, and capped at layer 1: each piece's presented layer, base arrival and dropped layers, and diff --git a/wai/pack-conformance/pack_verify.py b/wai/pack-conformance/pack_verify.py index 3779b5218..cce1cc4a5 100644 --- a/wai/pack-conformance/pack_verify.py +++ b/wai/pack-conformance/pack_verify.py @@ -3179,7 +3179,7 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): wv = IPV.parse_wiv1(payload, 0) frames, pw, ph = IPV.decode_wiv1_k0(wv), wv["w"], wv["h"] except (IPV.Refused, ValueError, TypeError, IndexError, struct.error, zlib.error) as x: - rep.check(False, f"{t}: entry {i}: the base's payload is refused ({x})") + rep.check(False, f"{t}: entry {i}: the base does not decode ({x}) (prefix-digest)") return named | unreached(files, c['id']) decoded.setdefault(e["hash"], (wv, frames)) stage[i] = frames @@ -3189,7 +3189,7 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): "model_requirement": {"capability": e["cap"]}, "conditioning": {"kind": e["cap"].split(".")[-1]}, "target": {"frames": n, "h": ph, "w": pw}} rep.check(objs[i][8:8 + ml] == json.dumps(want, separators=(",", ":")).encode(), - f"{t}: entry {i}: the base's manifest is §5.2's") + f"{t}: entry {i}: the base's manifest is §5.2's (object-manifest)") elif e["role"] == REFINEMENT: base = E[pieces[k]["first_entry"]] # §5.4: the refinement's manifest, as `wai stage refine` writes it. @@ -3198,7 +3198,7 @@ def verify_component(rep, tag, root, pkg, files, P, j, c, ix, inputs=True): "refines": {"base": base["hash"].hex(), "prev": E[e["refines"]]["hash"].hex(), "layer": e["layer"]}, "target": None} rep.check(objs[i][8:8 + ml] == json.dumps(want, separators=(",", ":")).encode(), - f"{t}: entry {i}: the refinement's manifest is §5.4's") + f"{t}: entry {i}: the refinement's manifest is §5.4's (object-manifest)") try: stage[i] = wir1_apply(payload, stage[e["refines"]]) except (SV.Refused, KeyError, IndexError, struct.error) as x: diff --git a/wai/pack-conformance/staged-mutations/expected.json b/wai/pack-conformance/staged-mutations/expected.json index cd1c7169e..cbdafd185 100644 --- a/wai/pack-conformance/staged-mutations/expected.json +++ b/wai/pack-conformance/staged-mutations/expected.json @@ -60,6 +60,27 @@ "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" ] }, + { + "name": "still-manifest-target", + "reason": "object-manifest", + "remove": [ + "objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai" + ] + }, + { + "name": "refinement-manifest-target", + "reason": "object-manifest", + "remove": [ + "objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai" + ] + }, + { + "name": "still-incomplete-dist", + "reason": "prefix-digest", + "remove": [ + "objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai" + ] + }, { "name": "still-held-digest", "reason": "prefix-digest", diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/LISTING b/wai/pack-conformance/staged-mutations/refinement-manifest-target/LISTING new file mode 100644 index 000000000..aaeed97d3 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +88403f0b1e76e972d5568b6c610d8b9c8168e066eef3639cff1652437582e23f dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +1b7bf011b974365e6bc6532de6d0c54fa9b1447f75c84f3c52d02e1c433e146a hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +641ac8fcf0edae28860393d55b59956500de66b9d70631d05a4cc35d5c401897 hls/tv/wai-v.m3u8 +9fc04144671dbedefe16a418944f190493a57ce7dfb2c074a400d8ed9bab9a58 hls/tv/wai.m3u8 +e90609ca0c6788bdd26aa721b8a53c2f95d09d12bb7f2a3c709956334d2f9d4c index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +684f9b7716feb8400e14799a21cbc0e1134e2d578baddab485a3ff6760964669 objects/0919a83b399562f8f5ab406974dbf53c8e09d44b7c18f373fca930e9f1094e93.wai +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +849b7e2cba0f65d3183119f6f4f2c77e5c2a1db9a38d8552369db2dec28df7a7 objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +500e2089d40ae283f92f87796377d797db5a8afcc9832b3e48574cc0e1cab1c4 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +b8beb6eaa1c5a04801b0dcbcbd7f0885f5b0730871708ce0a59093e3c8ea1fdb stages/mobile.json +3368ea50fb636647172c5ade72396f806f477111d784e02f3747e16b4e60d30a stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/dash/tv.mpd b/wai/pack-conformance/staged-mutations/refinement-manifest-target/dash/tv.mpd new file mode 100644 index 000000000..33999b97c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/dash/tv.mpd @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..b809f9619 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f.wai#n=974 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/0919a83b399562f8f5ab406974dbf53c8e09d44b7c18f373fca930e9f1094e93.wai#n=101293" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai.m3u8 b/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai.m3u8 new file mode 100644 index 000000000..02cf087fc --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/hls/tv/wai.m3u8 @@ -0,0 +1,7 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.class",VALUE="tv" +#EXT-X-SESSION-DATA:DATA-ID="science.transaction.wai.stages",URI="../../stages/tv.json" +#EXT-X-STREAM-INF:BANDWIDTH=714367,AVERAGE-BANDWIDTH=586899,CODECS="wai1.video.int_motion+video.int_refine",X-WAI-FLOOR="wai1.video.int_motion+video.int_refine",RESOLUTION=64x36 +wai-v.m3u8 diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/index-v.wsix b/wai/pack-conformance/staged-mutations/refinement-manifest-target/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..c4414fc339e2b5f1a5a965f069305ff92e7228f7 GIT binary patch literal 2792 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

kkxDOw@p&k7>Ki^$ZwA-Y4FCB8&-_QWHC8In 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

dCku(Xcm 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 ylcXAVEn literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/package.receipt.json b/wai/pack-conformance/staged-mutations/refinement-manifest-target/package.receipt.json new file mode 100644 index 000000000..25bee42c4 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"94893f594e5a75b69c22d56ba708422ded9c1ec961800ca98a589872ae7bfe06","media_class":"video","stage_root":"410887606cde2b077949f3969a3c3e15c88fd4eca96fbfc40578104d54123edf"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"f38eaf41a38726d47b2cef7bb79f25de42e23424385d00ef6a7569390b2c1db0","sig":"0c86e32565262ccea7caa5f4e989fe228a61b16f2956cb2c3cb02f128388ae56e7997c9548957ee8ffb0b66d249456fc0df7df386c8c1876f273abc711550107","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":484462,"total_bytes":566396,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/refinement-manifest-target/stages/tv.json b/wai/pack-conformance/staged-mutations/refinement-manifest-target/stages/tv.json new file mode 100644 index 000000000..3ce42d359 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/refinement-manifest-target/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":974,"h":"8173f0eca11aaae0c29d415aedb60252cd8c8c32460c13e124ad70d9a4acd90f","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101293,"deadline_ms":800,"h":"0919a83b399562f8f5ab406974dbf53c8e09d44b7c18f373fca930e9f1094e93","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/LISTING b/wai/pack-conformance/staged-mutations/still-incomplete-dist/LISTING new file mode 100644 index 000000000..d0a175cb4 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +e7d4cd2aa50bed86c1b837d273028e20e4a181a1f477153d785e5bcbf55668d6 hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +c4f0ca96dca1a87e8c3128a545ed9ddd84b1630f662345d949609f9ab3765af6 hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +9215679a85d4f7d7404b68d5042802383a68c06021164d113d8387191fcbd0e8 index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +933a727c55cfb03d21b27717fd6f923fbfc58c1928541c96d27b374c26970e7a objects/3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +1ecdf2a13fa326a52017442880ef7ac6d168eff1570c1bc12d37e7f538b5ef24 package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +0f0dc3aa2798ddfc28baf0c15458cab16b20cc1ab032db0043145d42299d5de5 stages/mobile.json +72fe1316e743f97a39bb1d6acc486d05d6e2bb180bbee8f5d5c9829cf84c6622 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..cacb3893c --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/mobile/wai-v.m3u8 @@ -0,0 +1,20 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65.wai#n=7266 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..281ce8027 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65.wai#n=7266 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai#n=101295" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/index-v.wsix b/wai/pack-conformance/staged-mutations/still-incomplete-dist/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..556cdcc9eaafac0ad14b958e6a9264d4f0a4516d GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJt3JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+j zf!`+Eyaijoo{@ijW*)P?Ld1Wk7N=D`MOTkm8!Xm8_8+Ws8&vF2h61ieXPJR0NH;B7}>M!o`q^1PP}2@ zH{W5e_r)7_uVz0M@tCyZ_ko;}Ey@TV+0(J^psJs&+RGc& zF3OyoVC`@M6#i_W&<8~V5dfqf1TI6Z)q*$&u2XwzTdk3S(6!_ZKThbJ0@OU115)$ii%#qii=7|&nJZ_{l2i+u!&fS{x%+8Nd^)Kz--!El zIja32yOHC+=3M>t>R&qcK#nB-96-~2)xt8HLl zc&YP)uBkrKX_pgt8IP~ryqIPoKaZ`p3kcfiWuP_JI{Cn zol*Mqa*%5Z%(p9`jCIm55eU6RFhus!>(H3_SGV1NaF$`$y3~@eCe`&TEFWim)6d?! zZVxoQ!t4XtPmcf3Km+gzC<2iHvFRUFmw*7seo&Nv3jiQZuk;VIA5_MJ(?2jkK;<93 z(m%|8ZixL%Ape8xr&s!i*$054Ah&(c+QKPf``%YH#73 z`6p2Jzbz!;!t4jt;jr>g0T?K9?-rO%dZKmMS?f#fQ=yu2{{w%l-IBw?(s;LT$bR+X zKkfcYO3m*nJsSp3_d z)u)W`k_=)Guzkhgh2%t_7m;{C4ihNrGXf#VCI5kR852WE85=`xeo|&mDjP#iYISWu~O&1NoVGC8>EOK!Kvvf}G6c z#FA8yqWqN9ocN;D!qUv5)LgK9wNi3oL1I#7PG(6ZP&Lp2AeAm6H5DLXSS1*i@xTas9mo(i<3S}Cn4F*mhX$;!k)M=3+e%GgXtsa(m* z%%rw{!u-f>$V$p3|1>{TA{qM z%3yctsxA4fgH3(Igy_a7m&vZzW`v)03%T!l?@;(*!#fwO4;onIdgW(S{+L`XRYdxM7GEKCr@& zzhP(=KJda&!eJtj_<=#+>%|lN zz+O9t!XO`Xz#II)K@%Q@!9D1JKlp)zCQ=H6dCZ4U@B_!J7!?Nbm=D3=2aZ{>Dh%2o zHzI-`IAqAIFj$A&hzWk+kRiLm;2iNJF8F~XwgL+V<%lPd!4Dj<FVdQ&MBb@0Fa*1 AEC2ui literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/package.receipt.json b/wai/pack-conformance/staged-mutations/still-incomplete-dist/package.receipt.json new file mode 100644 index 000000000..94978185b --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/package.receipt.json @@ -0,0 +1 @@ +{"asset_id":"video-staged-01","components":[{"id":"v","index":"index-v.wsix","index_digest":"a141cee10159c15acdfab49f6faf1c8fbe1c22534e5419753f21bc2ca2d873fb","media_class":"video","stage_root":"2bcb771bcb7f6f93f7b15cc7c6d13e9a6e7956e113426b14ddec297b328f2455"}],"energy_provenance":null,"grant":{"capability":"wai.video.package","funds_ceiling":null,"grant_hash":"0000000000000000000000000000000000000000000000000000000000000000","joule_ceiling_micro":null},"joules_micro":0,"kind":"package","packager":"builtin:wai-pack@0.1.0","parent_kind":"none","parent_receipt_hash":null,"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57","receipt_hash":"e4eafe6ee51d145f60e008013279332502d57efb0183b6adee826b943ba1649e","sig":"1cc695b8e524defe79e3c5ac10bddddc555aede86a167e8fc12bd5b9a5d9df274c4ccf5a18a9af6a7f27cae32637cfaa535e04c38fa29b687b79f5542cfa5e08","signer_id":"did:key:z6MkhGok4PzLmtw1v5uW3Bpip8gHHifrCt42QJn4gEBfRVQG","signer_pubkey":"29e5833a915a6429a4e3a7948475c338ef436eb82be89c92f059704403db9d55","stored_bytes":490756,"total_bytes":572690,"total_entries":18,"unique_objects":14,"version":2,"work_units":18} \ No newline at end of file diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/mobile.json b/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/mobile.json new file mode 100644 index 000000000..4472d6717 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/mobile.json @@ -0,0 +1 @@ +{"class":"mobile","components":{"v":{"media_playlist":"../hls/mobile/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":7266,"h":"3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/tv.json b/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/tv.json new file mode 100644 index 000000000..06accccb6 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-incomplete-dist/stages/tv.json @@ -0,0 +1 @@ +{"class":"tv","components":{"v":{"media_playlist":"../hls/tv/wai-v.m3u8","pieces":[{"dur":50,"p":0,"stages":[{"bytes":26030,"h":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27","layer":0},{"bytes":67779,"deadline_ms":1500,"h":"7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec","layer":1,"refines":"cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27"}],"start":0},{"dur":25,"p":1,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":50},{"dur":40,"p":2,"stages":[{"bytes":7266,"h":"3f0d5ef876822b9294b86dbab0a5ed7a68026623192adb7725face30faf40b65","layer":0}],"start":75},{"dur":25,"p":3,"stages":[{"bytes":9698,"h":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61","layer":0},{"bytes":30699,"deadline_ms":1500,"h":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b","layer":1,"refines":"2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61"},{"bytes":40303,"deadline_ms":1500,"h":"f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9","layer":2,"refines":"656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b"}],"start":115},{"dur":60,"p":4,"stages":[{"bytes":31610,"h":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9","layer":0},{"bytes":81407,"deadline_ms":800,"h":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d","layer":1,"refines":"9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9"},{"bytes":101295,"deadline_ms":800,"h":"a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87","layer":2,"refines":"98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d"}],"start":140}]}},"policy_digest":"1bed669917d9d78e1a0f65a5a998db50e98e3dbdf836cc2ae00ff2ac97d67e57"} diff --git a/wai/pack-conformance/staged-mutations/still-manifest-target/LISTING b/wai/pack-conformance/staged-mutations/still-manifest-target/LISTING new file mode 100644 index 000000000..db79dee1b --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-manifest-target/LISTING @@ -0,0 +1,41 @@ +f9359ffd69743b04bc491f9fe32e6bac50a3f2e6c9ec9e747b0f16572bea0dec dash/classic.mpd +e3f5ca3e780c57e10b71b30b644dd5c488f85cc11cb12557c75307ea6590d657 dash/mobile.mpd +825126074444a4419069e844ae4a0b9dcb02a2ef5830168e4e77967de9df710a dash/tv.mpd +312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2 floor/v/312da579d4bf042888e3ee044b8346360fad3032cdb8cacb7fcf19d7a26c3ba2.m4s +39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768 floor/v/39f61548f8f65c68612e02ed11803c3477ed293bc1be12608be69ffd652de768.m4s +470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa floor/v/470bbd4e4bc7d8c7a14adafdf50ae5c256fb5086b13389099cf0babf164986fa.m4s +c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362 floor/v/c3470c0a3624d3e458b8fe12a0ff68df725d27cf7768984d6f6aca0149dd2362.m4s +ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b floor/v/ca9a1818ed7001e0ef0543bf3864b4ad937362fa89b834f4838349bcb789fc5b.m4s +58c598174eed554cd0d1bc8abedca8c9d51d992c8e3b963bcb7574da4983ed9f floor/v/init.mp4 +b3a3be45bb384a06e744c8c2293ba59e16738791abe3ac90b262c2db9a0c5744 hls/classic/floor-v.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/classic/master.m3u8 +586a9ebcc3a05b8497658933f27caca010ff6bd74c885960a4bab04c2406879c hls/legacy/master.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/mobile/master.m3u8 +e2e2d0c4e02660e13ce06dd3359b18bed8e75b63f28db819ddad61f59af210cf hls/mobile/wai-v.m3u8 +adce889f610eaa20b77909693de01dfae787518748223b0774cd56004e0c9353 hls/mobile/wai.m3u8 +44a2f73da356e854f8fd05a564ad30ef05aff81a2f26fb316757a07acb79bf3c hls/tv/master.m3u8 +6d8b1dcae5b236c0e51e914121eb1334dc9228f2aad15fc995281cbe707fe5dc hls/tv/wai-v.m3u8 +fc9db3d8e327724c8a7ee793d4b1f1949cf95df1c1941bfcc2bf539fe4184e77 hls/tv/wai.m3u8 +8bb0cd3e9d6010987c3df90b5de437f31d460084ff1ed56175b4f2a7165a69f9 index-v.wsix +0387c4128de60831575723b868864c82f9d8d129aacec6ad8797db5ca5e5acf4 moq/catalog.json +fc3c7e6fc88dae24c824d2a0f14af72e86bbb5aa53c4ad6c0d40c9733fa1358b objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai +8cedefd8d5f0ae91768d7107a0a86b07580526f329339e5384c7e710f6c7edbf objects/3522115bc258246c4dd67879e2b9c10f3be41cbc88aec1254e1d26e9d87b4423.wai +588689b9e92b610b20d5f08ce6762adb6d32ac5221fffa3232882434050ad59d objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai +f6876fbfe12e3a3c164efa2ce9d65e2bc066ce5bb2bba16f285de485c9fddb78 objects/66c03382125a5a72828ca5223599007817784a12129e431066290f8ff5df562e.wai +84fcac3182c4dc25b84001bc58e2fa140ae3b8117a42fa4897af0e7ff7a1e920 objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai +22c6777c989d288a6a89876062a244194fde54f5b091b0f603c4ef11f1bc4f60 objects/93439520366affbd9ec0a51d57120d0e45a6b4651884ece2eaf5b8954c188038.wai +9a9052e3dba5e5a10979317d2c8b349cec2d5ee6f59cf8d256add7d6b5be6cbd objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai +83657a16f8686cb03f1da0b5fe3a60ad24b32e1b3f24c17e5b9e5e54720571be objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai +38725131c01f75141fe1790d40209348da7f45241df0bf82a794e8c8196a0fb6 objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai +f9ce66461527b6c1493336ca950908511fe0414fc3d836aff6c045a015090851 objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai +e191fd2fe53e15c30c964e355340cbfbb44562078d6c18c8321c9d3539c43423 objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai +59f1b4eae347ec3f0647ccefa738701dcce32ee83a5fa1c0aca29c3a27c7858d objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai +2f5da0ff307fd4a466607d2ef35ac4fc7215353d70f592c875cafe77c7a2fcc7 objects/d955b5f2232046829b6a5da0c436412c91e97b7d0c16afb07e5f6e68a39ef66c.wai +bf7bb63e1e3f03967f8da44a5700f4ded079e6d4d9a6c0ff9503c458269c486f objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai +38570bad3ed03466bc76b5e8165f395cfe87705bee6a23d51fcaf691f59c8bdb package.receipt.json +8454596c213291b896c4483017406c58076e27ad0cab4fab54b850841dfa1a29 plan-v.txt +11536aab05b3fe23aa428a52617da9d156f6326d42ee5b8785af4713b436ecf2 policy.json +61ad0d0ba75977d401ebaeb00b5a8f5795f3b7a1408018fcbb02038f2c412e16 policy.toml +703346f293954ae90516c7cf1a8616e6851f3dba3ca3f51967b388493b1736ff stages/legacy.json +d9019a7cdd0c4c256a6eb7913de7621264a412f4684a583583e16cc3a451e8b0 stages/mobile.json +9fcd13984d1e9d8771c1641facfce7c6a615b4c9831251cfb153b60e64a7ab55 stages/tv.json diff --git a/wai/pack-conformance/staged-mutations/still-manifest-target/hls/mobile/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-manifest-target/hls/mobile/wai-v.m3u8 new file mode 100644 index 000000000..1e48f3049 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-manifest-target/hls/mobile/wai-v.m3u8 @@ -0,0 +1,20 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai#n=980 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-manifest-target/hls/tv/wai-v.m3u8 b/wai/pack-conformance/staged-mutations/still-manifest-target/hls/tv/wai-v.m3u8 new file mode 100644 index 000000000..826a9c737 --- /dev/null +++ b/wai/pack-conformance/staged-mutations/still-manifest-target/hls/tv/wai-v.m3u8 @@ -0,0 +1,25 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXTINF:2.000, +#WAI-LAYER:N=1,URI="../../objects/7aeabafc9c50d289b5f65a7480a88774f94be9eec5b706c16835a59bbe59a0ec.wai#n=67779" +../../objects/cf015505a36b0b76b50c41f15f3a0210163886054d53d35f1c4d414bf1c22d27.wai#n=26030 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXT-X-GAP +#EXTINF:1.600, +../../objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai#n=980 +#EXTINF:1.000, +#WAI-LAYER:N=1,URI="../../objects/656e5f72c8b8165d9f59f30a6cbca465383cc3d478b6474e2a91e592796f320b.wai#n=30699" +#WAI-LAYER:N=2,URI="../../objects/f5cacd6f092d317d0972e3d8ed4efcf4702b820bd5295aebdd252dd8dbc409e9.wai#n=40303" +../../objects/2b95867d323012d663b0f8c88fc8d8d0c3b5642a9784f00d30ce2ab176341c61.wai#n=9698 +#EXTINF:2.400, +#WAI-LAYER:N=1,URI="../../objects/98bb5bbdb41e89f39288a59eb469b152e55b175983e46521ed2bb40999fc511d.wai#n=81407" +#WAI-LAYER:N=2,URI="../../objects/a59afe3368e48b835e3f0c170d8fc42b87fce91bb8567f7e8a9b091db0c51b87.wai#n=101295" +../../objects/9fb263069ffb6a50052e79baeeee7c5814106011d57be9c4f6396c5dbd9902a9.wai#n=31610 +#EXT-X-ENDLIST diff --git a/wai/pack-conformance/staged-mutations/still-manifest-target/index-v.wsix b/wai/pack-conformance/staged-mutations/still-manifest-target/index-v.wsix new file mode 100644 index 0000000000000000000000000000000000000000..8bcca441fcb598219a40c5e56983175bd7712e7a GIT binary patch literal 2792 zcmWF!_KaX;U|^60Vi3&=#6m#K0mRHeEd4fZrufb4eNz0XOIOag9q_WxcJB|fGg=S$ zKdqU5tu7p>ge1_uWqIfy%aqP14%;lstlX~Fd(S(qRpPuc^p^SdxfxSMa2aL@l-p)3s`4e}Ec10y4{f?}vVNIw@4voN6Q?}5tO0BJt3JOd-h z9+(;D8ADkYXLFZr<#GHNZ^a}aX3@s#8+j zf!`+Eyaijoo{@ijW*)P?Ld1Wk7N=D`MOTkm8!Xm8_8+Ws8&vF2h61ieXPJR0NH;B7}>M!o`q^1PP}2@ zH{W5e_r)7_uVz0M@tCyZ_ko;}Ey@TV+0(J^psJs&+RGc& zF3OyoVC`@M6#i_W&<8~V5dfqf1TI6Z)q*$&u2XwzTdk3S(6!_ZKThbJ0@OU115)$ii%#qii=7|&nJZ_{l2i+u!&fS{x%+8Nd^)Kz--!El zIja32yOHC+=3M>t>R&qcK#nB-96-~2)xt8HLl zc&YP)uBkrKX_pgt8IP~ryqIPoKaZ`p3kcfiWuP_JI{i2WPp|Y3vmYM+Y(R$)0YK_O09O8?r~mnzlG*0}&I(}FtK9YOT}_0D zK!V`a>X%2pS?0vAWqO8-t!ki$^v;NmYES zy@hk;pFr9FwvdDivmaE4!^%GeV4%poTVOWniPm9ftuM7tg=)(E5B#xqOAZT5rDl1N!Sr$$qymH4 zpU{PB|77Q>3T9dV_s%=8R5o0Qm(O+CmQ;zBH;-O@-7(ciqQOEMl;D5>=6_K5lav2p z@o$4xpEANrGKf9E_7#H{k`sYmMB)KCOrWgK2!tS){0GuyObjJuYz(>iNtrpRYz#T6 z>50jeA`In;nR;cJDXIB-nRzAgx%nlT`FZG~MX70-d8xceY7@&0A>7Q|#Pn3Xg1mHy Gvlsxx!me5X literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/staged-mutations/still-manifest-target/objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai b/wai/pack-conformance/staged-mutations/still-manifest-target/objects/953df17a7ac1be8ab159a1472dacbc716b2b6b563be5ac275fdf8625459150cb.wai new file mode 100644 index 0000000000000000000000000000000000000000..b70f47bbf35cab7b1703fe301cae66a87cd187e2 GIT binary patch literal 980 zcmWG{^fX+@z`#(gRGyfrWTj-NXP~5`l$)B8nF!>SWu~O&1NoVGC8>EOK!Kvvf}G6c z#FA8yqWqN9ocN;D!qUv5)LgK9wNi3oL1I#7PG(6ZP&Lp2AeAm6H5DLXSS1*i@xTas9mo(i<3S}Cn4F*mhX$;!k)M=3+e%GgXtsa(m* z%tS}2LdnXowpNOnfuS?N&z+Y`iVNs?UQZ8~ARz4k#3~$2Kyq8&>Z1${Ob978G? zlcyMefBN9T0~RJdKR*NBl!h4`X5ul70%2ichjunQ+AdyH$`BxRgjws+6NW$sGw}fC z3A|4gH*0HYEm(7K;k>}Jb|NPjHZ`1bWYA&$s5pVO#o?-hT8C@ZhYLWx+q_je*h5@e z$~}4ItX8C7`QE{dp_KKT-W=%83xH%eE zHTno}KZ{l1KOq_NN;3CwQO9?^4px&AgO%weJz5Nx926Dg->7m(?=EkYS@2PTL4?&v z@C{?|`4>|0Od(uJi>d@?@_yG7m@H!B;FJBeC+_4_2et{EABAAfZG9CB*VY(v{>A)2 zZm_1_H?cv@?R>8!n~P^R_;gHfXma2P;Z13ZkhsUnxIkkx5fq^O0fk>QA(%PYz3W$V~JHE11ZQn0kS#~SF+a7r%XYftZHGlob9!vz8Y zkG|^x1FC@4!Q#Oy$>`_#jH?*9n|}P*%l(9bGv!;u>hw1;TbMlr( zHp%Zl**b8YU=?E7%dIluCQ#(@lB?{EA_78>V|Oqq$f&%MT%K>i_JpbONQL-JTaPQo z=k8T;olw8D8<=`PDM0T=rQ8YSOS=ocJqSF%qvXEmqrKco6NPLP#Coq9ud}IXu6WNh z(;+EfrTkoZh9C#CRZ=NtYp?d7<7c?J0vI|a0sIWDg*Q|M?^?A0(>#NxtDnm{r-UW| D2~u Vec { let (h, wd) = (target["h"].as_u64().expect("h") as u32, target["w"].as_u64().expect("w") as u32); replace_object(&mut f, k, objects::base_envelope("video", "wai.image.png", w.payload.clone(), units as u64, h, wd)); add("still-at-cap", "floor-object-mismatch", sealed(f)); + // A base's and a refinement's manifest past §5.2 and §5.4: a still whose target holds + // one member more, and a top layer whose target is not null (`object-manifest`). + let mut f = g.clone(); + let x = golden_index(&f); + let k = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").expect("the still"); + let mut w = Wai::from_bytes(&f[&objects::object_path(&x.entries[k].content_hash)]).expect("an envelope"); + w.manifest.target["x"] = json!(1); + replace_object(&mut f, k, w.to_bytes().expect("a manifest serialises")); + add("still-manifest-target", "object-manifest", sealed(f)); + let mut f = g.clone(); + let x = golden_index(&f); + let k = top(&x, last); + let mut w = Wai::from_bytes(&f[&objects::object_path(&x.entries[k].content_hash)]).expect("an envelope"); + w.manifest.target = json!({}); + replace_object(&mut f, k, w.to_bytes().expect("a manifest serialises")); + add("refinement-manifest-target", "object-manifest", sealed(f)); + // The still as a PNG §5.7 does not read: its one dynamic block's lone distance code is + // two bits long, an incomplete code (`prefix-digest`). + let mut f = g.clone(); + let x = golden_index(&f); + let k = x.entries.iter().position(|e| x.capability(e) == "wai.image.png").expect("the still"); + let w = Wai::from_bytes(&f[&objects::object_path(&x.entries[k].content_hash)]).expect("an envelope"); + let (pw, ph, rgb) = crate::png::decode(&w.payload).expect("a still"); + let mut raw = Vec::new(); + for row in rgb.chunks_exact(3 * pw as usize) { + raw.push(0); + raw.extend_from_slice(row); + } + let mut png = vec![0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; + crate::png::chunk(&mut png, b"IHDR", &[&pw.to_be_bytes()[..], &ph.to_be_bytes(), &[8, 2, 0, 0, 0]].concat()); + crate::png::chunk(&mut png, b"IDAT", &[&[0x78u8, 0x01][..], &dynamic_literals(&raw, 2)].concat()); + crate::png::chunk(&mut png, b"IEND", &[]); + assert!(crate::png::decode(&png).is_err()); + let target = &w.manifest.target; + let frames = target["frames"].as_u64().expect("frames"); + replace_object(&mut f, k, objects::base_envelope("video", "wai.image.png", png, frames, ph, pw)); + add("still-incomplete-dist", "prefix-digest", sealed(f)); // The still's prefix digest of its one frame, not of the frame held for its piece. let mut f = g.clone(); let mut x = golden_index(&f); diff --git a/wai/wai-pack/src/verify.rs b/wai/wai-pack/src/verify.rs index 296a958b8..2e3b12a71 100644 --- a/wai/wai-pack/src/verify.rs +++ b/wai/wai-pack/src/verify.rs @@ -24,7 +24,10 @@ //! verifies against them (§9.1's reasons, the index rules among them). //! 6. Per component: the index against the policy (`index-policy-mismatch`); the plan //! file (`plan-mismatch`); every object (`object-missing`, the object rules, -//! `object-lengths`); every decode-equivalent prefix (`prefix-digest`). Then, only when +//! `object-lengths`); each floor's unit count, before any frame is decoded +//! (`floor-object-mismatch`); every base and refinement in entry order, decoded or +//! applied (`prefix-digest`) and its manifest §5.2's or §5.4's (`object-manifest`); +//! every decode-equivalent prefix (`prefix-digest`). Then, only when //! all of that holds, the floor (`floor-object-mismatch`, `floor-motif-mismatch`, //! `floor-av1-malformed`, `floor-keyframe-mismatch`, `floor-cmaf-mismatch`) and the //! catalogs (`catalog-mismatch`). @@ -367,7 +370,7 @@ fn verify_component( return Err(vec![r]); } if errs.is_empty() { - match prefix_digests(ix, &objects) { + match prefix_digests(comp.media.label(), ix, &objects) { Ok(d) => dims = d, Err(mut v) => errs.append(&mut v), } @@ -407,13 +410,26 @@ fn decode_digest(cap: &str, env: &[u8], n: u32) -> Result<([u8; 32], u32, u32), } } -/// Every decode-equivalent prefix decoded and compared with its `prefix_digest` (§6.5): -/// per piece, its base by its capability's decode, then its refinements applied in layer -/// order by the reference sink (`wai::staged`), under the reference tools' cost limit and -/// chain budget, each checked after it applies. The frame size of the first base. -fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Result, Vec> { +/// An object's manifest bytes: the `ml` bytes after the magic and the manifest length. +fn manifest_bytes(env: &[u8]) -> Option<&[u8]> { + let ml = u32::from_le_bytes(env.get(4..8)?.try_into().ok()?) as usize; + env.get(8..8 + ml) +} + +/// Every base and refinement, in entry order (§11.3), then every decode-equivalent prefix +/// against its `prefix_digest` (§6.5). A base decodes by its capability (a still is §5.7's +/// PNG, its one frame held for the piece), or it is `prefix-digest`; its manifest is the +/// one §5.2 states for its piece's frames and its frame size (`object-manifest`). A +/// refinement's manifest is §5.4's (`object-manifest`), and it applies, in layer order, +/// to every frame of the stage below it by the reference sink (`wai::staged`) under the +/// reference tools' cost limit and chain budget, or it is `prefix-digest`. A base that +/// does not decode, or a layer that does not apply, ends the pass. Returns the frame size +/// of the first base. +fn prefix_digests(media: &str, ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Result, Vec> { let mut errs = Vec::new(); let mut dims = None; + // Each entry's digest, for the comparisons after the pass. + let mut digests: Vec<(usize, [u8; 32])> = Vec::new(); for p in 0..ix.pieces.len() { let first = ix.pieces[p].first_entry as usize; let es = ix.piece_entries(p); @@ -421,18 +437,22 @@ fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Res let base = &es[bi]; let bytes = &objects[&base.content_hash]; let at = |k: usize| format!("entry {}", first + k); - match decode_digest(ix.capability(base), bytes, ix.pieces[p].duration) { - Ok((digest, w, h)) => { - dims.get_or_insert((w, h)); - if base.prefix_tier == wsix::TIER_DECODE && digest != base.prefix_digest { - errs.push(reason("prefix-digest", format!("{}: the decode is not its prefix digest", at(bi)))); - } + let cap = ix.capability(base); + let (digest, w, h) = match decode_digest(cap, bytes, ix.pieces[p].duration) { + Ok(d) => d, + Err(why) => { + errs.push(reason("prefix-digest", format!("{}: the base does not decode: {why}", at(bi)))); + return Err(errs); } - Err(why) if base.prefix_tier == wsix::TIER_DECODE => errs.push(reason("prefix-digest", format!("{}: {why}", at(bi)))), - Err(_) => {} + }; + dims.get_or_insert((w, h)); + let want = crate::objects::base_envelope(media, cap, Vec::new(), ix.pieces[p].duration as u64, h, w); + if manifest_bytes(bytes) != manifest_bytes(&want) { + errs.push(reason("object-manifest", format!("{}: the base's manifest is not §5.2's", at(bi)))); } + digests.push((first + bi, digest)); let layers: Vec<(usize, &wsix::EntryRec)> = es.iter().enumerate().filter(|(_, e)| e.role == Role::Refinement).collect(); - if !layers.iter().any(|(_, e)| e.prefix_tier == wsix::TIER_DECODE) { + if layers.is_empty() { continue; } let limit = wai::staged::TOOL_DEFAULT_LIMIT; @@ -446,19 +466,29 @@ fn prefix_digests(ix: &StageIndex, objects: &BTreeMap<[u8; 32], Vec>) -> Res }), Err(e) => { errs.push(reason("prefix-digest", format!("{}: the base its layers refine does not decode for the sink: {e}", at(bi)))); - continue; + return Err(errs); } }; for (k, e) in layers { - let applied = chain.offer(&objects[&e.content_hash], 0, wai::staged::ReceiptEvidence::None); + let below = ix.entries.get(e.refines as usize).map(|x| x.content_hash).unwrap_or_default(); + let want = crate::objects::refinement_envelope(media, ix.capability(e), Vec::new(), &base.content_hash, &below, e.layer); + let obj = &objects[&e.content_hash]; + if manifest_bytes(obj) != manifest_bytes(&want) { + errs.push(reason("object-manifest", format!("{}: the refinement's manifest is not §5.4's", at(k)))); + } + let applied = chain.offer(obj, 0, wai::staged::ReceiptEvidence::None); let ok = applied.as_ref().is_ok_and(|r| r.iter().all(|x| x.envelope.is_none() && x.applied.len() as u32 == chain.n_units())); if !ok { errs.push(reason("prefix-digest", format!("{}: the layer does not apply to every frame of the stage below it", at(k)))); - break; - } - if e.prefix_tier == wsix::TIER_DECODE && chain.clip_sha256() != Some(e.prefix_digest) { - errs.push(reason("prefix-digest", format!("{}: the prefix does not decode to its prefix digest", at(k)))); + return Err(errs); } + digests.push((first + k, chain.clip_sha256().unwrap_or_default())); + } + } + for (k, digest) in digests { + let e = &ix.entries[k]; + if e.prefix_tier == wsix::TIER_DECODE && digest != e.prefix_digest { + errs.push(reason("prefix-digest", format!("entry {k}: the prefix does not decode to its prefix digest"))); } } if errs.is_empty() { Ok(dims) } else { Err(errs) } -- 2.54.0 (Apple Git-157) From e6a16c0408881c0db1a3c02b9926d473a4e90f0c Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 15:28:03 -0400 Subject: [PATCH 17/21] wai-pack live keeps no record per file: listing lines on disk, open objects only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LiveOut held every object's path, hex SHA-256 and first piece for the whole session. It now holds: - no listing record per file. Each file's listing line is appended to a hidden .LISTING.lines beside the package as the file is written. At the end the lines are read back, sorted into LISTING, and the lines file is removed; that is the one moment it holds a line per file. - no set of written objects. An object already written is known by its file on disk. - for the abandon path, only the objects the unsealed pieces published, as a piece number and a 32-byte content hash. Those of a window are dropped when the window is sealed. packaging §9.2 and the CLI help now say exactly what remains: - what no window has sealed yet: those pieces' entry fields and their objects' hashes, which with --window 0 means every piece's until the end; - the frames of the piece being cut; - eight bytes per piece for the plan. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/packaging.md | 21 ++++--- wai/wai-pack/src/bin/wai_pack.rs | 95 +++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 41 deletions(-) diff --git a/wai/extensions/packaging.md b/wai/extensions/packaging.md index fb0526342..287b0aae7 100644 --- a/wai/extensions/packaging.md +++ b/wai/extensions/packaging.md @@ -973,14 +973,19 @@ and layer *k* `layer_lag_ms[k]` after it (§8.6, §8.9). It writes as it goes: each object when it is published, and each window's index and then its receipt when the window is sealed, every file whole (written beside its place -and renamed into it); the plan file and `LISTING` at the end of the source. An object's -bytes are not held once written. Until its window is sealed a piece is held as its -entries' fields, so with one window over the whole source (the default) every piece's -are held until the end; besides those it holds the frames of the piece being cut and -each piece's first frame, for the plan file. A source that fails before its end (a short -frame, a frame of another size) leaves every sealed window and the objects it lists, -removes the objects only unsealed pieces published, writes no plan file or `LISTING`, -and is an error that names what was kept. +and renamed into it); the plan file and `LISTING` at the end of the source. It keeps no +record per file of the session: each file's listing line goes to a hidden file beside +the package as the file is written, and is sorted into `LISTING` at the end (the one +moment it holds a line per file); an object already written is known by its file; an +object's bytes are not held once written. What it holds is what no window has sealed +yet: those pieces' entry fields and the content hash of each object they published +first. So with windows of `n` pieces that is at most `n` pieces', and with one window +over the whole source (the default) every piece's until the end. Besides those it holds +the frames of the piece being cut and each piece's first frame, eight bytes a piece, for +the plan file. A source that fails before its end (a short frame, a frame of another +size) leaves every sealed window and the objects it lists, removes the objects only +unsealed pieces published, writes no plan file or `LISTING`, and is an error that names +what was kept. **9.3** A version-1 receipt (`wai.video.package`'s segment map) keeps its bytes and still verifies. Its JSON form is RFC 8785 canonical with `version` 1: `asset_id`, diff --git a/wai/wai-pack/src/bin/wai_pack.rs b/wai/wai-pack/src/bin/wai_pack.rs index 4caf201ed..a0db30b7e 100644 --- a/wai/wai-pack/src/bin/wai_pack.rs +++ b/wai/wai-pack/src/bin/wai_pack.rs @@ -25,14 +25,16 @@ //! then; each window of pieces written when it is sealed, as //! windows//index-.wsix and then windows//package.receipt.json, chained to //! the one before; at the end of the stream the last window, plan-.txt and -//! LISTING. Every file is written whole (a temporary file renamed into place). An -//! object's bytes are not held once written; with --window 0 (the default) the one -//! window is sealed at the end, so until then every piece's entry fields are held -//! (packaging §9.2). A source that fails mid-stream (a short frame, a frame of -//! another size) keeps every sealed window and the objects it lists, removes the -//! objects only unsealed pieces published, writes no plan file or LISTING, and -//! exits 1 saying what it kept. No floor: a supplied floor is the on-demand -//! package's (§7.3).

is new or empty +//! LISTING. Every file is written whole (a temporary file renamed into place), and its +//! listing line to a hidden file beside the package, sorted into LISTING at the end. +//! An object's bytes are not held once written; what is held is what no window has +//! sealed yet (those pieces' entry fields and their objects' content hashes), so with +//! --window 0 (the default), one window sealed at the end, every piece's until then, +//! and each piece's first frame for the plan (packaging §9.2). A source that fails +//! mid-stream (a short frame, a frame of another size) keeps every sealed window and +//! the objects it lists, removes the objects only unsealed pieces published, writes +//! no plan file or LISTING, and exits 1 saying what it kept. No floor: a supplied +//! floor is the on-demand package's (§7.3). is new or empty //! wai-pack verify //! verify a package (packaging §9): every reason, or its counts and pack digest //! wai-pack listing [--primary] @@ -234,22 +236,36 @@ fn cmd_listing(args: &[String]) -> Result<(), String> { std::io::stdout().write_all(&l).map_err(|e| e.to_string()) } -/// What `wai-pack live` has written: each file by its listing path with its SHA-256 (the -/// listing is built from these at the end), each object's first piece, and the pieces the -/// windows written so far hold. +/// What `wai-pack live` has written. It holds no record per file of the session: each +/// file's listing line goes to a hidden file beside the package as the file is written +/// (sorted into `LISTING` at the end), an object already written is known by its file, +/// and only the objects the pieces no window has sealed yet published are held, by +/// content hash and piece, for a session that ends in an error to remove. struct LiveOut<'a> { dir: &'a Path, comp: String, - listed: std::collections::BTreeMap, - first_piece: std::collections::BTreeMap, + lines: std::fs::File, + open: Vec<(u64, [u8; 32])>, + objects: u64, windows: u64, sealed_pieces: u64, } +/// The hidden file the listing's lines are written to as the files are. +const LIVE_LINES: &str = ".LISTING.lines"; + impl LiveOut<'_> { + fn new<'a>(dir: &'a Path, comp: String) -> Result, String> { + std::fs::create_dir_all(dir).map_err(|e| format!("{}: {e}", dir.display()))?; + let at = dir.join(LIVE_LINES); + let lines = std::fs::File::create(&at).map_err(|e| format!("{}: {e}", at.display()))?; + Ok(LiveOut { dir, comp, lines, open: Vec::new(), objects: 0, windows: 0, sealed_pieces: 0 }) + } + /// Write a file of the package whole or not at all: its bytes to a hidden temporary - /// file beside it, then renamed into place. + /// file beside it, then renamed into place; then its listing line. fn write(&mut self, path: &str, bytes: &[u8]) -> Result<(), String> { + use std::io::Write as _; let at = self.dir.join(path); let parent = at.parent().ok_or_else(|| format!("{path}: no directory"))?; std::fs::create_dir_all(parent).map_err(|e| format!("{}: {e}", parent.display()))?; @@ -257,8 +273,7 @@ impl LiveOut<'_> { let tmp = parent.join(format!(".{name}.tmp")); std::fs::write(&tmp, bytes).map_err(|e| format!("{}: {e}", tmp.display()))?; std::fs::rename(&tmp, &at).map_err(|e| format!("{}: {e}", at.display()))?; - self.listed.insert(path.to_string(), wai::pin::sha256_hex(bytes)); - Ok(()) + writeln!(self.lines, "{} {path}", wai::pin::sha256_hex(bytes)).map_err(|e| format!("{LIVE_LINES}: {e}")) } /// Each object as it is published; one already written (a repeated piece's) once. @@ -266,15 +281,17 @@ impl LiveOut<'_> { for x in published { println!("piece {} layer {} at frame {}: {}", x.piece, x.layer, x.at_frame, hex(&x.hash)); let path = wai_pack::objects::object_path(&x.hash); - if !self.listed.contains_key(&path) { + if !self.dir.join(&path).exists() { self.write(&path, &x.envelope)?; + self.open.push((x.piece, x.hash)); + self.objects += 1; } - self.first_piece.entry(path).or_insert(x.piece); } Ok(()) } - /// Each window as it is sealed: its index, then its receipt. + /// Each window as it is sealed: its index, then its receipt. The objects of the + /// pieces it holds are no longer open. fn seal(&mut self, windows: Vec) -> Result<(), String> { for w in windows { let n = w.number; @@ -283,21 +300,42 @@ impl LiveOut<'_> { println!("window {n}: pieces {} to {}, receipt {}", w.first_piece, w.first_piece + w.pieces - 1, hex(&w.receipt.receipt_hash())); self.windows = n + 1; self.sealed_pieces = w.first_piece + w.pieces; + let through = self.sealed_pieces; + self.open.retain(|(p, _)| *p >= through); } Ok(()) } + /// The end of the source: the plan file, then `LISTING` from the lines written, sorted + /// by path (the one moment a line per file is held), and the lines' file removed. + fn finish(&mut self, starts: &[u64]) -> Result<(), String> { + use std::io::Write as _; + self.write(&format!("plan-{}.txt", self.comp), wai_pack::pack::plan_file(starts).as_bytes())?; + self.lines.flush().map_err(|e| format!("{LIVE_LINES}: {e}"))?; + let at = self.dir.join(LIVE_LINES); + let text = std::fs::read_to_string(&at).map_err(|e| format!("{}: {e}", at.display()))?; + let mut digests = std::collections::BTreeMap::new(); + for l in text.lines() { + let (d, p) = l.split_once(" ").ok_or_else(|| format!("{LIVE_LINES}: a line that is not a listing line"))?; + digests.insert(p.to_string(), d.to_string()); + } + let listing = wai::pin::listing_from_sha256(&digests).map_err(|e| e.to_string())?; + self.write("LISTING", &listing)?; + std::fs::remove_file(&at).map_err(|e| format!("{}: {e}", at.display())) + } + /// The session ended in `why`: every sealed window and the objects they list are /// kept; the objects only unsealed pieces published are removed, and no plan file or /// LISTING is written. The error says what was kept. fn abandon(&mut self, why: String) -> String { let mut removed = 0; - for (path, &p) in &self.first_piece { - if p >= self.sealed_pieces && std::fs::remove_file(self.dir.join(path)).is_ok() { + for (_, h) in &self.open { + if std::fs::remove_file(self.dir.join(wai_pack::objects::object_path(h))).is_ok() { removed += 1; } } - let kept = self.first_piece.values().filter(|&&p| p < self.sealed_pieces).count(); + let _ = std::fs::remove_file(self.dir.join(LIVE_LINES)); + let kept = self.objects - removed; let windows = match self.windows { 0 => "no window".to_string(), n => format!("windows 0 to {} (pieces 0 to {}) with the {kept} objects they list", n - 1, self.sealed_pieces - 1), @@ -323,14 +361,7 @@ fn cmd_live(args: &[String]) -> Result<(), String> { { return Err("live: pack-source-rate: the source's frame rate is not the component's timebase".into()); } - let mut w = LiveOut { - dir: Path::new(out), - comp: lp.policy().components[0].id.clone(), - listed: Default::default(), - first_piece: Default::default(), - windows: 0, - sealed_pieces: 0, - }; + let mut w = LiveOut::new(Path::new(out), lp.policy().components[0].id.clone())?; let mut frames = || -> Result<(), String> { while let Some(f) = r.next_frame() { let f = f.map_err(|e| e.to_string())?; @@ -346,9 +377,7 @@ fn cmd_live(args: &[String]) -> Result<(), String> { let (p, windows, starts) = lp.finish().map_err(|e| w.abandon(format!("live: {e}")))?; w.publish(p).map_err(|e| w.abandon(format!("live: {e}")))?; w.seal(windows).map_err(|e| w.abandon(format!("live: {e}")))?; - w.write(&format!("plan-{}.txt", w.comp), wai_pack::pack::plan_file(&starts).as_bytes())?; - let listing = wai::pin::listing_from_sha256(&w.listed).map_err(|e| e.to_string())?; - w.write("LISTING", &listing) + w.finish(&starts) } fn cmd_sim(args: &[String]) -> Result<(), String> { -- 2.54.0 (Apple Git-157) From 42bfce677ffa1f891bea04ebfa4696dedc7c60fc Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 15:39:55 -0400 Subject: [PATCH 18/21] ci: wai-pack's test-count floors at its counts (lib 34, staged 10) Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-pack.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wai-pack.yml b/.github/workflows/wai-pack.yml index 2ad39bf8f..64edd1143 100644 --- a/.github/workflows/wai-pack.yml +++ b/.github/workflows/wai-pack.yml @@ -141,14 +141,14 @@ jobs: echo "$target: $n tests (floor $floor)" [ "$n" -ge "$floor" ] || { echo "::error::$target lists $n tests, under its floor of $floor"; fail=1; } done <<'EOF' - --lib 33 + --lib 34 --test=conformance 1 --test=packs 7 --test=review 9 --test=refusals 5 --test=scenecut 3 --test=source_alloc 1 - --test=staged 8 + --test=staged 10 EOF exit $fail -- 2.54.0 (Apple Git-157) From af0572a73b53ffccb7948f5cf9808074fb5e2e9f Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 15:40:48 -0400 Subject: [PATCH 19/21] =?UTF-8?q?changelog:=20W4-4=20round=203=20=E2=80=94?= =?UTF-8?q?=20zlib's=20code-set=20rules,=20the=20longest=20piece,=20manife?= =?UTF-8?q?sts=20in=20both=20verifiers,=20live=20bookkeeping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- wai/CHANGELOG.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index 00f234e89..7aa244a17 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -34,16 +34,25 @@ wire format may change and such changes are recorded here. byte for byte. `wai-pack listing --primary` lists what the two share. It writes as it goes: each object when it is published and each window when it is sealed, every file whole, and the plan file and `LISTING` at the end. It does not keep an object's bytes - once written. A source that fails mid-stream keeps every sealed window and the objects + once written, or a record per file: each listing line goes to a hidden file and is + sorted into `LISTING` at the end. A source that fails mid-stream keeps every sealed window and the objects it lists, removes the rest, and exits 1 saying what it kept. `wai::pin::listing_from_sha256` writes a listing from per-file digests. -- **Stills read by PNG's rules** (§5.7). Both reference readers take any PNG of 8-bit RGB +- **Stills read by PNG's rules** (§5.7). Both reference readers take a PNG of 8-bit RGB without interlace and agree on what that admits. They read a suggested palette, ancillary chunks and split image data. They refuse an interlace, bytes after `IEND` or after the zlib stream, a short row, a zlib window past 32 KiB, and every other - chunk fault (`stills/`). A verifier refuses a piece longer than the policy's - `max_ms` before it decodes a frame. It hashes a still's frame held for its piece - without repeating the frame, so a declared duration never sets what it allocates. + chunk fault (`stills/`). Both inflate as the zlib library does: no over-subscribed + Huffman code, and no incomplete one but a lone one-bit code. wai-pack's reader has + its own inflater, and `png_diff.py` runs it against `pack_verify.py`'s zlib. +- **The longest piece** (§3.4, §4.4). `max_ms` is at most ten minutes, and at most + `2^24` units on every component's timebase (`policy-piece-bounds`). A verifier + refuses a piece longer than the policy's `max_ms` before it decodes a frame. It + checks each floor object's unit count before decoding (§11.3). It hashes a still's + frame held for its piece without repeating the frame. +- **Manifests checked by both verifiers** (§11.3). A base's manifest must be §5.2's and a + refinement's §5.4's (`object-manifest`, a new reason). A base that does not decode + is `prefix-digest`. - **A repeated object over HTTP** ([http-delivery](extensions/http-delivery.md) §4.2). A repeated object contributes once, with its layers, and a layer joins every occurrence of its base. `http_verify.py` and `wai-rs` now agree on a playlist's -- 2.54.0 (Apple Git-157) From 9ab59f2c9696fab40747da929813085e1f1857ba Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Fri, 9 Oct 2026 16:03:42 -0400 Subject: [PATCH 20/21] wai-pack: the inflater's bit reader aligns, by name (clippy's conversion rule) Co-Authored-By: Claude Opus 5.5 --- wai/wai-pack/src/png.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wai/wai-pack/src/png.rs b/wai/wai-pack/src/png.rs index 67ab20250..82c6f6338 100644 --- a/wai/wai-pack/src/png.rs +++ b/wai/wai-pack/src/png.rs @@ -367,7 +367,7 @@ impl BitReader<'_> { Ok(v) } - fn to_byte(&mut self) { + fn align(&mut self) { if self.bit != 0 { self.bit = 0; self.at += 1; @@ -494,7 +494,7 @@ pub fn inflate_zlib(z: &[u8], limit: usize) -> Result, String> { let last = r.bit()?; match r.bits(2)? { 0 => { - r.to_byte(); + r.align(); let n = z.get(r.at..r.at + 4).ok_or("the zlib stream ends early")?; let (len, nlen) = (u16::from_le_bytes([n[0], n[1]]), u16::from_le_bytes([n[2], n[3]])); if len != !nlen { @@ -555,7 +555,7 @@ pub fn inflate_zlib(z: &[u8], limit: usize) -> Result, String> { break; } } - r.to_byte(); + r.align(); let sum = z.get(r.at..r.at + 4).ok_or("the zlib stream ends before its Adler-32")?; if u32::from_be_bytes(sum.try_into().expect("4")) != adler32(&out) { return Err("the zlib stream: its Adler-32".into()); -- 2.54.0 (Apple Git-157) From 409b1c1fb22c68b0cf7daa3ec326723f8e4595e4 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Sat, 10 Oct 2026 01:21:31 -0400 Subject: [PATCH 21/21] http falsification: union-no-layer against this branch's described() The union-no-layer patch was written against the described() that this branch rewrote in round 2 (a repeated object described once, a layer joining every occurrence of its base), so CI could not apply it ("patch failed: codecs.rs:326"). It is regenerated from the branch's codecs.rs, still a plain patch file: the layer joins no alt. Its must-fail list adds the two cases that pin the repeated-object rule: union_O10_repeated_layer_after_first and union_O10_repeated_layer_after_second. With the patch applied, verify fails all three and two multivariant playlists. The receipt-surrogates mutation is left to #200's sharded runner, which owns it. Co-Authored-By: Claude Opus 5.5 --- .../falsify/union-no-layer.patch | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/wai/http-conformance/falsify/union-no-layer.patch b/wai/http-conformance/falsify/union-no-layer.patch index fd3392a45..f4ceffa70 100644 --- a/wai/http-conformance/falsify/union-no-layer.patch +++ b/wai/http-conformance/falsify/union-no-layer.patch @@ -1,15 +1,15 @@ -# must fail: union_O10_layers +# must fail: union_O10_layers union_O10_repeated_layer_after_first union_O10_repeated_layer_after_second # a layer joins no alt (codecs::described) diff --git a/wai/wai-rs/src/http_binding/codecs.rs b/wai/wai-rs/src/http_binding/codecs.rs -index 530ae822..161ed997 100644 +index a4d35e39..821b7f51 100644 --- a/wai/wai-rs/src/http_binding/codecs.rs +++ b/wai/wai-rs/src/http_binding/codecs.rs -@@ -326,7 +326,7 @@ fn described(objects: &[SeqObject]) -> Result, CodecsError> { - let el = els[k].as_mut().expect("a base is not a layer"); - let alt = el.alts.iter_mut().find(|a| a.cap == base_cap) - .ok_or_else(|| CodecsError::LayerUnbound(format!("wai.{cap}: its base's capability is not dispatched")))?; -- push_unique(&mut alt.plus, cap); -+ let _ = (alt, cap); +@@ -334,7 +334,7 @@ fn described(objects: &[SeqObject]) -> Result, CodecsError> { + let el = els[k].as_mut().expect("a base is not a layer"); + let alt = el.alts.iter_mut().find(|a| a.cap == base_cap) + .ok_or_else(|| CodecsError::LayerUnbound(format!("wai.{cap}: its base's capability is not dispatched")))?; +- push_unique(&mut alt.plus, cap.clone()); ++ let _ = (alt, &cap); + } } Ok(els.into_iter().flatten().collect()) - } -- 2.54.0 (Apple Git-157)

#K*d9de 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 U&@(zYwRq@v)n=tEZitwbTFg`_VtKcxGW~ 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 Bj0yk% literal 0 HcmV?d00001 diff --git a/wai/pack-conformance/packs/video-staged-01/package/objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai b/wai/pack-conformance/packs/video-staged-01/package/objects/a411541dd2eb555c9fd5b6dfe0cd00baae6574568225afa839e36af62f6bbdae.wai new file mode 100644 index 0000000000000000000000000000000000000000..ea3e576f4bf7db3a59837b50e59a975fc38fd661 GIT binary patch literal 1980 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