wai.live.ads: server-guided ad insertion for rooms — cues to HLS interstitials and DASH alternative presentations, asset lists, and the break form of wai.video.ssai (L11) #360
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/live-ads"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
Slice L11 of the live program: server-guided ad insertion (SGAI) for rooms. It adds the record
wai.live.adsand giveswai.video.ssaia second receipt form, the break form. Manifests stay shared and cacheable, and each viewer's decision happens behind the asset list.wai/extensions/live-ads.md(new):WAP1, signed by the room authority;cueevents, with the post-roll annotationWAB1;draft-pantos-hls-rfc8216bis-22Appendix D and Appendix F);wai.video.ssaireceipt;wai-rs:live_ads(featurelive_ads = ["live_room", "video_ssai"]) with unit tests, a corpus generator, thewai_live_ads_vectorsbin andtests/live_ads.rs;SsaiBreakReceiptinwai_video_ssai.rs, with its JSON carriage, its rules (check) and its tests;energy_figure.rs, under the existing profilewai:video-ssai-receipt-id.live-conformance/ads/:NAME.m3u8;live-conformance/live_ads_verify.py: the second implementation, written from the text alone. It shares no code with wai-rs and runs aspython3 -I.wai-conformance.yml(lib tests forlive_ads::andwai_video_ssai::,--test live_ads, then the Python verifier), andlive-ads.mdadded topaths:.REGISTRY.mdand the export (wai.live.ads, plus the magicsWAP1andWAB1among the wire identifiers).wai.video.ssairow in SPEC §5 now names the two forms. Also a CHANGELOG entry and a cross-reference in live-events §1.Design decisions and why
wai.live.ads) besidewai.video.ssai, rather than folded into it.wai.video.ssai's, in a second receipt form. One decision rule and one spend rule then serve stitched and server-guided insertion alike.duration_ms, or the policy'sopen_avail_mswhen the cue states none. With neither, it is refusedbreak_duration_unknown.cue_idand the same author. Its duration is ⌊(t_in − t_out)/1000⌋ ms. It is refusedcue_in_unmatched,break_zero_durationorcue_duration_mismatchwhen the in's stated duration is not 0 and disagrees.break_zero_duration.break_overlap.metaannotationWAB1at the samet. This is the same pattern as WMS1 and WTX1. Any cue after it is refusedcue_after_close.IDiswai-ad-followed by the opening cue's digest in hex. It is distinct from live-events' own date range of that cue, whoseIDis the bare digest. The DASH event id is the low 53 bits of the digest's first eight bytes, so a player that holds it as a double holds it exactly.START-DATEis floored to the millisecond, as live-events §8.3 does. Durations are written asS.mmm. DASH uses timescale 1000 withpresentationTime = pt0 + ⌊t/1000⌋.CUEisONCEfor a mid-roll andPOST,ONCEfor a post-roll.X-RESUME-OFFSETis the break's duration, so the viewer returns where the program resumes.ID. It addsDURATION,X-RESUME-OFFSETandX-PLAYOUT-LIMIT, as §6.2.1 has a server signal the end of a range with a planned duration.t0, so every edge writes the same bytes. §6.2.4 requires identical date ranges across Media Playlists, and this satisfies it.<asset_base><ID>.json?_HLS_interstitial_id=<ID>(Appendix D.4).<asset_base><ID>.mpd.type="list",ImportedMPD).decide, unchanged. A viewer who joins a break while it runs is filled with the avail minus the offset (_HLS_start_offset). A room has no pre-roll.wai:video-ssai-break-receipt\x01, and shares the identity domain. So the energy-measurement §6.1 profilewai:video-ssai-receipt-idcovers both forms, the same way package v2 shares its identity domain."form": 2. Energy evidence uses energy-measurement §2.7's snake_case names, and the legacy labelHwShuntis refused.funds_exceeded).t. Its receipts must name it exactly as live-rooms §4.5 describes.cueis refused by the room itself (kind_not_permitted). There is a vector for this.gapand only the gaps are stated, because a schedule read from part of the cues is not the room's.What I read and could not read
draft-pantos-hls-rfc8216bis-22(1 May 2026, the latest; -23 does not exist), as text: §4.4.5.1, §6.2.1, §6.2.4, Appendix D and Appendix F. Its Appendix H is a glossary. No revision from -18 to -22 has a "Date Range Schedules" appendix, so nothing maps there, and the spec says so.EventType,AlternativeMPDEventType,AlternativeMPDReplaceEventType,ImportedMpdType,PresentationType) and its example MPDs G23, G29-1 and G29-2, at the schema's revision of 2026-06-13.noJump,serviceDescriptionId,startWithOffsetandclipkeep their defaults. So the jump restriction andX-SNAPhave no DASH mapping in this revision.skipAfterwithout a skip control is written as the avail rounded up. I read that from the example's comment that a missingskipAftermeans a viewer may skip at once.returnOffsetis written as the break's duration, mirroringX-RESUME-OFFSET.EventStreamin the Period, the manifest form that stays shared. This revision defines no in-band (emsg) form of the alternative-presentation event.Checks (all green at head
29e7f07ab)Build-host runs used
~/kani-logs/remote/rb <worktree> -- '<cmd>', both before and after merging origin/main (which brought DVR #351 in).cargo test --locked --no-default-features --features live_ads --lib live_ads::cargo test --locked --no-default-features --features live_ads --lib wai_video_ssai::cargo test --locked --no-default-features --features live_ads --test live_adscargo run -q --locked --no-default-features --features live_ads --bin wai_live_ads_vectors -- verify ../live-conformance/adscargo test --locked --no-default-features --features video_ssai,receipt_claims --lib energy_figure::cargo test --locked --no-default-features --lib registrycargo test --locked --no-default-features --lib container::cargo test --locked --no-default-features --test cli_registrycargo run -q --locked --no-default-features --bin wai -- registry export --checkcargo test --locked --no-default-features --features neural_int,video_int,audio_int,world_int,splat_int --test int_kernels_vectors every_decoded_output_states_a_sha256cargo test --locked --no-default-features --features world,…,video_workflow,…,receipt_claims --lib --bins --test video_receiptcargo clippy --locked --no-default-features --features live_ads,receipt_claims --all-targetscargo build --lockedinwai/wai-web(usesvideo_workflowandreceipt_claims)cargo metadata --locked --offline --format-version 1(wai-rs)asset_formatiscbindgen:ignorepython3 -I live-conformance/live_ads_verify.py live-conformance/adslive_events_verify.py,live_rooms_verify.py,live_moderation_verify.pycheck-tests-in-ci.py,check-workflow-concurrency.sh,check-spec-refs.py,check-wai-section-refs.py,check-no-vanity-metrics.sh,test-public-text-checks.sh,check-comparative-prose.sh,check-orphan-sources.pyAbout the clippy row: the one note in a touched file is
wai_video_ssai.rs:211, acollapsible_ifin form 1'sverify. It is pre-existing and unchanged. Form 2's equivalent uses a let-chain.Falsifications
All were run after committing, one mutant at a time, each then restored.
Rust (on the build host,
cargo test --features live_ads --test live_ads every_case): each mutant exits 101 and names its vector.funds_exceededaccepteddecision_mismatchreachedasset_list_mismatchbreak_overlapremovedreceipt_duplicateremovedcue_after_closeremovedreceipt_format_threeacceptedwork_decide_opsrule removedreceipt_work_below_fillacceptedPython (verifier with one check removed): each exits 1 and names its vector.
funds_exceeded,asset_list_mismatch,break_overlap,cue_after_close,receipt_duplicateanddecision_mismatch.receipt_format_three,receipt_work_below_fill,receipt_figure_without_class,receipt_below_resolutionandreceipt_over_its_ceiling.Corpus mutations (17, all caught), in
live_ads_verify.py's own suite:POSTdropped from a tag;pt0;Public-text note
The HLS interstitial and preload date-range classes that Appendix D.2 and Appendix F define are protocol identifiers that contain a company name:
.md,.rsand.pyfiles, every line that holds them carriesallow-name: <reason>.NAME.m3u8tag files. That keeps them out ofvectors.json, because JSON cannot carry the opt-out.check-public-names.shlocally, since its denylist is a CI secret. CI runs it.Not done / open items
skipAfter,returnOffset,noJump(the jump restriction) and snapping. There is no in-bandemsgform.START-DATEpast year 9999 is written empty rather than refused (unreachable in practice).🤖 Generated with Claude Code