e2e/run.sh builds the collector, installs the automation package and an open-source web player from e2e/package-lock.json (npm ci, no install scripts), runs the player integration's unit tests, then e2e/audit.e2e.mjs: an HLS stream made with the host's media tool (H.264 and AAC in 2 s CMAF segments, a program time, a marked ad over two segments) served from another origin with CMSD-Static exposed, played in a real browser twice, once through the loader adapter and once through the network tap, each session sent to a collector on loopback. It checks the outcome, the acknowledged objects against the origin's own SHA-256 of what it served, the ad's claim, the CMSD notes, the unmetered energy, the CMCD batches, the report, its recomputation by `wai-audit check`, a replay stored once and a tampered record refused. Three defects it found, fixed here: - the player hands fragment buffers on (to a worker), emptying them after the loader's callback: the session now copies the bytes at once, and keeps each object in the order it was given; - the tap read XMLHttpRequest responses after the player had moved them: it now listens from the request's construction, so it reads first; - a stitched ad is entered between two observations, so its claim began past media time 0 and reached quartile 1: play that enters an ad continuously now starts its first span at 0 (stream-audit §6.3), as leaving it already ran the last span to the ad's end. Co-Authored-By: Claude Opus 5.5 <[email protected]>
60 lines
2.4 KiB
HTML
60 lines
2.4 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Stream audit test page</title>
|
|
<style>
|
|
:root { color-scheme: light dark; }
|
|
body { margin: 0; padding: 16px; font: 14px/1.4 system-ui, sans-serif; background: Canvas; color: CanvasText; }
|
|
video { width: 640px; max-width: 100%; aspect-ratio: 16 / 9; background: #000; display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<video playsinline muted></video>
|
|
<pre id="state">starting</pre>
|
|
<script src="/vendor/player.js"></script>
|
|
<script type="module">
|
|
// The end-to-end page of stream audit: a web player plays a stream another origin serves, and the
|
|
// audit kit signs the session and sends it to the collector. `?mode=loader` audits through the
|
|
// player's loader class (web/adapter-hlsjs.js); `?mode=tap` through the network tap
|
|
// (web/adapter-tap.js), using nothing of the player's but what it fetches.
|
|
import { AuditSession, attach, CLASS, ROLE } from "/web/audit.js";
|
|
import { loaderAudit } from "/web/adapter-hlsjs.js";
|
|
import { tap } from "/web/adapter-tap.js";
|
|
|
|
const q = new URLSearchParams(location.search);
|
|
const video = document.querySelector("video");
|
|
const state = document.getElementById("state");
|
|
const Player = window.Hls; // allow-name: the global the open-source player's script defines
|
|
const session = await AuditSession.start({ channel: q.get("channel"), owner: q.get("owner"), collector: q.get("collector"), cmcdEveryMs: 2000 });
|
|
// A page has no meter: sink decode and display took part and were not measured (§8.1).
|
|
session.energy(ROLE.SINK_DECODE, 0, CLASS.UNMETERED);
|
|
session.energy(ROLE.DISPLAY, 0, CLASS.UNMETERED);
|
|
let programMs;
|
|
let player;
|
|
if (q.get("mode") === "tap") {
|
|
tap(session, { manifest: q.get("src") });
|
|
player = new Player();
|
|
programMs = () => session.programAt(video.currentTime);
|
|
} else {
|
|
const a = loaderAudit(Player, session);
|
|
player = new Player({ loader: a.Loader });
|
|
programMs = () => a.programMs(player);
|
|
}
|
|
attach(video, session, {
|
|
programMs,
|
|
onBundle: (bundle, answer) => {
|
|
window.auditResult = { bundle, answer, sid: session.sid };
|
|
state.textContent = `sent: ${answer.status}`;
|
|
},
|
|
});
|
|
player.loadSource(q.get("src"));
|
|
player.attachMedia(video);
|
|
video.muted = true;
|
|
video.play().catch((e) => (state.textContent = `play: ${e}`));
|
|
state.textContent = "playing";
|
|
window.auditSession = session;
|
|
</script>
|
|
</body>
|
|
</html>
|