最近在做开源实例: Vue.js 实战系列之实现视频类WebApp的项目(仿抖音App)【感兴趣的小伙伴可以看一下】,其中就有一个功能是视频的拍摄发布(摄像头调用,视频录制等功能),所以特意整理一个Vue如何使用该API调用本地摄像头实现录像拍照功能。
本文主要包括前端调用本地摄像头实现拍照、录像的使用实例、应用技巧、基本知识点总结和一些需要注意的事项,在项目开发中具有一定的参考价值,需要的朋友可以参考一下。
Vue 调用本地摄像头实现拍照功能,由于调用摄像头有使用权限,只能在本地运行,线上需用 https 域名才可以使用。
MediaDevices.getUserMedia()
该 API 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream
,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。
它返回一个 Promise
对象,成功后会 resolve
回调一个 MediaStream
对象。若用户拒绝了使用权限,或者需要的媒体源不可用,Promise
会 reject
回调一个 PermissionDeniedError
或者 NotFoundError
。
注意:返回的
Promise
对象可能既不会resolve
也不会reject
,因为用户不是必须选择允许或拒绝。
使用:我们可以使用 navigator.mediaDevices
来获取 MediaDevices
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
/* 使用这个stream stream */
})
.catch(function(err) {
/* 处理error */
});
constraints
参数介绍:
指定了请求的媒体类型和相对应的参数。
参数包含了 video
和 audio
两个成员的 MediaStreamConstraints
对象,用于说明请求的媒体类型。必须至少一个类型或者两个同时可以被指定。
关于该API其他具体使用细节,请移步 getUserMedia - MDN 查看。
<template>
<div class="publish">
<video ref="video">video>
<canvas style="display: none" id="canvasCamera">canvas>
<div v-if="imgSrc" class="img_bg_camera">
<img :src="imgSrc" class="tx_img" />
div>
<button @click="OpenCamera">打开摄像头button>
<button @click="CloseCamera">关闭摄像头button>
<button @click="setImage">拍照button>
div>
template>
<script>
export default {
data() {
return {
mediaStreamTrack: {},
video_stream: '', // 视频stream
imgSrc: '', // 拍照图片
canvas: null,
context: null,
};
},
mounted() {
// 进入页面 自动调用摄像头
this.getCamera();
},
methods: {
// 调用打开摄像头功能
getCamera() {
// 获取 canvas 画布
this.canvas = document.getElementById('canvasCamera');
this.context = this.canvas.getContext('2d');
// 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
// 正常支持版本
navigator.mediaDevices
.getUserMedia({
video: true,
})
.then((stream) => {
// 摄像头开启成功
this.mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
this.video_stream = stream;
this.$refs.video.srcObject = stream;
this.$refs.video.play();
})
.catch(err => {
console.log(err);
});
},
// 拍照 绘制图片
setImage() {
console.log('拍照');
// 点击canvas画图
this.context.drawImage(
this.$refs.video,
0,
0,
200,
100,
);
// 获取图片base64链接
const image = this.cancas.toDataURL('image/png');
this.imgSrc = image;
this.$emit('refreshDataList', this.imgSrc);
},
// 打开摄像头
OpenCamera() {
console.log('打开摄像头');
this.getCamera();
},
// 关闭摄像头
CloseCamera() {
console.log('关闭摄像头');
this.$refs.video.srcObject.getTracks()[0].stop();
},
},
};
script>
<style lang="less" scoped>
video {
width: 100%;
height: 300px;
}
canvas {
width: 100%;
height: 300px;
}
button {
width: 100px;
height: 40px;
position: relative;
bottom: 0;
left: 0;
background-color: rgb(22, 204, 195);
}
.img_bg_camera {
img {
width: 300px;
height: 200px;
}
}
style>
<template>
<div class="publish">
<a id="downLoadLink" style="display: none;">a>
<video ref="video">video>
<div @click="recordOrStop">视频录制div>
div>
template>
<script>
export default {
data() {
return {
mediaStreamTrack: {}, // 退出时关闭摄像头
video_stream: '', // 视频stream
recordedBlobs: [], // 视频音频 blobs
isRecord: false, // 视频是否正在录制
};
},
mounted() {
// 进入页面 调用摄像头
this.getCamera();
},
methods: {
// 调用打开摄像头功能
getCamera() {
// 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
navigator.mediaDevices
.getUserMedia({
video: true,
})
.then((stream) => {
// 摄像头开启成功
this.mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
this.video_stream = stream;
this.$refs.video.srcObject = stream;
this.$refs.video.play();
})
.catch(err => {
console.log(err);
});
},
// 录制或暂停
recordOrStop() {
if (this.isRecord) {
this.stop();
} else {
this.record();
}
},
// 视频录制
record() {
console.log('record');
this.isRecord = !this.isRecord;
let mediaRecorder;
let options;
this.recordedBlobs = [];
if (typeof MediaRecorder.isTypeSupported === 'function') {
// 根据浏览器来设置编码参数
if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
options = {
MimeType: 'video/webm;codecs=h264',
};
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
options = {
MimeType: 'video/webm;codecs=h264',
};
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) {
options = {
MimeType: 'video/webm;codecs=vp8',
};
}
mediaRecorder = new MediaRecorder(this.video_stream, options);
} else {
// console.log('isTypeSupported is not supported, using default codecs for browser');
console.log('当前不支持isTypeSupported,使用浏览器的默认编解码器');
mediaRecorder = new MediaRecorder(this.video_stream);
}
mediaRecorder.start();
// 视频录制监听事件
mediaRecorder.ondataavailable = e => {
console.log(e);
// 录制的视频数据有效
if (e.data && e.data.size > 0) {
this.recordedBlobs.push(e.data);
}
};
// 停止录像后增加下载视频功能,将视频流转为mp4格式
mediaRecorder.onstop = () => {
const blob = new Blob(this.recordedBlobs, { type: 'video/mp4' });
this.recordedBlobs = [];
// 将视频链接转换完可以用于在浏览器上预览的本地视频
const videoUrl = window.URL.createObjectURL(blob);
// 设置下载链接
document.getElementById('downLoadLink').href = videoUrl;
// 设置下载mp4格式视频
document.getElementById('downLoadLink').download = 'media.mp4';
document.getElementById('downLoadLink').innerHTML = 'DownLoad video file';
// 生成随机数字
const rand = Math.floor((Math.random() * 1000000));
// 生成视频名
const name = `video${rand}.mp4`;
// setAttribute() 方法添加指定的属性,并为其赋指定的值
document.getElementById('downLoadLink').setAttribute('download', name);
document.getElementById('downLoadLink').setAttribute('name', name);
// 0.5s后自动下载视频
setTimeout(() => {
document.getElementById('downLoadLink').click();
}, 500);
};
},
// 停止录制
stop() {
this.isRecord = !this.isRecord;
if (!this.$refs.video.srcObject) return;
const stream = this.$refs.video.srcObject;
const tracks = stream.getTracks();
// 关闭摄像头和音频
tracks.forEach(track => {
track.stop();
});
},
},
};
script>
<style lang="less" scoped>
.publish {
color: #fff;
video {
width: 100%;
height: 100vh;
}
div {
position: absolute;
left: calc(50% - 80px);
bottom: 0;
height: 40px;
width: 160px;
font-size: 14px;
border-radius: 10px;
line-height: 40px;
background-color: rgb(25, 179, 179);
text-align: center;
}
}
style>
通过以上两段代码,可以分别实现调用本地摄像头实现录像或者拍照的功能,并且支持视频下载到本地。
以上代码可以拿来直接使用,欢迎小伙伴踊跃尝试,有什么问题欢迎留言讨论。
如果觉得有用,别忘了一键三连哦,感觉各位的支持。