前端音视频之录制器(Recorder)的实现

972 阅读3分钟

介绍

本文将介绍如果通过 MediaRecorder API 在浏览器中进行屏幕录制和将录制的视频进行下载,并且拓展实现边录边播的功能。

预览地址:recorder

要进一步进行,您应该熟悉一些术语:

MediaDevices:  MediaDevices 接口提供对连接的媒体输入设备(如相机和麦克风)的访问,以及屏幕共享。参考:MDN

MediaSource: MediaSource 是 Media Source Extensions API 表示媒体资源 HTMLMediaElement 对象的接口。MediaSource 对象可以附着在 HTMLMediaElement 在客户端进行播放。参考:MDN

SourceBuffer: 接口表示通过 MediaSource 对象传递到 HTMLMediaElement 并播放的媒体分块。它可以由一个或者多个媒体片段组成。参考:MDN

MediaRecorder: MediaStream API 的 MediaRecorder 接口提供了轻松录制媒体的功能。参考:MDN

HTMLMediaElement: HTML 媒体元素接口在属性和方法中添加了 HTML 元素来支持基础的媒体相关的能力,就像 audio 和 video 一样。HTML 视频元素和 HTML 音频元素元素都继承自此接口。参考:MDN

获取用户媒体输入的许可

获取用户媒体输入的许可,就可以拿到用户摄像头的媒体流,下一步就可以拿它去创建 MediaRecorder 对象进行录制了。

navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true
}).then(stream=>{
    // ...
})

创建MediaRecorder

使用媒体流创建一个MediaRecorder对象,监听 ondataavailable 事件回调 (当媒体数据传递时会触发)。并将每次传递时接收到的每个数据将其放入 blobs 数组中。后续实现下载录制文件时将它组合成 Blob,并使用 URL.createObjectURL 为 Blob 创建下载链接。

class Recorder {

    // ...

    createMediaRecorder() {
        if (!this.stream || !this.mimeType) return
        const { mimeType, recorderOptions } = this
        this.mediaRecorder = new MediaRecorder(this.stream, {
            mimeType,
            ...recorderOptions
        })
        this.mediaRecorder.ondataavailable = (e) => {
            const blob = e.data
            if (blob.size > 0) {
                const reader = new FileReader()
                this._blobs.push(blob)
                // 读取 Blob 的 ArrayBuffer 并喂给 SourceBuffer
                reader.readAsArrayBuffer(blob)
                reader.onloadend = () => {
                    // ...
                    this.sourceBuffer?.appendBuffer(reader.result)
                }
            }
        }
    }
    
    download(filename: string) {
        // 创建Blob
        const blob = new Blob(this.blobs, { type: this.mimeType })
        // 创建下载链接
        const url = URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = url
        a.download = filename || `${Date.now()}`
        a.click()
        URL.revokeObjectURL(a.href)
    }
}

创建MediaSource

实现边录边播的核心代码噢 ~

初始化 MediaSource 对象,利用 URL.createObjectURL 创建Blob格式的视频文件赋值给 HTMLMediaElement 的 src。

监听 MediaSource 的 sourceopen 事件回调并调用 它的 addSourceBuffer 方法初始化 SourceBuffer 集合,并监听上文中提到的 MediaRecorder 的 ondataavailable 事件回调, 持续读取 Blob 的 ArrayBuffer,并喂给 SourceBuffer,由它将数据喂给 MediaSource 并输入到 HTMLMediaElement 对象进行数据解析播放。从而实现介绍中所提到的边录边播功能。

class Recorder {

    // ...

    createMediaSource() {
        this.mediaSource = new MediaSource()
        this.mediaSource.addEventListener('sourceopen', this.onMediaSourceOpen.bind(this))
    }
    
    onMediaSourceOpen() {
        if (!this.mediaSource || !this.mimeType) return
        // 创建 SourceBuffer
        this.sourceBuffer = this.mediaSource?.addSourceBuffer(this.mimeType)
    }
}

边录边播效果

使用 canvas 模拟一个时钟的效果,使用 captureStream 获取画布流,图中第一个 video 为画布流直播效果, 图中第二个 video 为使用录制器实现的边录边播效果,并且实现了2s的延时效果,是不是很神奇?

动画.gif

💡 Tip

对于边录边播在纯前端中的应用,目前只是发现一个场景,对于导播系统业务中,前端需要对直播效果进行模拟,

最后

至此,我们就可以实现介绍中所提到的下载录制文件、边录边播的功能啦。

目前前端音视频领域中还是处于一个深水区,这个领域需要大家去挖掘它,尝试未使用过的 Native API。

源码地址:media-stream/recorder
预览地址:recorder