vue3版本的录制视频上传案例
HTML5 提供了一种简单而强大的方式来实现视频录制和上传功能。
通过使用 HTML5 的 video 元素和 MediaRecorder 接口,我们可以在浏览器中录制视频,并将其上传到服务器。
原理解释
- 使用
getUserMedia方法获取用户的摄像头和麦克风权限。 - 创建
video元素,将摄像头的视频流绑定到video元素上,实时显示摄像头的画面。 - 创建
MediaRecorder对象,将video元素的画面录制为视频。 - 监听
dataavailable事件,在录制过程中获取视频的数据块。 - 将数据块存储为
Blob对象,并上传到服务器。 -
使用 URL.createObjectURL 方法把blob对象转化为对象url,赋值给video元素,进行预览已录制的视频。
注意
要创建对象 URL,可以使用 window.URL.createObjectURL() 方法,并传入 File 或 Blob 对象。如果不再需要相应数据,最好释放它占用的内容。但只要有代码在引用对象 URL,内存就不会释放。要手工释放内存,可以把对象 URL 传给 URL.revokeObjectURL()。
案例代码(vue3版本)
实现的主要功能:
- 打开设备摄像头进行预览
- 关闭摄像头
- 开始录制视频
- 暂停录制视频
- 继续录制视频
- 停止录制视频
- 重新录制视频
- 上传录制视频
<template><video id="preview" width="400" height="300" autoplay muted></video><video id="preview2" ref="video2" width="400" height="300" autoplay muted></video><button id="startBtn" @click="openMediaDevies">开启摄像头</button><button id="stopBtn" @click="closeMediaDevies">关闭摄像头</button><div v-if="cameraStatus === 1"><button id="startBtn" @click="startRecord">{{ recordStatus === 1 ? '继续录制' : '开始录制'}}</button><button id="startBtn" @click="pauseRecord" v-if="recordStatus === 2">暂停录制</button><button id="stopBtn" @click="stopRecord" v-if="recordStatus === 2">停止录制</button><button id="stopBtn" @click="reRecord" v-if="recordStatus === 2">重新录制</button></div></template><script setup lang="ts">
import { ref } from 'vue'// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {navigator.mediaDevices = {};
}// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有 getUserMedia 属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function (constraints) {// 首先,如果有 getUserMedia 的话,就获得它let getUserMedia =navigator.webkitGetUserMedia || navigator.mozGetUserMedia;// 一些浏览器根本没实现它 - 那么就返回一个 error 到 promise 的 reject 来保持一个统一的接口if (!getUserMedia) {return Promise.reject(new Error("getUserMedia is not implemented in this browser"),);}// 否则,为老的 navigator.getUserMedia 方法包裹一个 Promisereturn new Promise(function (resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});};
}let mediaStream;
const front = ref(true) // 前置或者后置摄像头
const recordStatus = ref(0) // 录制状态 0 停止 1 暂停 2录制
const video2 = ref(null)
const cameraStatus = ref(0) // 0 关闭 1 开启let videoEle;
let mediaRecorder;
let chunks = []; // 录制的数据
let isReRecord = false
let videoURL;// 开始录制
function startRecord() {if(!mediaRecorder) returnrecordStatus.value = 2// (闲置中,录制中或者暂停 ) (inactive, recording, or paused)mediaRecorder.state === 'paused' ? mediaRecorder.resume() : mediaRecorder.start()
}// 暂停录制
function pauseRecord() {recordStatus.value = 1mediaRecorder && mediaRecorder.pause()
}// 停止录制
function stopRecord() {recordStatus.value = 0mediaRecorder && mediaRecorder.stop()
}// 重新开始录制
function reRecord() {console.log('重新开始录制')isReRecord = truestopRecord()// mediaRecorder.requestData()
}// 初始化录制监听事件
function initMediaRecorderEvent() {// 获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象)mediaRecorder.ondataavailable = function(e) {// if(isReRecord) {// isReRecord = false// // chunks.map(i => {// // i = null// // })// chunks = JSON.parse(JSON.stringify([]))// } else {console.log('获取录制的媒体资源', chunks, e)chunks.push(e.data);// }}// 录制报错mediaRecorder.onerror = function(e) {console.log('录制报错')}// 开始录制mediaRecorder.onstart = function(e) {console.log('开始录制')}// 媒体暂停录制mediaRecorder.onpause = function(e) {console.log('媒体暂停录制')}// 暂停后恢复录制视频mediaRecorder.onresume = function(e) {console.log('暂停后恢复录制视频')}// 结束录制mediaRecorder.onstop = function(e) {// 重录标识if(isReRecord) {isReRecord = falsechunks = []startRecord()return}console.log('结束录制', chunks)if(videoURL) {// 释放Blob链接URL.revokeObjectURL(videoURL)}const blob = new Blob(chunks, { type: 'video/wasm' })videoURL = URL.createObjectURL(blob)chunks = []// videoEle.srcObject = null// videoEle.src = videoURL// videoEle.controls = truevideo2.value.src = videoURLvideo2.value.controls = true}}// 上传录制视频到服务器
function uploadBlob(blob) {const formData = new FormData()formData.append('video', blob, 'myvideo.webm')fetch('/upload', {method: 'POST',body: formData}).then(res => {console.log('视频上传成功', res)}).catch(error => {console.log('视频上传失败', error)})
}// 开启摄像头
function openMediaDevies() {// 开启摄像头,获取视频流navigator.mediaDevices.getUserMedia({ audio: true, video: { facingMode: front ? "user" : "environment" } }).then(function (stream) {cameraStatus.value = 1mediaStream = stream// 获取video标签videoEle = document.querySelector("video");// 旧的浏览器可能没有 srcObjectif ("srcObject" in videoEle) {videoEle.srcObject = stream;} else {// 防止在新的浏览器里使用它,应为它已经不再支持了videoEle.src = window.URL.createObjectURL(stream);}videoEle.onloadedmetadata = function (e) {videoEle.play();};// 创建MediaRecorder对象mediaRecorder = new MediaRecorder(stream)initMediaRecorderEvent()}).catch(function (err) {cameraStatus.value = 0console.log(err.name + ": " + err.message);});
}// 关闭摄像头
function closeMediaDevies() {cameraStatus.value = 0if(mediaStream) {const tracks = mediaStream.getTracks();for(let i = 0; i < tracks.length; i++) {tracks[i].stop();}}}</script><style scoped>#preview {display: block;margin: 20px auto;background-color: aquamarine;}
</style>
参考技术
MediaDevices.getUserMedia() - Web API 接口参考 | MDN
MediaRecorder - Web API 接口参考 | MDN