分享一个canvas录屏方案

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

背景

对比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向前端不断的推小的视频切片,再由前端把视频拼接起来,借助浏览器转码下载

  • 为什么要用websocket推送切片,而不直接通过一个请求下载?
  • 若直接通过后端把视频拼接起来,再通过一个请求推给前端这种方案在视频尺寸过大时,会有请求callback超时风险
  • 通过websocket可以比较方便的实现流式下载,并在前端做对应可视化展现(做一个像迅雷一样的下载器)。


  • 为什么要借助浏览器转码?
  • 尝试过后端拼接OSS文件存储的Buffer格式文件,但暂时没找到合适的转码库。而前端可以通过window.URL.createObjectURL,比较简单的把缓存里的Blob对象通过浏览器下载成一个文件,代码如下:
// 通过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();
}


最后

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


相关知识点

  • Blob ( Binary Large Object )表示一个二进制文件的数据内容,表示一个不可变、原始数据的类文件对象。(File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。)
  • WebM由Google提出,是一个开放、免费的媒体文件格式。WebM 影片格式其实是以 Matroska(即 MKV)容器格式为基础开发的新容器格式,里面包括了VP8影片轨和 Ogg Vorbis 音轨,其中Google将其拥有的VP8视频编码技术以类似BSD授权开源,Ogg Vorbis 本来就是开放格式。
  • 二进制数组相关
  • 使用场景:ArrayBuffer对象、TypedArray对象、DataView对象是JavaScript操作二进制数据的一个接口
  • 区别:ArrayBuffer对象代表原始的二进制数据,TypedArray对象代表确定类型的二进制数据,DataView对象代表不确定类型的二进制数据
  • 目的:这些对象原始的设计目的,与WebGL项目有关。所谓WebGL,就是指浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个32位整数,两端的JavaScript脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像C语言那样,直接操作字节,将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。


  • 兼容性相关
  • 其中MediaRecorderMediaStream 都是处于Working Draft阶段的API,所以并不是所有浏览器都支持,Chrome (49.0+)和 FireFox(25.0+) 支持的比较好,详情可以点击前面链接进去,拉到页面最底下查看兼容性。
相关文章
|
前端开发 数据安全/隐私保护 容器
|
前端开发 小程序
小程序使用canvas制作beas64图片
小程序使用canvas制作beas64图片
97 0
Echarts手机端无刷新实现图表自适应横屏和竖屏的解决方案
Echarts手机端无刷新实现图表自适应横屏和竖屏的解决方案
380 0
|
小程序 前端开发
微信小程序中使用画布canvas实现动态心电图绘制
微信小程序中使用画布canvas实现动态心电图绘制
640 0
微信小程序中使用画布canvas实现动态心电图绘制
|
前端开发 小程序
微信小程序 Canvas导出图片模糊?(已解决)
首先确定 userInfo 的 avatar 不是 132,原图是 0; 其次确定你的 destWidth 和 destHeight 不是 width 和 height;
665 0
|
29天前
|
前端开发 小程序 JavaScript
小程序 canvas 生成海报 一次搞掂
小程序 canvas 生成海报 一次搞掂
23 1
|
6月前
|
前端开发 小程序
微信小程序绘制canvas时在不同 设备上的大小不同的问题
微信小程序绘制canvas时在不同 设备上的大小不同的问题
248 0
|
6月前
|
前端开发 小程序
【微信小程序5】利用canvas实现纯色背景抠图功能
【微信小程序5】利用canvas实现纯色背景抠图功能
313 0
|
6月前
|
前端开发 小程序
微信小程序canvas画布绘制;canvas画布图片保存
微信小程序canvas画布绘制;canvas画布图片保存
87 0
|
前端开发 索引
Canvas画布撤销与前进实现方案
Canvas画布撤销与前进实现方案
421 0
Canvas画布撤销与前进实现方案