MediaStream Recording API 对画布元素的视频捕获不起作用

Posted

技术标签:

【中文标题】MediaStream Recording API 对画布元素的视频捕获不起作用【英文标题】:Video capture of canvas element by MediaStream Recording API is not working 【发布时间】:2021-06-23 01:32:53 【问题描述】:

我正在尝试使用官方MediaStream Recording API录制和下载canvas元素的视频

<!DOCTYPE html>
<html>

<body>
    <h1>Lets test mediaRecorder</h1>

    <canvas id="myCanvas"   style="border:1px solid #d3d3d3;">
        Your browser does not support the HTML canvas tag.
    </canvas>

    <script>
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
        ctx.font = "30px Arial";
        ctx.fillText("Hello World", 10, 50);

        const stream = c.captureStream(25);
        var recordedChunks = [];
        console.log(stream);
        var options =  mimeType: "video/webm; codecs=vp9" ;
        mediaRecorder = new MediaRecorder(stream, options);

        mediaRecorder.ondataavailable = handleDataAvailable;
        mediaRecorder.start();

        function handleDataAvailable(event) 
            console.log("data-available");
            if (event.data.size > 0) 
                recordedChunks.push(event.data);
                console.log(recordedChunks);
                download();
             else 
                // ...
            
        

        function download() 
            var blob = new Blob(recordedChunks, 
                type: "video/webm"
            );
            var url = URL.createObjectURL(blob);
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            a.href = url;
            a.download = "test.webm";
            a.click();
            window.URL.revokeObjectURL(url);
        

        // demo: to download after 10 sec
        setTimeout(event => 
            console.log("stopping");
            mediaRecorder.stop();
        , 10000);
    </script>

</body>

</html>

代码正在运行,我可以下载test.webm,但我猜这没有任何数据,因为我在 VLC 媒体播放器中播放此文件时看不到任何内容

我缺少什么让它工作?

【问题讨论】:

【参考方案1】:

您在这里遇到了一些错误。

第一个有点情有可原,Chrome doesn't generate seekable webm files. 这是因为媒体文件的构建方式以及 MediaRecorder API 的工作方式。为了让他们能够添加此信息,他们必须保留元数据所在的块,以便在录制完成时添加此信息。

我不太确定 Firefox 在这里有什么不同,但 VLC 更喜欢他们的文件。

另一个不太可原谅的 Chrome 错误是,在我们再次在源画布上绘制之前,它们不会将新帧传递给 MediaRecorder。 因此,由于在您的情况下,您在启动 MediaRecorder 后没有绘制任何内容,因此您将在输出中一无所获...

要解决这个问题,只需在我们停止记录器之前绘制一个框架就足够了,除了没有什么可以让我们确切知道浏览器何时将该框架推送到记录器...

所以这里唯一可行的解​​决方法是在我们记录的同时不断地在画布上绘制。好消息是它不需要绘制任何新内容:我们可以通过绘制透明矩形来欺骗浏览器,使其认为新内容已被绘制。

最后一点,虽然 Chrome 确实支持以透明方式导出画布,但并非所有浏览器都可以,即使在受支持的情况下,大多数播放器也具有默认的黑色背景。所以在录制的时候一定要给自己画一个其他颜色的背景。

说了这么多,这是一个固定的演示:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// draw a white background
ctx.fillStyle = "white";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "black";
ctx.font = "30px Arial";
ctx.fillText("Hello World", 10, 50);

const stream = c.captureStream(25);
var recordedChunks = [];
var options = ;
mediaRecorder = new MediaRecorder(stream, options);

mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
// Chrome requires we draw on the canvas while recording
mediaRecorder.onstart = animationLoop;

function animationLoop() 
  // draw nothing, but still draw
  ctx.globalAlpha = 0;
  ctx.fillRect(0, 0, 1, 1);
  // while we're recording
  if (mediaRecorder.state !== "inactive") 
    requestAnimationFrame(animationLoop);
  

// wait for the stop event to export the final video
// the dataavailable can fire before
mediaRecorder.onstop = (evt) => download();

function handleDataAvailable(event) 
  recordedChunks.push(event.data);


function download() 
  var blob = new Blob(recordedChunks, 
    type: "video/webm"
  );
  var url = URL.createObjectURL(blob);
  // exporting to a video element for that demo
  // the downloaded video will still not work in some programs
  // For this one would need to fix the markers using something like ffmpeg.
  var video = document.getElementById('video');
  video.src = url;
  // hack to make the video seekable in the browser
  // see https://***.com/questions/38443084/
  video.onloadedmetadata = (evt) => 
    video.currentTime = 10e6;
    video.addEventListener("seeked", () => video.currentTime = 0, 
      once: true
    )
  


setTimeout(() => 
  console.clear();
  mediaRecorder.stop();
, 10000);
console.log("please wait while recording (10s)");
<h1>Lets test mediaRecorder</h1>

<canvas id="myCanvas"   style="border:1px solid #d3d3d3;">
  Your browser does not support the HTML canvas tag.
</canvas>
<video controls id="video"></video>

【讨论】:

感谢您的详尽回答。我们可以将其流式传输到 youtube 直播,而不是添加视频或页面或下载它吗? 哦,对不起,我完全不知道这项服务,也不知道他们接受什么作为输入......但是,必须有一种方法可以假装这样的 MediaStream 是一个设备,但这可能涉及一些我不确定它们是否已经编写好的第三方应用程序 ;-)

以上是关于MediaStream Recording API 对画布元素的视频捕获不起作用的主要内容,如果未能解决你的问题,请参考以下文章

MediaStream图像捕捉

WebRTC音视频采集和播放示例及MediaStream媒体流解析

如何对 MusicBrainz 搜索 API 的结果进行排序

licode学习之erizo篇--MediaStream

通过 NodeJS 发送 MediaStream

如何开始运行 mediaStream