Files
ladybird/Tests/LibWeb/Text/input/HTML/media-source-setup.html
2026-04-01 02:54:22 -05:00

142 lines
5.6 KiB
HTML

<!DOCTYPE html>
<video id="video"></video>
<script src="../include.js"></script>
<script>
asyncTest(done => {
const TIMEOUT_MS = 1000;
let timer;
function restartTimeout() {
clearTimeout(timer);
timer = setTimeout(() => {
println("FAIL: timeout");
done();
}, TIMEOUT_MS);
}
restartTimeout();
function step(name) {
println(`PASS: ${name}`);
restartTimeout();
}
function fail(name, error) {
println(`FAIL: ${name}: ${error}`);
clearTimeout(timer);
done();
}
try {
const video = document.getElementById("video");
// Step 1: Check isTypeSupported
const mimeType = 'video/webm;codecs="vp9,opus"';
if (!MediaSource.isTypeSupported(mimeType)) {
fail("isTypeSupported", `${mimeType} not supported`);
return;
}
step("isTypeSupported");
// Step 2: Create MediaSource
const mediaSource = new MediaSource();
step("created MediaSource");
// Step 3: Create object URL and assign to video
const url = URL.createObjectURL(mediaSource);
video.src = url;
step("assigned object URL to video.src");
// Step 4: Wait for sourceopen
mediaSource.addEventListener("sourceopen", async () => {
try {
step("sourceopen fired");
// Step 5: Add a SourceBuffer
const sourceBuffer = mediaSource.addSourceBuffer(mimeType);
step("addSourceBuffer succeeded");
// Step 6: Fetch test media
const response = await fetch("../../../Assets/test-webm.webm");
if (!response.ok) {
fail("fetch media", `HTTP ${response.status}`);
return;
}
const data = await response.arrayBuffer();
step(`loaded media data (${data.byteLength} bytes)`);
// Step 7: Append buffer in slices and print buffered ranges
function printBuffered(label) {
const buffered = sourceBuffer.buffered;
if (!buffered) {
println(`buffered after ${label}: not available`);
return;
}
const ranges = [];
for (let i = 0; i < buffered.length; i++)
ranges.push(`${buffered.start(i)}-${buffered.end(i)}`);
println(`buffered after ${label}: [${ranges.join(", ")}]`);
}
async function appendSlice(slice, label) {
await new Promise((resolve, reject) => {
sourceBuffer.addEventListener("updateend", resolve, { once: true });
sourceBuffer.addEventListener("error", () =>
reject(new Error("error event on SourceBuffer")), { once: true });
sourceBuffer.appendBuffer(slice);
step(`appendBuffer() for ${label}`);
});
printBuffered(label);
}
// Embed the data in a padded buffer and append as typed array views, so that if the view slice is
// ignored by appendBuffer(), it will cause read errors. YouTube uses these typed array views.
const padding = 1024;
const paddedBuffer = new ArrayBuffer(padding + data.byteLength + padding);
new Uint8Array(paddedBuffer).set(new Uint8Array(data), padding);
const dataView = new Uint8Array(paddedBuffer, padding, data.byteLength);
const numSlices = 3;
const sliceSize = Math.ceil(dataView.length / numSlices);
let pos = 0;
for (let i = 0; i < numSlices; i++) {
const end = Math.min(pos + sliceSize, dataView.length);
await appendSlice(dataView.subarray(pos, end), `slice ${i + 1}/${numSlices}`);
step(`slice ${i + 1} appended`);
pos = end;
}
// Step 8: End of stream and wait for sourceended
await new Promise(resolve => {
mediaSource.addEventListener("sourceended", resolve);
mediaSource.endOfStream();
});
step("endOfStream succeeded");
printBuffered("endOfStream");
// Step 9: Play
video.addEventListener("error", () => {
fail("playback", video.error?.message ?? "unknown error");
});
await video.play();
step("play() promise resolved");
await new Promise(resolve => {
if (!video.paused)
resolve();
else
video.addEventListener("playing", resolve);
});
step("playing");
clearTimeout(timer);
done();
} catch (e) {
fail("sourceopen handler", e);
}
});
} catch (e) {
fail("setup", e);
}
});
</script>