tor-browser

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

MediaRecorder-canvas-media-source-legacy.https.html (5276B)


      1 <!doctype html>
      2 <html>
      3 <meta name="timeout" content="long">
      4 
      5 <head>
      6  <title>MediaRecorder canvas media source</title>
      7  <meta name=variant content="?mimeType=''">
      8  <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
      9  <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
     10  <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
     11  <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
     12  <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
     13  <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
     14  <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
     15  <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
     16  <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
     17  <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
     18  <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
     19  <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
     20  <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
     21  <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
     22  <meta name=variant content="?mimeType=video/mp4">
     23  <link rel="help"
     24        href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#dom-mediarecorder-mimeType">
     25  <script src="/resources/testharness.js"></script>
     26  <script src="/resources/testharnessreport.js"></script>
     27  <script src="/resources/testdriver.js"></script>
     28  <script src="/resources/testdriver-vendor.js"></script>
     29  <script src="../../mediacapture-streams/permission-helper.js"></script>
     30 </head>
     31 
     32 <body>
     33  <canvas id="canvas"></canvas>
     34  <script>
     35 
     36 async_test(test => {
     37  const CANVAS_WIDTH = 256;
     38  const CANVAS_HEIGHT = 144;
     39 
     40  let large_size_data_available = false;
     41 
     42  // Empty video frames from this resolution consistently have ~750 bytes in my
     43  // tests, while valid video frames usually contain 7-8KB. A threshold of
     44  // 1.5KB consistently fails when video frames are empty but passes when video
     45  // frames are non-empty.
     46  const THRESHOLD_FOR_EMPTY_FRAMES = 1500;
     47 
     48  const CAMERA_CONSTRAINTS = {
     49    video: {
     50      width: { ideal: CANVAS_WIDTH },
     51      height: { ideal: CANVAS_HEIGHT }
     52    }
     53  };
     54 
     55  function useUserMedia(constraints) {
     56    let activeStream = null;
     57 
     58    function startCamera() {
     59      return navigator.mediaDevices.getUserMedia(constraints).then(
     60        (stream) => {
     61          activeStream = stream;
     62          return stream;
     63        }
     64      );
     65    }
     66 
     67    function stopCamera() {
     68      activeStream?.getTracks().forEach((track) => track.stop());
     69    }
     70 
     71    return { startCamera, stopCamera };
     72  }
     73 
     74  function useMediaRecorder(stream, mimeType, frameSizeCallback) {
     75    const mediaRecorder = new MediaRecorder(
     76      stream, { mimeType }
     77    );
     78 
     79    mediaRecorder.ondataavailable = event => {
     80      const {size} = event.data;
     81      frameSizeCallback(size);
     82 
     83      if (mediaRecorder.state !== "inactive") {
     84        mediaRecorder.stop();
     85      }
     86    };
     87 
     88    mediaRecorder.onstop = event => {
     89      assert_equals(large_size_data_available, true),
     90          "onstop is called after valid data is available";
     91    };
     92 
     93    mediaRecorder.start(1000);
     94  }
     95 
     96  const params = new URLSearchParams(window.location.search);
     97  const mimeType = params.get('mimeType');
     98  if (mimeType && !MediaRecorder.isTypeSupported(mimeType)) {
     99    test.done();
    100    return;
    101  }
    102 
    103  if (!window.MediaStreamTrackProcessor) {
    104    test.done();
    105    return;
    106  }
    107 
    108  const canvas = document.querySelector("canvas");
    109  const ctx = canvas.getContext("2d", {
    110    alpha: false,
    111  });
    112 
    113  canvas.width = CANVAS_WIDTH;
    114  canvas.height = CANVAS_HEIGHT;
    115 
    116  const {startCamera, stopCamera} = useUserMedia(CAMERA_CONSTRAINTS);
    117  startCamera().then(async stream => {
    118    const videoTrack = stream.getVideoTracks()[0];
    119    const { readable: readableStream } = new MediaStreamTrackProcessor({
    120      track: videoTrack
    121    });
    122 
    123    const composedTrackGenerator = new MediaStreamTrackGenerator({
    124      kind: "video"
    125    });
    126    const sink = composedTrackGenerator.writable;
    127 
    128    ctx.fillStyle = "#333";
    129    ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    130 
    131    const transformer = new TransformStream({
    132      async transform(cameraFrame, controller) {
    133        if (cameraFrame && cameraFrame?.codedWidth > 0) {
    134          const leftPos = (CANVAS_WIDTH - cameraFrame.displayWidth) / 2;
    135          const topPos = (CANVAS_HEIGHT - cameraFrame.displayHeight) / 2;
    136 
    137          ctx.drawImage(cameraFrame, leftPos, topPos);
    138 
    139          const newFrame = new VideoFrame(canvas, {
    140            timestamp: cameraFrame.timestamp
    141          });
    142          cameraFrame.close();
    143          controller.enqueue(newFrame);
    144        }
    145      }
    146    });
    147 
    148    readableStream.pipeThrough(transformer).pipeTo(sink);
    149 
    150    const compositedMediaStream = new MediaStream([composedTrackGenerator]);
    151 
    152    useMediaRecorder(compositedMediaStream, mimeType, (size => {
    153      if (size > THRESHOLD_FOR_EMPTY_FRAMES) {
    154        large_size_data_available = true;
    155        stopCamera();
    156        test.done();
    157      }
    158    }));
    159  });
    160 }, "MediaRecorder returns frames containing video content");
    161 
    162  </script>
    163 </body>
    164 
    165 </html>