是否可以将流作为源添加到 html canvas 元素以及 html 视频元素?

Is it possible to add a stream as source to an html canvas element as to a html video element?

根据 MDN:

The HTMLMediaElement interface adds to HTMLElement the properties and methods needed to support basic media-related capabilities that are common to audio and video.

HTMLMediaElement.captureStream()。它可以与 <video><canvas> 元素一起使用以捕获它们的流。

相反,可以将 视频流 作为 srcObject 添加到 <video> 元素,然后显示它。 <canvas> 元素也可以吗?

是否可以将流作为添加到html <canvas>元素?

不,Canvas API 中没有任何东西能够使用 MediaStream。

canvas APIs 仅适用于原始像素,不包含任何类型的解码器。您必须使用能够执行此解码的 javascript 对象(例如 ImageBitmap)或 HTMLElements。

因此对于 MediaStream,目前唯一能够解码其视频内容的对象将是 HTMLVideoElement,您将能够 draw on your canvas easily


2021 年更新

WebCodecs API最近取得了长足的进步,越来越成熟,值得作为解决方案提及。

这个API提供了一个名为VideoFrame which will soon be part of the CanvasImageSources类型的新接口,意思是,我们可以直接在drawImagetexImage2D和任何地方使用它CanvasImageSource可以使用。
MediaCapture Transform W3C group has developed a MediaStreamTrackProcessor 从视频 MediaStreamTrack 中做 return 这样的 VideoFrames。

所以我们现在有一种更直接的方法来将 MediaStream 渲染到 canvas,目前只能在 Chrome 浏览器中使用 #enable-experimental-web-platform-features 标志...

if( window.MediaStreamTrackProcessor ) {
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  const track = getCanvasTrack(); // MediaStream.getVideoTracks()[0]
  const processor = new MediaStreamTrackProcessor( track );
  const reader = processor.readable.getReader();
  readChunk();
  function readChunk() {
    reader.read().then( ({ done, value }) => {
      // the MediaStream video can have dynamic size
      if( canvas.width !== value.displayWidth || canvas.height !== value.displayHeight ) {
        canvas.width = value.displayWidth;
        canvas.height = value.displayHeight;
      }
      ctx.clearRect( 0, 0, canvas.width, canvas.height );
      // value is a VideoFrame
      ctx.drawImage( value, 0, 0 );
      value.close(); // close the VideoFrame when we're done with it
      if( !done ) {
        readChunk();
      }
    });
  }
}
else {
  console.error("Your browser doesn't support this API yet");
}

// We can't use getUserMedia in StackSnippets
// So here we use a simple canvas as source
// for our MediaStream.
function getCanvasTrack() {
  // just some noise...
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  const img = new ImageData(300, 150);
  const data = new Uint32Array(img.data.buffer);
  const track = canvas.captureStream().getVideoTracks()[0];

  anim();
  
  return track;
  
  function anim() {
    for( let i=0; i<data.length;i++ ) {
      data[i] = Math.random() * 0xFFFFFF + 0xFF000000;
    }
    ctx.putImageData(img, 0, 0);
    if( track.readyState === "live" ) {
      requestAnimationFrame(anim);
    }
  }
  
}
<canvas></canvas>

作为 glitch project (source) 使用相机作为来源。

@Kaiido 是正确的,因为没有任何方法可以直接执行此操作。所以,这是你必须做的:

function onFrame() {
  window.requestAnimationFrame(onFrame);
  canvasContext.drawImage(video, 0, 0);
}
onFrame();

您将 运行 遇到的几个问题:

  • 您的源视频可以更改分辨率 mid-stream。这在 WebRTC 调用中非常常见,其中源可能会因带宽或 CPU 限制而缩放实际像素分辨率。解决此问题的一种方法是检查您绘制的每一帧的视频大小,并在 canvas.
  • 上相应地缩放
  • This frame loop doesn't 运行 at speed when the tab doesn't have focus.如果您也依赖此 canvas 中的 captureStream,由于限制策略,如果选项卡没有焦点,它将无法工作。
  • The canvas buffer doesn't update when the tab doesn't have focus, so even if you hack around the timer issue with an audio script node or something, it won't work if you want也可以使用 canvas 中的 captureStream
  • 记住这里没有"genlock"。对于您复制到 canvas 的每一帧,视频中可能会经过任意数量的帧(可能为零!)。这对您的情况可能无关紧要。