media-source-webcodecs-util.js (4719B)
1 const testIV = window.crypto.getRandomValues(new Uint8Array(16)); 2 const testKey = window.crypto.getRandomValues(new Uint8Array(16)); 3 const testKeyId = window.crypto.getRandomValues(new Uint8Array(8)); 4 var testEncodedKey = null; 5 6 const keySystemConfig = [{ 7 initDataTypes: ['keyids'], 8 videoCapabilities: [{contentType: 'video/mp4; codecs="vp09.00.10.08"'}] 9 }]; 10 11 // TODO(crbug.com/1144908): Consider extracting metadata into helper library 12 // shared with webcodecs tests. This metadata is adapted from 13 // webcodecs/video-decoder-any.js. 14 const vp9 = { 15 async buffer() { 16 return (await fetch('vp9.mp4')).arrayBuffer(); 17 }, 18 // Note, file might not actually be level 1. See original metadata in 19 // webcodecs test suite. 20 codec: 'vp09.00.10.08', 21 frames: [ 22 {offset: 44, size: 3315, type: 'key'}, 23 {offset: 3359, size: 203, type: 'delta'}, 24 {offset: 3562, size: 245, type: 'delta'}, 25 {offset: 3807, size: 172, type: 'delta'}, 26 {offset: 3979, size: 312, type: 'delta'}, 27 {offset: 4291, size: 170, type: 'delta'}, 28 {offset: 4461, size: 195, type: 'delta'}, 29 {offset: 4656, size: 181, type: 'delta'}, 30 {offset: 4837, size: 356, type: 'delta'}, 31 {offset: 5193, size: 159, type: 'delta'} 32 ] 33 }; 34 35 async function getOpenMediaSource(t) { 36 return new Promise(async resolve => { 37 const v = document.createElement('video'); 38 document.body.appendChild(v); 39 const mediaSource = new MediaSource(); 40 const url = URL.createObjectURL(mediaSource); 41 mediaSource.addEventListener( 42 'sourceopen', t.step_func(() => { 43 URL.revokeObjectURL(url); 44 assert_equals(mediaSource.readyState, 'open', 'MediaSource is open'); 45 resolve([v, mediaSource]); 46 }), 47 {once: true}); 48 v.src = url; 49 }); 50 } 51 52 async function setupEme(t, video) { 53 testEncodedKey = await crypto.subtle.importKey( 54 'raw', testKey.buffer, 'AES-CTR', false, ['encrypt', 'decrypt']); 55 56 var handler = new MessageHandler( 57 'org.w3.clearkey', {keys: [{kid: testKeyId, key: testKey}]}); 58 59 function handleMessage(event) { 60 handler.messagehandler(event.messageType, event.message).then(response => { 61 event.target.update(response).catch(e => { 62 assert_unreached('Failed to update session: ' + e); 63 }); 64 }); 65 } 66 67 return navigator 68 .requestMediaKeySystemAccess('org.w3.clearkey', keySystemConfig) 69 .then(keySystemAccess => { 70 return keySystemAccess.createMediaKeys(); 71 }) 72 .then(createdMediaKeys => { 73 return video.setMediaKeys(createdMediaKeys); 74 }) 75 .then(_ => { 76 let session = video.mediaKeys.createSession(); 77 session.addEventListener('message', handleMessage, false); 78 79 let encoder = new TextEncoder(); 80 let initData = encoder.encode( 81 JSON.stringify({'kids': [base64urlEncode(testKeyId)]})); 82 session.generateRequest('keyids', initData).catch(e => { 83 assert_unreached('Failed to generate a license request: ' + e); 84 }); 85 }) 86 .catch(e => { 87 assert_unreached('Failed to setup EME: ', e); 88 }); 89 } 90 91 async function runEncryptedChunksTest(t) { 92 let buffer = await vp9.buffer(); 93 let [videoElement, mediaSource] = await getOpenMediaSource(t); 94 95 // Makes early prototype demo playback easier to control manually. 96 videoElement.controls = true; 97 98 await setupEme(t, videoElement); 99 100 let sourceBuffer = mediaSource.addSourceBuffer( 101 {videoConfig: {codec: vp9.codec, encryptionScheme: 'cenc'}}); 102 let nextTimestamp = 0; 103 let frameDuration = 100 * 1000; // 100 milliseconds 104 // forEach with async callbacks makes it too easy to have uncaught rejections 105 // that don't fail this promise_test or even emit harness error. 106 // Iterating explicitly instead. 107 for (i = 0; i < vp9.frames.length; i++, nextTimestamp += frameDuration) { 108 let frameMetadata = vp9.frames[i]; 109 let frameData = 110 new Uint8Array(buffer, frameMetadata.offset, frameMetadata.size); 111 let encryptedFrameData = await window.crypto.subtle.encrypt( 112 {name: 'AES-CTR', counter: testIV, length: 128}, testEncodedKey, 113 frameData); 114 115 await sourceBuffer.appendEncodedChunks(new EncodedVideoChunk({ 116 type: frameMetadata.type, 117 timestamp: nextTimestamp, 118 duration: frameDuration, 119 data: encryptedFrameData, 120 decryptConfig: { 121 encryptionScheme: 'cenc', 122 keyId: testKeyId, 123 initializationVector: testIV, 124 subsampleLayout: [{clearBytes: 0, cypherBytes: frameMetadata.size}], 125 } 126 })); 127 } 128 129 mediaSource.endOfStream(); 130 131 return new Promise((resolve, reject) => { 132 videoElement.onended = resolve; 133 videoElement.onerror = reject; 134 videoElement.play(); 135 }); 136 }