//! The floor-only profile (packaging §7.8): a component of floor rungs alone, with no //! integer base and no source. The goldens `video-floor-only-01` (the CMAF form) and //! `video-floor-only-02` (the OBU form) carry the ladder's two floors and nothing else; //! their pieces are the first floor rung's; and what the profile refuses is refused by //! name. use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use wai::stage_index::{Role, StageIndex, PIECE_KIND_NONE}; 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 policy(g: &str) -> String { String::from_utf8(std::fs::read(corpus().join(format!("packs/{g}/policy.toml"))).unwrap()).unwrap() } /// Pack `policy` over the supplied floor `floor` (its listing pinned), with no source. fn pack(policy: &str, floor: Files, base_from: Option) -> 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); o.base_from = base_from.map(wai_pack::files::FileSet::Memory); wai_pack::pack::pack(policy.as_bytes(), &BTreeMap::new(), &mut o) } fn code(r: Result) -> String { r.map(|_| "ok".to_string()).unwrap_or_else(|e| e.code) } /// Each piece is its two floors, presenting no base (no derived_from), of kind none; the /// pieces are the first floor's segments or key frames, which are the ladder golden's /// plan; the floor objects hold the same units as the ladder golden's; no class lists a /// WAI object, so there is no WAI playlist, and the MoQ catalog is the floors' tracks. #[test] fn a_floor_only_package_is_its_floors_alone() { let ladder = held(&corpus().join("packs/video-ladder-01/package")); let lix = StageIndex::parse(&ladder["index-v.wsix"]).unwrap(); for g in ["video-floor-only-01", "video-floor-only-02"] { let pkg = held(&corpus().join(format!("packs/{g}/package"))); wai_pack::verify::verify_files(&pkg).unwrap(); let ix = StageIndex::parse(&pkg["index-v.wsix"]).unwrap(); assert_eq!(ix.pieces.iter().map(|p| (p.start, p.duration)).collect::>(), lix.pieces.iter().map(|p| (p.start, p.duration)).collect::>(), "{g}: the ladder's plan"); for (p, piece) in ix.pieces.iter().enumerate() { assert_eq!((piece.kind, piece.flags), (PIECE_KIND_NONE, 0)); let es = ix.piece_entries(p); assert_eq!(es.iter().map(|e| e.role).collect::>(), vec![Role::Floor, Role::Floor], "{g} piece {p}"); for (k, e) in es.iter().enumerate() { assert_eq!(e.flags & wai::stage_index::ENTRY_DERIVED_FROM, 0); let w = wai::container::Wai::from_bytes(&pkg[&wai_pack::objects::object_path(&e.content_hash)]).unwrap(); assert!(w.manifest.derived_from.is_none()); let lw = wai::container::Wai::from_bytes(&ladder[&wai_pack::objects::object_path(&lix.piece_entries(p)[1 + k].content_hash)]).unwrap(); assert_eq!(w.payload, lw.payload, "{g} piece {p}: the ladder golden's units"); } } assert!(!pkg.keys().any(|k| k.ends_with("/wai.m3u8") || k.starts_with("dash/all")), "{g}: no WAI view"); let moq: serde_json::Value = serde_json::from_slice(&pkg["moq/catalog.json"]).unwrap(); let names: Vec<&str> = moq["tracks"].as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); assert_eq!(names, vec!["hi/floor", "lo/floor"]); } } /// §3.4 and §7.8's refusals. #[test] fn what_the_floor_only_profile_refuses() { let p = policy("video-floor-only-02"); let fl = held(&corpus().join("packs/video-floor-only-02/floor")); assert_eq!(code(pack(&p, fl.clone(), None)), "ok"); let base_rung = "\n[[ladder]]\nid = \"b0\"\ncomponent = \"v\"\ncapability = \"wai.video.int_motion\"\nrole = \"base\"\n\n[floor]"; assert_eq!(code(pack(&p.replacen("\n[floor]", base_rung, 1), fl.clone(), None)), "policy-floor-only", "a base rung"); assert_eq!(code(pack(&p.replace("profile = \"floor-only\"", "profile = \"floor-only\"\nsource = \"v.y4m\""), fl.clone(), None)), "policy-floor-only", "a source"); assert_eq!(code(pack(&p.replace("profile = \"floor-only\"", "profile = \"other\""), fl.clone(), None)), "policy-value"); assert_eq!(code(pack(&p.replace("profile = \"floor-only\"\n", ""), fl.clone(), None)), "policy-no-base", "the standard profile needs a base"); // A rule's ladder is every floor rung, and only them. let rule = |l: &str| format!("{p}\n[[rule]]\nid = \"r\"\nthen = {{ ladder = [{l}] }}\n"); assert_eq!(code(pack(&rule("\"hi\", \"lo\""), fl.clone(), None)), "ok"); assert_eq!(code(pack(&rule("\"hi\""), fl.clone(), None)), "policy-rule-ladder"); // A piece past max_ms: the first floor's first piece is 60 frames, 2.4 s. assert_eq!(code(pack(&p.replace("max_ms = 4000", "max_ms = 2000\nmin_ms = 1000\ntarget_ms = 2000"), fl.clone(), None)), "floor-piece-length"); // The first floor rung decides the plan: one that opens on an inter frame has none. The // stream opens on frame 1, its sequence header put after its delimiter (a first unit // without one is refused before, floor-av1-malformed). let tus = wai_pack::av1::temporal_units(&fl["hi.obu"]).unwrap(); let seq = tus[0].sequence_header().unwrap().bytes.to_vec(); let hi: Vec> = tus.iter().map(|t| t.bytes()).collect(); let mut opened = hi[1..].to_vec(); opened[0] = [&opened[0][..2], &seq[..], &opened[0][2..]].concat(); let mut off = fl.clone(); off.insert("hi.obu".into(), opened.concat()); assert_eq!(code(pack(&p, off, None)), "floor-keyframe-mismatch"); let mut cut = fl.clone(); cut.insert("hi.obu".into(), hi[1..].concat()); assert_eq!(code(pack(&p, cut, None)), "floor-av1-malformed", "a first unit without a sequence header"); // The other floor rungs are held to it. let mut short = fl.clone(); let lo: Vec> = wai_pack::av1::temporal_units(&fl["lo.obu"]).unwrap().iter().map(|t| t.bytes()).collect(); short.insert("lo.obu".into(), lo[..lo.len() - 1].concat()); assert_eq!(code(pack(&p, short, None)), "floor-frame-count"); // Nothing to re-pack: a floor-only component has no base. assert_eq!(code(pack(&p, fl, Some(held(&corpus().join("packs/video-base-01/package"))))), "repack-parent-mismatch"); }