分享一个canvas录屏方案

背景

对比Web开发和原生开发,通常APP被说到的优势在于两点:1.交互体验更流畅。2.硬件支持更友好。随着HTML5的发展,我们可以通过浏览器做的事情会越来越多,之前只能原生开发做的事情通过浏览器也能实现,比如下面我要说的"视频录制"

不啰嗦,先看demo:一张canvas画布上有一颗红色的小球左右来回循环滚动,点击"开始录制"按钮,视频就开始录制,再点击"下载录像"按钮,视频结束录制,并且会下载一个webm格式的视频文件在本地。

技术上实现

我这里分享一整个前端录制+服务端存储+下载的完整方案(上面demo是简版,无服务端存储步骤)。整个开发流程可分为3个步骤:1.视频录制,2.上传持久化存储,3.下载视频。整个前端流程可概括成下图

下面详细地介绍各实现步骤

录制视频

首先,调研找到了HTML5 的一个API MediaRecorder 适合来做录制这个事情,摘抄下官网文档:

MediaRecorderMediaStream Recording API 提供的用来进行媒体轻松录制的接口, 他需要通过调用 MediaRecorder() 构造方法进行实例化。 一个新的MediaRecorder对象,对指定的MediaStream 对象进行录制,支持的配置项包括设置容器的MIME 类型 (例如"video/webm" 或者 "video/mp4")和音频及视频的码率或者二者同用一个码率

通过上面介绍可以得知,只要能得到录制对象的 MediaStream (本文展示的是canvas录制,目前能转为MediaStream的对象还有video,audio,电脑前置摄像头输入)就可以轻松的实现录制这个事情。

接着找到canvas如下API,可用于从canvas中获取到我们需要的MediaStream

MediaStream = canvas.captureStream(frameRate);

以上就走通了视频的录制过程:canvas —>MediaStream—>MediaRecord—>视频。整个录制的示例代码如下:

//录制到的数据数据
letallChunks=[];
letcanvas=document.getElementById("canvasId");
letstream=canvas.captureStream(60); // 60 FPS recording
letrecorder=newMediaRecorder(stream, {
  mimeType: 'video/webm;codecs=vp9',
});
// canvas 录制回调
recorder.ondataavailable=e=>{
   allChunks.push(e.data);
}
recorder.start(10);

还有一点要注意的是我们这边录制视频的格式是webm, MP4格式的在目前最新chrome版本中暂不支持,可通过下面API查询支持情况

MediaRecorder.isTypeSupported('video/webm'); //true
MediaRecorder.isTypeSupported('video/mp4');//false

上传&持久化存储

上传要做的事情相对来说比较明确,只需要将上面提到的allChunks对象上传即可。我这边在业务上考虑到了两个点:1.长时间录制allChunks尺寸会过大,上传会比较耗时。2.录制过程中如有突发情况(刷新或点击链接跳走),视频就无法保存了。

针对于上面两种业务场景,我简单的采用了Blob切片上传,开始录制后,设置一个定时器,每隔10秒发送增量的视频切片给后端,前端示例代码如下:

// 定时向后端发送 增量blob
function sendNewChunks(){
   let start = 0;
   let iterationIndex = 0;
   sendTimer = window.setInterval( () => {
       let allBlob = new Blob(allChunks);     
       // 把增量视频的片段切下来
       let newBlob = allBlob.slice(start, allBlob.size);
       start = allBlob.size;
       // 当录像有更新的时候,才向接口发送分片信息
       if (newBlob.size > 0) {          
           iterationIndex++;
         // 在这里Http post 发送newBlob对象
       }
   },10000)
}

我这个场景,后端用是node.js,所以在node层直接调用阿里云的OSS API 进行上传即可:

letresult=awaitclient.put('object-name', newBuffer('hello world'));

这边一点需要注意的是,OSS node.js API 这边需要上传的是Buffer格式的数据,在尝试了各种方法后,我最终能走通整个流程做法是:先在前端把Blob格式的视频切片转换成Uint8Array,传递到node.js后再通过new Buffer.from(dataArray)转换成Buffer格式。

下载视频

下载视频方案用的是通过websocket向前端不断的推小的视频切片,再由前端把视频拼接起来,借助浏览器转码下载

// 通过blob对象下载
function downloadByBlob(blobObj) {
   const link = document.createElement('a');
   link.style.display = 'none';
   const downloadUrl = window.URL.createObjectURL(blobObj);
   link.href = downloadUrl;
   link.download = `test.webm`;
   document.body.appendChild(link);
   link.click();
   link.remove();
}

最后 实践过程中,有查阅到的一些相关知识点我在下面列一下,希望对你有所帮助! 相关知识点