随着浏览器功能的增强,前端实现录屏功能已经是手到擒来的事情——MediaRecorder Api,但是它也存在一些弊端:1.只能录制webm格式的视频;2.录制的视频没有时长。
针对第二点,可以使用recordrtc库解决,简单介绍一下(vue2代码):
<script>
import {
RecordRTCPromisesHandler,
invokeSaveAsDialog,
getSeekableBlob
} from "recordrtc"
export default {
data() {
return {
recorder: null
}
},
methods: {
// 浏览器调取摄像头,播放
async handlePlay() {
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: false
})
this.$refs.videoRef.srcObject = mediaStream
this.$refs.videoRef.play().catch(console.error)
},
// 视频录制--start
async startRecord() {
try {
this.recorder = new RecordRTCPromisesHandler(this.video.srcObject, { type: 'video' })
} catch (error) {
console.error(error)
return
}
this.recorder.startRecording()
},
// 视频录制--end
async endRecord() {
this.recorder?.stop()
await this.recorder.stopRecording()
const blob = await this.recorder.getBlob()
// getSeekableBlob是解决原生MediaRecorder录屏的webm视频没有视频总时间和拖动滚动条的功能
// 内部用到了ts-ebml库,所以再html中引入了ts-ebml.js
getSeekableBlob(blob, (seekableBlob) => {
invokeSaveAsDialog(seekableBlob, `${new Date().getTime()}.webm`)
})
}
}
}
</script>
重点说一下录制MP4格式视频的方法,主要用到了两个库avrecorder-recorder和@webav/av-cliper,以下是完整代码(vue2):
<template>
<div>
<div>
<el-button @click="handlePlay">播放</el-button>
<el-button @click="handleClose">关闭</el-button>
<el-button @click="handleStartRecord">开始录像</el-button>
<el-button @click="handleStopRecord">停止录像</el-button>
</div>
<video ref="videoRef" width="600" height="333" style="background-color: black;margin-top: 20px;"></video>
</div>
</template>
<script>
// import { AVRecorder } from '@webav/av-recorder' 不要用这个库中的AVRecorder
import { AVRecorder } from 'avrecorder-recorder'
import { fixFMP4Duration } from '@webav/av-cliper'
const createFileWriter = async (extName = 'mp4') => {
const fileHandle = await window.showSaveFilePicker(
{
suggestedName: `WebAV-export-${Date.now()}.${extName}`
}
)
return fileHandle.createWritable()
}
export default {
data() {
return {
recorder: null,
writer: null
}
},
methods: {
async handlePlay() {
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: false
})
this.$refs.videoRef.srcObject = mediaStream
this.$refs.videoRef.play().catch(console.error)
},
handleClose() {
if (this.$refs.videoRef.srcObject instanceof MediaStream) {
this.$refs.videoRef.srcObject.getTracks().forEach(track => {
track.stop()
})
this.$refs.videoRef.srcObject = null
}
},
async handleStartRecord() {
try {
this.writer = await createFileWriter()
} catch (error) {
console.log(error)
}
this.recorder = new AVRecorder(this.$refs.videoRef.srcObject, {
width: 1280,
height: 720,
})
await this.recorder.start()
try {
(await fixFMP4Duration(this.recorder.outputStream)).pipeTo(this.writer)
} catch (error) {
console.error(error)
}
},
handleStopRecord() {
this.recorder.stop()
}
}
}
</script>
<style>
</style>
注意:如果使用Vue3的话,writer不要使用响应式,否则会报错“Uncaught (in promise) TypeError: Cannot write to private field”。