前言
请使用该Token:1563266971025访问本文Demo,或者点击这里访问。
说起web前端录像📹网页内容,,我们的反应可能是先给页面截屏,再将一帧帧的图像合成视频,截屏的操作则是通过html 2 canvas的方式实现。 如果想要合成流畅的视频,那么就必须截出密集帧图像,这无疑是很消耗性能的。如果有一个原生的API,支持对网页录像就好了!!!
本文介绍的内容包括:
MediaRecoder
录制媒体流Canvas
绘制动画- 导出录像视频
MediaRecoder
MediaRecoder
提供了一系列用于录制媒体(视频音频等)的接口,通过调用其构造函数可以创建一个MediaRecoder
实例对象,在new
实例对象时传入媒体对象参数,则可以实现对该媒体对象的录音/像。
其中,媒体对象的来源有两类,一类是当前网页上的媒体,可以是video、audio或者canvas元素;另一类是用户的桌面设备,通过navigator.mediaDevices.getUserMedia()
可以取得用户的桌面录像录音权限,这一类技术的应用场景有web RTC。这里讨论的是第一类:针对网页内容的录像.
举个例子: 对video元素录像
const mediaStream = $video.captureStream(10) // 获取媒体元素的媒体流对象
mediaRecord = new MediaRecorder(mediaStream, { // 实例化录制对象
videoBitsPerSecond: 8500000, // 比特率
mimeType: 'video/mp4' // 录屏媒体流文件类型
})
mediaRecord.ondataavailable = (e) => { // 接收媒体流数据: Blob类型
this.chunks.add(e.data)
}
以上仅仅是初始化一个媒体录像实例对象, 我们还需要手动启动录制或停止:
mediaRecord.start() // 开始录制
mediaRecord.stop() // 停止录制
实例化过程的mimeType
参数在使用上有一定的限制,需要预先检测当前浏览器是否支持该类型:
MediaRecorder.isTypeSupported('video/mp4') // true or false
常用属性与方法
MediaRecorder.mimeType
[只读]:返回实例化过程设置的媒体文件类型。笔者设备测试,如果未设置,默认返回:Chrome ->video/webm;codecs=vp8
,Firefox ->video/webm
;MediaRecorder.state
[只读]:返回当前MediaRecorder
实例的工作状态,可选值有:inactive
、recording
和paused
;MediaRecorder.stream
[只读]:返回当前媒体流,亦即实例化过程传入的媒体流对象MediaRecorder.ignoreMutedMedia
:是否静音模式录制;MediaRecorder.start(timeslice)
:开始录制。timeslice
参数可选,表示以该持续时间切片媒体数据;MediaRecorder.pause()
:暂停录制;MediaRecorder.resume()
:继续录制;MediaRecorder.stop()
:停止录制。
事件处理器
MediaRecoder.ondataavailable
:在该事件中接收媒体录制数据
const chunks = new Set()
mediaRecord.ondataavailable = (e) => {
chunks.add(e.data)
}
如果在start()
中设置了timeslice
,那么该事件每隔timeslice
时间会触发一次;否则只在录制结束时触发。
MediaRecoder.onstart
:在调用start()
时触发,类似的还有onpause
、onresume
、onstop
MediaRecoder.onerror
:捕获错误异常。
captureStream
在前面的举例中我们用到了captureStream
这个方法,该方法用于获取媒体流。在H5里面,有两个对象实现了这个API,分别是HTMLMediaElement
和HTMLCanvasElement
。前面示例中我们使用$video.captureStream()
,就是应用了HTMLMediaElement
元素的接口(audio
元素也支持),canvas
元素则可以通过HTMLCanvasElement
访问该接口。
HTMLMediaElement
提供这个接口有什么作用呢?我目前想到的一个应用场景是媒体资源的下载。如果我们需要下载某个视频,通常的做法可能是需要后端提供一个接口返回二进制内容,有了这个API,前端就可以自己实现下载了。
接下来主要介绍HTMLCanvasElement
的captureStream
:通过录屏实现对canvas
内容的监控。
canvas绘制圆周运动
Step 1:来,左边画个圆
// 参数分别为画布上下文对象、横纵坐标、填充颜色
function drawCircle (ctx, x=100, y=100, color) {
const radius = 20
ctx.clearRect(0, 0, 600, 300)
ctx.beginPath()
ctx.arc(x, y, radius, 0, 2 * Math.PI, false)
ctx.lineWidth = 0
ctx.closePath()
ctx.fillStyle = color
ctx.fill()
ctx.strokeStyle = color
ctx.stroke()
}
Step 2:右边铺块布
// 初始化画布
function initCanvas () {
const $circle = $('#circle')
$circle.width = 600
$circle.height = 300
const ctx = $circle.getContext('2d')
drawCircle(ctx, x, y, color)
}
Step 3:诶,加个动画
canvas动画的核心就是:重复绘制!
function initCanvas () {
// ...省略初始化
const cTimer = new Timer(5000) // 动画周期5s
let color = colors[parseInt(Math.random() * 6)] // 随机颜色
cTimer.onProgress = (p) => {
const x = Math.cos(p * 2 * Math.PI) * 50 + 300
const y = Math.sin(p * 2 * Math.PI) * 50 + 150
if(x < 300 && y > 150 || x > 300 && y < 150) color = colors[parseInt(Math.random() * 6)]
drawCircle(ctx, x, y, color) // 5s内不断画圆
}
cTimer.onFinished = () => {
cTimer.start() // 动画结束之后重新开始
}
cTimer.start() // 动起来
}
录频并下载
Step 1:初始化录屏实例
const chunks = new Set()
function createRecord () {
const mediaStream = $canvas.captureStream(10) // 设置帧频率(FPS)
mediaRecord = new MediaRecorder(mediaStream, {
videoBitsPerSecond: 8500000
})
mediaRecord.ondataavailable = (e) => { // 接收数据
chunks.add(e.data)
}
}
Step 2:控制录屏
mediaRecord.start() // 开始录屏
this.mediaRecord.stop() // 结束录屏
Step 3:下载录屏
const videoBlob = new Blob(chunks, { 'type' : 'video/mp4' })
videoUrl = window.URL.createObjectURL(videoBlob)
chunks
是元素为Blob
类型的列表,可能只有一个元素,可能是多个元素,取决于start()
中是否设置了timeslice
,将这些片段blob
重新封装为一个完整的blob
数据,并且可以指定数据类型。然后根据blob
数据生成一条blob URL
。关于blob
和createObjectURL
的介绍可以移步这里。
最后,借助a
标签下载视频:
function download (videoUrl) {
var a = document.createElement('a')
a.href = videoUrl
a.download = 'record-canvas.mp4'
a.style.display = 'none'
document.body.appendChild(a)
a.click()
}
以上,戳Demo
参考