mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 09:45:06 +02:00
142 lines
5.6 KiB
HTML
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>
|