Search code examples
javascriptcanvasweb-workermedia-sourcemediastream

Is there a way to send video data from a video tag/MediaStream to an OffscreenCanvas?


Basically I want to be able to perform effectively this same code:


const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

const draw = () => {
  context.drawImage(video, 0, 0);
  requestAnimationFrame(draw);
}

video.onplay = () => {
  requestAnimationFrame(draw);
}

only using an offscreen canvas. I can send images over messages to the worker the offscreen canvas is on, but not video as it's directly tied to an HTMLElement. Is there currently a way to somehow still render video data or a MediaStream in an offscreen canvas?


Solution

  • You can send frames of a video to an OffscreenCanvas in a Web Worker by modifying your script with the following changes:

    const worker = new Worker('my-worker.js');
    const video = document.getElementById('video');
    const stream = video.captureStream();
    const [track] = stream.getVideoTracks();
    const imageCapture = new ImageCapture(track);
    const canvas = document.getElementById('canvas');
    const offscreen = canvas.transferControlToOffscreen();
    
    worker.postMessage({ offscreen }, [offscreen]);
    
    const draw = () => {
      imageCapture.grabFrame().then(imageBitmap => {
        worker.postMessage({ imageBitmap }, [imageBitmap]);
      });
    
      requestAnimationFrame(draw);
    };
    
    video.onplay = () => {
      requestAnimationFrame(draw);
    };
    

    my-worker.js

    let canvas;
    let context;
    
    addEventListener('message', event => {
      if (event.data.offscreen) {
        canvas = event.data.offscreen;
        context = canvas.getContext('2d');
      } else if (event.data.imageBitmap && context) {
        context.drawImage(event.data.imageBitmap, 0, 0);
        // do something with frame
      }
    });
    

    References