mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-27 10:07:15 +02:00
Tests: Add a test to demonstrate MSE progress
This commit is contained in:
committed by
Gregory Bertilson
parent
cd2089eb9c
commit
59bc23f309
Notes:
github-actions[bot]
2026-04-01 07:57:05 +00:00
Author: https://github.com/Zaggy1024 Commit: https://github.com/LadybirdBrowser/ladybird/commit/59bc23f3099 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8655 Reviewed-by: https://github.com/tcl3
141
Tests/LibWeb/Text/input/HTML/media-source-setup.html
Normal file
141
Tests/LibWeb/Text/input/HTML/media-source-setup.html
Normal file
@@ -0,0 +1,141 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user