5

The situation

I need to do the following:

  • Get the video from a <video> and play inside a <canvas>

  • Record the stream from the canvas as a Blob

That's it. The first part is okay.

For the second part, I managed to record a Blob. The problem is that the Blob is empty.

The view

<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>

The code

// 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);
})

The result

This the console.log of the ondataavailable event:

Enter image description here

This is the console.log of the Blob:

Enter image description here

The fiddle

Here is the JSFiddle. You can check the results in the console:

https://jsfiddle.net/1b7v2pen/

Browsers behavior

This behavior (Blob data size: 0) it happens on Chrome and Opera.
On Firefox it behaves slightly different. It records a very small video Blob (725 bytes). The video length is 5 seconds as it should be, but it's just a black screen.

The question

What is the proper way to the record a stream from a canvas?
Is there something wrong in the code?

Why did the Blob come out empty?

4

1 回答 1

5

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

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

这意味着在您调用MediaRecorder#stop()之后同步地抓取的最后一个数据将不会成为您的allChunks数组的一部分。它们会在不久之后变得(通常在同一个事件循环中)。

因此,当您要保存从 MediaRecorder 制作的记录时,请务必始终从MediaRecorder 的onstop事件构建最终 Blob,这将表明 MediaRecorder 实际上已结束,确实触发了它的最后一个数据可用事件,并且一切都很好.

起初我错过的一件事是,您正在请求跨域视频。这样做,如果没有正确的跨域请求,将使您的画布(和 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>

另外,我不得不指出,如果您不从画布上进行任何特殊绘图,您可能希望直接保存视频源,或者至少直接记录 <video> 的 captureStream MediaStream。

于 2018-09-26T12:50:34.570 回答