| <!DOCTYPE html> |
| <html> |
| <title>Test basic encoded chunk buffering and playback with MediaSource</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script> |
| |
| setup(() => { |
| assert_implements( |
| SourceBuffer.prototype.hasOwnProperty('appendEncodedChunks'), |
| 'SourceBuffer prototype hasOwnProperty "appendEncodedChunks", used ' + |
| 'here to feature detect MSE-for-WebCodecs implementation.'); |
| }); |
| |
| // TODO(crbug.com/1144908): Consider extracting metadata into helper library |
| // shared with webcodecs tests. This metadata is adapted from webcodecs/video-decoder-any.js. |
| let vp9 = { |
| async buffer() { return (await fetch('vp9.mp4')).arrayBuffer(); }, |
| // Note, file might not actually be level 1. See original metadata in webcodecs test suite. |
| codec: "vp09.00.10.08", |
| frames: [{offset: 44, size: 3315, type: 'key'}, |
| {offset: 3359, size: 203, type: 'delta'}, |
| {offset: 3562, size: 245, type: 'delta'}, |
| {offset: 3807, size: 172, type: 'delta'}, |
| {offset: 3979, size: 312, type: 'delta'}, |
| {offset: 4291, size: 170, type: 'delta'}, |
| {offset: 4461, size: 195, type: 'delta'}, |
| {offset: 4656, size: 181, type: 'delta'}, |
| {offset: 4837, size: 356, type: 'delta'}, |
| {offset: 5193, size: 159, type: 'delta'}] |
| }; |
| |
| async function getOpenMediaSource(t) { |
| return new Promise(async resolve => { |
| const v = document.createElement('video'); |
| document.body.appendChild(v); |
| const mediaSource = new MediaSource(); |
| const url = URL.createObjectURL(mediaSource); |
| mediaSource.addEventListener('sourceopen', t.step_func(() => { |
| URL.revokeObjectURL(url); |
| assert_equals(mediaSource.readyState, 'open', 'MediaSource is open'); |
| resolve([ v, mediaSource ]); |
| }), { once: true }); |
| v.src = url; |
| }); |
| } |
| |
| promise_test(async t => { |
| let buffer = await vp9.buffer(); |
| let [ videoElement, mediaSource ] = await getOpenMediaSource(t); |
| videoElement.controls = true; // Makes early prototype demo playback easier to control manually. |
| let sourceBuffer = mediaSource.addSourceBuffer({ videoConfig: { codec: vp9.codec } }); |
| let next_timestamp = 0; |
| let frame_duration = 100 * 1000; // 100 milliseconds |
| // forEach with async callbacks makes it too easy to have uncaught rejections |
| // that don't fail this promise_test or even emit harness error. |
| // Iterating explicitly instead. |
| for (i = 0; i < vp9.frames.length; i++, next_timestamp += frame_duration) { |
| let frame_metadata = vp9.frames[i]; |
| await sourceBuffer.appendEncodedChunks(new EncodedVideoChunk( { |
| type: frame_metadata.type, |
| timestamp: next_timestamp, |
| duration: frame_duration, |
| data: new Uint8Array(buffer, frame_metadata.offset, frame_metadata.size) |
| })); |
| } |
| |
| mediaSource.endOfStream(); |
| |
| return new Promise( (resolve, reject) => { |
| videoElement.onended = resolve; |
| videoElement.onerror = reject; |
| videoElement.play(); |
| }); |
| |
| }, "Buffer EncodedVideoChunks (VP9) one-by-one and play them with MSE"); |
| |
| // TODO(crbug.com/1144908): More exhaustive tests (multiple sourcebuffers, |
| // varying append patterns, invalid append patterns; eventually more codecs, |
| // out-of-order DTS, durations, etc.) |
| |
| </script> |
| </html> |