AVRecorder录制MP4视频

317 阅读1分钟

随着浏览器功能的增强,前端实现录屏功能已经是手到擒来的事情——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”。