如何正确记录画布上的流?

时间:2018-09-26 08:12:36

标签: javascript html html5 vue.js html5-canvas

情况:

我需要执行以下操作:

  • <video>获取视频并在<canvas>

  • 中播放
  • 将来自画布的流记录为Blob

就是这样。第一部分还可以。 对于第二部分,我设法记录了Blob,问题是Blob为空。

视图:

<video id="video" controls="true" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>

代码:

// Init
console.log(MediaRecorder.isTypeSupported('video/webm')) // true
const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")

// Start the video in the player
video.play()

// On play event - draw the video in the canvas
video.addEventListener('play', () => {
  function step() {
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
    requestAnimationFrame(step)
  }
  requestAnimationFrame(step);

  // Init stream and recorder
  const stream = canvas.captureStream()
  const recorder = new MediaRecorder(stream, {
    mimeType: 'video/webm',
  });

  // Get the blob data when is available
  let allChunks = [];
  recorder.ondataavailable = function(e) {
    console.log({e}) // img1
    allChunks.push(e.data);
  }

  // Start to record
  recorder.start()

  // Stop the recorder after 5s and check the result
  setTimeout(() => {
    recorder.stop()
    const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
    const downloadUrl = window.URL.createObjectURL(fullBlob)
    console.log({fullBlob}) // img2
  }, 5000);
})

结果:

这是console.log事件的ondataavailable

enter image description here

这是Blob的console.log

enter image description here

该领域

这里是JsFiddle。您可以在控制台中检查结果:

https://jsfiddle.net/1b7v2pen/

浏览器行为:

此行为(Blob数据大小:0)在Chrome和Opera上发生。
在Firefox上,其行为略有不同。 它记录了一个非常小的视频Blob(725字节)。视频长度应为5秒,但这只是一个黑屏。

问题:

从画布记录流的正确方法是什么?
代码有问题吗?
你知道为什么Blob空出来吗?

谢谢!

1 个答案:

答案 0 :(得分:2)

MediaRecorder.stop()一种异步方法。

stop 算法中,有一个对 requestData 的调用,该调用本身会排队一个任务,以触发当前事件 dataavailable 的事件。自上次此类事件以来的可用数据。

这意味着在您同步调用 MediaRecorder#stop()之后,最后获取的数据将不再属于allChunks数组。它们很快就会消失(通常在同一事件循环中)。

因此,当您要保存由MediaRecorder制作的记录时,请确保始终从MediaRecorder's onstop事件构建最终的Blob,这将表明MediaRecorder实际上已结束,确实触发了它的最后一个 dataavailable 事件,一切都很好。

我一开始错过的一件事是,您正在请求跨域视频。这样做,如果没有正确的跨域请求,将使您的画布(和MediaElement)受到污染,因此您的MediaStream将被静音。

由于您要请求的视频来自Wikimedia,因此您可以简单地将其请求为跨域资源,但是对于其他资源,则必须确保服务器已配置为允许这些请求。

const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")

// Start the video in the player
video.play()

// On play event - draw the video in the canvas
video.addEventListener('play', () => {
  function step() {
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
    requestAnimationFrame(step)
  }
  requestAnimationFrame(step);
  
  // Init stream and recorder
  const stream = canvas.captureStream()
  const recorder = new MediaRecorder(stream, {
    mimeType: 'video/webm',
  });

  // Get the blob data when is available
  let allChunks = [];
  recorder.ondataavailable = function(e) {
    allChunks.push(e.data);
  }
  recorder.onstop = (e) => {
    const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
    const downloadUrl = window.URL.createObjectURL(fullBlob)
    console.log({fullBlob})
    console.log({downloadUrl})
  }

  // Start to record
  recorder.start()

  // Stop the recorder after 5s and check the result
  setTimeout(() => {
    recorder.stop()
  }, 5000);
})
<!--add the 'crossorigin' attribute to your video -->
<video id="video" controls="true" src="https://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" crossorigin="anonymous"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>

此外,我也谨记一下,如果您不从画布上绘制任何特殊图形,则可能要直接保存视频源,或者至少要记录