tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }