效果预览
1.录制前

2.录制中

3.录制结束下载文件至本地

4.视频文件同时上传至后端接口


后端准备
参考博客https://blog.csdn.net/wuchenlhy/article/details/79311234?spm=1001.2014.3001.5506
博主在后端这块写的十分简洁明了,可以直接参考实现开设后端简单文件上传接口的方法
前端组件实现
参考文章:
https://blog.csdn.net/XH_jing/article/details/117415496
http://t.csdn.cn/ap9Zy
http://t.csdn.cn/NpKtL
Vue 调用本地摄像头实现拍照功能,由于调用摄像头有使用权限,只能在本地运行,线上需用 https 域名才可以使用。
需求分析
点击“视频录制”按钮,打开摄像头开启录制,同时按钮中内容更换为“停止录制”
点击“停止录制”按钮,关闭摄像头并保存录制文件,同时将以录制的文件上传后端接口
使用API
MediaDevices.getUserMedia()
该 API 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。
它返回一个 Promise 对象,成功后会 resolve 回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,Promise 会 reject 回调一个 PermissionDeniedError 或者 NotFoundError。
HTML部分
<template>
<div class="CallCamera">
<!-- 下载按钮 -->
<a id="downLoadLink" style="display: none"></a>
<div class="video-box">
<video ref="video"></video>
<!-- 视频录制或暂停 -->
<button @click="recordOrStop" id="record_btu">视频录制</button>
</div>
</div>
</template>
JS部分
1.调用开启摄像头
// 调用打开摄像头功能
getCamera() {
// 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
// 正常支持版本
navigator.mediaDevices
.getUserMedia({
video: true,
audio: 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);
});
},
2.视频录制并下载以及停止录制
// 视频录制
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);
console.log(name);
const filename = "C:\\Users\\k1114\\Downloads\\" + name;
console.log(filename);
// 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();
});
},
3.获取文件流并上传后端
直接将视频流定义为file对象,通过post方法上传接口
//上传至后端
let fileobj = new File(
[blob],
name,
{
type: "video/mp4",
});
let formData = new FormData(); //创建form对象
formData.append("filename", fileobj); //通过append向form对象添加数据
console.log(formData.get("filename")); //FormData私有类对象,访问不到,可以通过get判断值是否传进去
//上传
this.$axios
.post("http://127.0.0.1:8000/runcase/start", formData, {
headers: { "Content-Type": "none" },
}) //请求头要为表单
.then((response) => {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
4.按钮事件
通过 document.getElementById("record_btu") 获取按钮对象,并根据点击事件修改按钮内容和样式
根据 this.isRecord (boolen)判断视频录制状态,在进入页面时声明为false,按钮触发点击事件后修改为对立状态
因为在录制时要在页面的video窗口实时回显,如果不静音页面会产生回音,所以在开启摄像头时静音页面
触发按钮事件开启摄像头后直接开始录制会触发错误(我不知道具体原因,可能是摄像头还没有初始化完毕),所以设置了延时执行录制文章来源:https://www.toymoban.com/news/detail-628301.html
recordOrStop() {
if (this.isRecord) {
document.getElementById("record_btu").innerText = "视频录制";
document.getElementById("record_btu").style.backgroundColor =
"rgb(22, 204, 195)";
document.getElementById("record_btu").style.color = "black";
this.stop();
} else {
this.getCamera(); //打开摄像头
this.mutePage(); //将页面静音
document.getElementById("record_btu").innerText = "结束录制";
document.getElementById("record_btu").style.backgroundColor =
"rgb(255 99 71)";
document.getElementById("record_btu").style.color = "white";
setTimeout(() => {
// 方法区
this.record();
}, 2000); //设置录制延迟 2s
}
完整JS代码文章来源地址https://www.toymoban.com/news/detail-628301.html
<script>
export default {
data() {
return {
mediaStreamTrack: {}, // 退出时关闭摄像头
video_stream: "", // 视频stream
recordedBlobs: [], // 视频音频 blobs
isRecord: false, // 视频是否正在录制
};
},
mounted() {
// 进入页面 调用摄像头
// this.getCamera();
// this.mutePage();
window.onerror = () => {
// mes 报错信息, source 那个文件, line 第几行, column 第几列, error 错误的对象
this.$notify({
title: "录制失败",
message: "请点击结束录制并重新尝试",
type: "warning",
});
};
},
methods: {
// Mute a singular HTML5 element
muteMe(elem) {
elem.muted = true;
elem.pause();
},
// Try to mute all video and audio elements on the page
mutePage() {
document
.querySelectorAll("video, audio")
.forEach((elem) => this.muteMe(elem));
},
// 调用打开摄像头功能
getCamera() {
// 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
navigator.mediaDevices
.getUserMedia({
video: true,
audio: 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) {
document.getElementById("record_btu").innerText = "视频录制";
document.getElementById("record_btu").style.backgroundColor =
"rgb(22, 204, 195)";
document.getElementById("record_btu").style.color = "black";
this.stop();
} else {
this.getCamera();
this.mutePage();
document.getElementById("record_btu").innerText = "结束录制";
document.getElementById("record_btu").style.backgroundColor =
"rgb(255 99 71)";
document.getElementById("record_btu").style.color = "white";
setTimeout(() => {
// 方法区
this.record();
}, 2000);
}
},
// 视频录制
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);
console.log(name);
const filename = "C:\\Users\\k1114\\Downloads\\" + name;
console.log(filename);
// 0.5s后自动下载视频
setTimeout(() => {
document.getElementById("downLoadLink").click();
}, 500);
//上传至后端
let fileobj = new File(
[blob],
name,
{
type: "video/mp4",
});
let formData = new FormData(); //创建form对象
formData.append("filename", fileobj); //通过append向form对象添加数据
console.log(formData.get("filename")); //FormData私有类对象,访问不到,可以通过get判断值是否传进去
//上传
this.$axios
.post("http://127.0.0.1:8000/runcase/start", formData, {
headers: { "Content-Type": "none" },
}) //请求头要为表单
.then((response) => {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
};
},
// 停止录制
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>
CSS部分
<style>
.CallCamera {
flex-wrap: wrap;
align-content: space-between;
}
.video-box {
width: 800px;
height: 600px;
text-align: center;
}
video {
width: 740px;
height: 555px;
background-color: black;
object-fit: fill;
margin: auto;
/*position: relative;*/
/*position: absolute;*/
}
canvas {
width: 100%;
height: 100%;
}
.CallCamera button {
width: 100px;
height: 40px;
position: relative;
margin: auto;
border-radius: 15px;
background-color: rgb(22, 204, 195);
cursor: pointer;
transition: 0.5s;
top: 20px;
}
.CallCamera button:hover {
font-size: 15px;
background-color: rgb(255 99 71);
color: white;
}
.img_bg_camera img {
width: 300px;
height: 200px;
}
</style>
到了这里,关于VUE+Django实现前端开启摄像头录制存储视频并直接上传后端的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!