//! Several floor rungs in one package (packaging §7.1): the golden `video-ladder-01` of //! `wai/pack-conformance` carries two AV1 floors over one integer base, which is //! `video-base-01`'s, object for object; the catalogs list both floors; floor names that //! cannot name a file, and each floor's streams, are refused by name. use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use wai::stage_index::{Role, StageIndex}; use wai_pack::golden; type Files = BTreeMap>; fn corpus() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("../pack-conformance") } fn held(p: &Path) -> Files { wai::pin::read_dir_set(p).unwrap() } fn ladder_package() -> Files { held(&corpus().join("packs/video-ladder-01/package")) } fn ladder_policy() -> String { String::from_utf8(std::fs::read(corpus().join("packs/video-ladder-01/policy.toml")).unwrap()).unwrap() } fn ladder_floor() -> Files { held(&corpus().join("packs/video-ladder-01/floor")) } /// Pack the ladder golden's fixture under `policy` (its listing pinned to `floor`'s). fn pack(policy: &str, floor: Files) -> Result { let listing = wai_pack::floor::listing_sha256(&floor).unwrap(); let at = policy.find("supplied_listing = \"").unwrap() + "supplied_listing = \"".len(); let policy = format!("{}{listing}{}", &policy[..at], &policy[at + 64..]); let mut o = wai_pack::pack::PackOpts::new(ed25519_dalek::SigningKey::from_bytes(&[0x5a; 32]), "did:key:test", floor); let mut src = BTreeMap::new(); src.insert("v".to_string(), wai_pack::fixture::generate("video-base-01").unwrap()); wai_pack::pack::pack(policy.as_bytes(), &src, &mut o) } fn code(r: Result) -> String { r.map(|_| "ok".to_string()).unwrap_or_else(|e| e.code) } /// One base per piece under two floors, in ladder order; the bases are `video-base-01`'s /// objects (the same fixture and base rung); each floor object presents its piece's base; /// the classical master lists one variant per floor, each its own size; and each floor's /// fragments hold every frame of its piece. #[test] fn a_ladder_is_one_base_under_its_floors() { let pkg = ladder_package(); wai_pack::verify::verify_files(&pkg).unwrap(); let ix = StageIndex::parse(&pkg["index-v.wsix"]).unwrap(); let base_pkg = held(&corpus().join("packs/video-base-01/package")); let base_ix = StageIndex::parse(&base_pkg["index-v.wsix"]).unwrap(); for p in 0..ix.pieces.len() { let es = ix.piece_entries(p); assert_eq!(es.iter().map(|e| e.role).collect::>(), vec![Role::Base, Role::Floor, Role::Floor], "piece {p}"); assert_eq!(es[0].content_hash, base_ix.piece_entries(p)[0].content_hash, "piece {p}: video-base-01's base"); assert_ne!(es[1].content_hash, es[2].content_hash, "two renditions"); for fe in &es[1..] { let w = wai::container::Wai::from_bytes(&pkg[&wai_pack::objects::object_path(&fe.content_hash)]).unwrap(); let df = wai::container::DerivedFrom::from_value(w.manifest.derived_from.as_ref().unwrap()).unwrap(); assert_eq!(df.motif, wai_pack::hex(&es[0].content_hash), "piece {p}: the floor presents the piece's base"); } } let master = String::from_utf8(pkg["hls/classic/master.m3u8"].clone()).unwrap(); let variants: Vec<&str> = master.lines().filter(|l| !l.starts_with('#')).collect(); assert_eq!(variants, vec!["../classic/floor-hi.m3u8", "../classic/floor-lo.m3u8"]); assert!(master.contains("RESOLUTION=64x36") && master.contains("RESOLUTION=32x18"), "{master}"); for c in ["tv", "mobile", "legacy"] { let m = String::from_utf8(pkg[&format!("hls/{c}/master.m3u8")].clone()).unwrap(); wai::http_binding::hls::classical_unchanged(&master, &m).unwrap(); } for name in ["hi", "lo"] { let pl = String::from_utf8(pkg[&format!("hls/classic/floor-{name}.m3u8")].clone()).unwrap(); let frags: Vec<&str> = pl.lines().filter(|l| !l.starts_with('#')).map(|l| l.trim_start_matches("../../")).collect(); assert_eq!(frags.len(), ix.pieces.len()); for (p, f) in frags.iter().enumerate() { assert!(f.starts_with(&format!("floor/{name}/")), "{f}"); let top = wai_pack::cmaf::boxes(&pkg[*f]).unwrap(); let traf = wai_pack::cmaf::boxes(top[0].1).unwrap().into_iter().find(|(k, _)| k == b"traf").unwrap().1; let trun = wai_pack::cmaf::boxes(traf).unwrap().into_iter().find(|(k, _)| k == b"trun").unwrap().1; assert_eq!(u32::from_be_bytes(trun[4..8].try_into().unwrap()), ix.pieces[p].duration); } // A WAI media playlist is adjacent to every floor's, which share the pieces' // durations. wai::http_binding::hls::check_adjacent(std::str::from_utf8(&pkg["hls/tv/wai-v.m3u8"]).unwrap(), &pl).unwrap(); } let dash = String::from_utf8(pkg["dash/classic.mpd"].clone()).unwrap(); assert!(dash.contains(" = moq["tracks"].as_array().unwrap().iter().filter(|t| t["packaging"] == "cmaf").map(|t| t["name"].as_str().unwrap()).collect(); assert_eq!(floors, vec!["hi/floor", "lo/floor"]); // The same inputs pack to the same bytes. assert_eq!(golden::pack_golden(&corpus(), "video-ladder-01").unwrap().files, pkg); } /// §7.1, §7.3, §11.1: with several floor rungs each stream is `.obu`, each is /// checked as one floor is, and a floor name that cannot name a file, or two equal in any /// case, is refused (`pack-id`); with one floor rung, the stream is the component's. #[test] fn each_floor_is_checked_and_named() { let p = ladder_policy(); let fl = ladder_floor(); assert_eq!(code(pack(&p, fl.clone())), "ok"); // A floor rung whose id names no file, and two that fold to one name. assert_eq!(code(pack(&p.replace("id = \"lo\"", "id = \"l o\""), fl.clone())), "pack-id"); assert_eq!(code(pack(&p.replace("id = \"lo\"", "id = \"HI\""), fl.clone())), "pack-id"); // One floor rung: its stream is the component's, v.obu. let one = p.replace("[[ladder]]\nid = \"lo\"\ncomponent = \"v\"\ncapability = \"wai.video.av1\"\nrole = \"floor\"\nparams = { source = \"supplied\" }\n\n", ""); assert_ne!(one, p); let only_hi: Files = [("hi.obu".to_string(), fl["hi.obu"].clone())].into(); assert_eq!(code(pack(&one, only_hi)), "floor-stream-missing"); let as_v: Files = [("v.obu".to_string(), fl["hi.obu"].clone())].into(); assert_eq!(code(pack(&one, as_v)), "ok"); // The second floor's stream: missing, a unit short, or a piece opening on an inter // frame. let mut no_lo = fl.clone(); no_lo.remove("lo.obu"); assert_eq!(code(pack(&p, no_lo)), "floor-stream-missing"); let lo: Vec> = wai_pack::av1::temporal_units(&fl["lo.obu"]).unwrap().iter().map(|t| t.bytes()).collect(); let mut short = fl.clone(); short.insert("lo.obu".into(), lo[..lo.len() - 1].concat()); assert_eq!(code(pack(&p, short)), "floor-frame-count"); let mut inter = lo.clone(); inter[85] = lo[86].clone(); let mut off = fl.clone(); off.insert("lo.obu".into(), inter.concat()); assert_eq!(code(pack(&p, off)), "floor-keyframe-mismatch"); } /// §11.3: a floor object at another floor rung's place is refused: piece 2's two floors /// swapped in the index, the package re-sealed, gives the first floor an object whose /// framing header is not its sequence header's size (`floor-object-mismatch`, which §11.3 /// checks before the units' sequence headers). #[test] fn a_floor_at_another_rung_s_place_is_refused() { let mut f = ladder_package(); let mut ix = StageIndex::parse(&f["index-v.wsix"]).unwrap(); let at = ix.pieces[2].first_entry as usize; ix.entries.swap(at + 1, at + 2); f.insert("index-v.wsix".into(), ix.to_bytes()); let key = ed25519_dalek::SigningKey::from_bytes(&golden_key()); golden::reseal(&mut f, &key).unwrap(); let got: Vec = wai_pack::verify::verify_files(&f).err().unwrap_or_default().into_iter().map(|r| r.code).collect(); assert_eq!(got, vec!["floor-object-mismatch"]); } fn golden_key() -> [u8; 32] { let t = std::fs::read_to_string(corpus().join("packs/video-ladder-01/key.hex")).unwrap(); let b = wai_pack::unhex(t.trim()).unwrap(); b.try_into().unwrap() }