2.6、实战案例(三)
文章导读:本节实现视频的录制和保存功能。推荐阅读方式:实操。
为了更好的讲解代码,我们还是把软件界面展示出来,如下图2.6.1。
图 2.6.1 (软件界面截图)
本节内容,我们来完成该软件的最后一个功能:视频的录制和保存。先说下视频的录制,在webrtc中如何实现视频的录制呢?主要依赖一个类——MediaRecorder,该类是一个全局类,使用的时候可以直接实例化,实例化时需要传入两个参数,第一个参数是流对象,即通过getUserMedia获取的流对象;第二个参数是录制的视频格式以及编码方式,示例代码如下。
let recordOption = { mimeType:"video/webm;codecs=vp8" } if(!MediaRecorder.isTypeSupported(recordOption.mimeType)){ throw new Error("录制时不支持该格式"+recordOption.mimeType); } let recordObject = new MediaRecorder(mediaStream,recordOption)
如上的代码,mediaStream是一个流对象,即本软件中cameraManager对象的mediaStream属性(记不住的同学自觉翻前几节文章来look look);recordOption则录制时的配置对象,其中mimeType属性描述录制视频的格式以及编码,其中“video/webm”表示录制的类型是视频,格式是webm;codecs=vp8表示采用vp8的编解码规范。recordObject是MediaRecorder实例化后的对象,当然,仅创建了对象还不会立即录制,还得手动调用启动录制,代码如下。
recordObject.start(10)
start方法还传进了一个参数10,表示每片数据时长为10毫秒,即一段完整的视频由多个“10毫秒”长的片段组成的。在录制的过程中,我们如何存储数据?这里就要提到recordObject对象中的一个回调方法——ondataavailable,每隔一段的时间就触发一次,每触发一次就说明完成了一片数据的采集,这里的start方法我们传入参数10,就表示每10毫秒ondataavailable会被触发一次。代码如下。
recordObject.ondataavailable = (e)=>{ if(e && e.data && e.data.size){ // 这里把数据片缓存起来 } }
停止录制的方法也很简单,直接调用其stop方法,代码如下。
recordObject.stop();
以上就是视频录制的核心功能方法,现在我们将其集成到软件中。从业务的角度来看,录制功能可以看做是单独业务,于是这里创建一个对象来管理录制业务——recordManager。该对象有两个方法,两个属性。两个方法:开始录制startRecord、结束录制stopRecord。两个属性:数据缓存对象buffer(用于临时存储二进制视频数据)、recordObject对象存放MediaRecorder的实例化后的对象。代码结构如下。
// 录制管理对象 const recordManager = { // 二进制数据的数组 buffer:[], //录制实例 recordObject:null, // 开始录制 startRecord(){ }, // 停止录制 stopRecord(){ } }
先来实现第一个方法——startRecord。分析下其运行过程:首先清空缓存,防止上一次录制时残留一些垃圾数据影响本次录制;其次实例化一个MediaRecorder对象,同时设置其数据回调函数;最后启动录制,并且更新系统状态为“录制中”。代码如下。
// 开始录制 startRecord(){ this.buffer = [];//清空数据 if(!cameraManager.mediaStream){ throw new Error("录制失败,mediaStream读取失败"); } let recordOption = { mimeType:"video/webm;codecs=vp8" } if(!MediaRecorder.isTypeSupported(recordOption.mimeType)){ throw new Error("录制时不支持该格式"+recordOption.mimeType); } this.recordObject = new MediaRecorder(cameraManager.mediaStream,recordOption) // 录制时数据回调 this.recordObject.ondataavailable = (e)=>{ if(e && e.data && e.data.size){ this.buffer.push(e.data); } } this.recordObject.start(10) statusManager.startedRecord()//更改系统状态 },
如上代码所示, 在实例化MediaRecorder对象之前,考虑到当前环境对指定的格式——“video/webm;codecs=vp8”的支持情况,所以调用了isTypeSupported方法判断。this.recordObject.ondataavailable 是完成一个数据片录制的回调,我们可以在这个回调函数中缓存数据。至此,开始录制功能完成,接下来实现停止录制功能。
分析下停止录制功能的运行流程:首先停止录制;其次创建下载对象,并且下载数据到本地;最后,清空缓存,并还原系统的状态为“摄像头已打开”。实现的代码如下。
// 停止录制 stopRecord(){ if(!this.recordObject){ return; } this.recordObject.stop(); let blob = new Blob(this.buffer,{type:"video/webm"}); let url = URL.createObjectURL(blob); let a = document.createElement("a"); a.href = url; a.download = "record.webm" a.click(); this.buffer = []; statusManager.openedCamera(); }
至此,开始录制和停止录制的功能编写完毕,这两个方法都需要在eventManager(事件管理对象)中通过监听“开始录制”、“结束录制”两个按钮来调用,这里就不在演示,接下来展示recordManager的完整代码。
// 录制管理对象 const recordManager = { // 二进制数据的数组 buffer:[], //录音实例 recordObject:null, // 开始录制 startRecord(){ this.buffer = [];//清空数据 if(!cameraManager.mediaStream){ throw new Error("录制失败,mediaStream读取失败"); } let recordOption = { mimeType:"video/webm;codecs=vp8" } if(!MediaRecorder.isTypeSupported(recordOption.mimeType)){ throw new Error("录制时不支持该格式"+recordOption.mimeType); } this.recordObject = new MediaRecorder(cameraManager.mediaStream,recordOption) // 录制时数据回调 this.recordObject.ondataavailable = (e)=>{ if(e && e.data && e.data.size){ this.buffer.push(e.data); } } this.recordObject.start(10) statusManager.startedRecord()//更改系统状态 }, // 停止录制 stopRecord(){ if(!this.recordObject){ return; } this.recordObject.stop(); let blob = new Blob(this.buffer,{type:"video/webm"}); let url = URL.createObjectURL(blob); let a = document.createElement("a"); a.href = url; a.download = "record.webm" a.click(); this.buffer = []; statusManager.openedCamera(); } }
最后。总结下:第一、本节的新内容 :MediaRecorder类的运用(实例化、start和stop的调用以及数据回调函数ondataavailable的使用);第二、下载数据时用到了Blob类,该类是JS中用来处理二进制大文件的,如果你对此不熟悉,建议查阅些资料。下载数据的逻辑是通过创建一个a元素并且模拟点击来实现的。
到这里,本软件的所有功能已经实现完毕了,为了给大家更多的练习的机会,有些功能我并没有实现或者说没有细化,比如音频参数的配置,我只是简单的配置否启用,实际上音频的采集时还是有一些可配置的参数,所以下节补充音视频采集的相关内容以及桌面录制的相关内容,并布置本章的实战练习任务。