vue 调用pc端本地摄像头、麦克风实现拍照、录视频、录音 并上传
自己写blog只是为了下次方便使用 过程确实很烦 ,自己摸索加各大网站cv查看
可以直接使用
1、调用摄像头拍照 录屏
首先是npm i vue-video-player -D 安装依赖
在main.js里面引入
import VideoPlayer from 'vue-video-player'
require('video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
Vue.use(VideoPlayer)
此处代码主要是用来覆盖video的播放按钮样式
require(’./styles/video.css’);具体内容如下
.video-js .vjs-big-play-button {
width: 72px;
height: 72px;
border-radius: 100%;
z-index: 100;
background-color: #ffffff;
border: solid 1px #979797;
}
<template>
<div class="main ">
<div class="left">
<div class="vedio">
<div class="shexiang">
<!--图片展示-->
<video
ref="video"
width="350px"
height="265px"
autoplay
></video>
<div class="btnsBackground">
<el-popover
placement="top-start"
width="160"
trigger="click"
>
<div style="text-align: left; margin: 0">
<div style="letter-spacing:1px">
间隔 <input
v-model="everyTime"
type="text"
id="everyTime"
style="width:30px;color:#41CFB1"
>
秒拍一次;</div>
<div style="letter-spacing:1px">
最长拍照时间<input
v-model="allTime"
type="text"
id="allTime"
style="width:30px;color:#41CFB1;margin-top:10px"
> 秒</div>
<el-checkbox
v-model="checked"
style="margin-top:10px"
>连拍模式</el-checkbox>
</div>
<el-image
slot="reference"
class="photo1"
:src="require('@/assets/images/luxiang/setting.png')"
fit="cover"
></el-image>
</el-popover>
<el-image
class="photo2"
@click="recordMedia"
:src="require('@/assets/images/luxiang/video.png')"
fit="cover"
></el-image>
<el-image
@click="takephoto"
class="photo3"
:src="require('@/assets/images/luxiang/photo.png')"
fit="fill"
></el-image>
</div>
</div>
<span class="greySpan"> 注:1、录制视频文件时,一个视频时长不能超过一分钟;</span>
<span
class="greySpan"
style="margin-left:24px"
> 2、点击设置按钮,可选择连拍模式</span>
</div>
</div>
<div class="right">
//通过canvas绘画 <!--canvas截取流-->
<canvas
width="350px"
height="265px"
ref="canvas"
v-show="false"
></canvas>
<div class="right1">
<div
class="box"
v-for="(item, index) in dataList"
:key="'photo-' + index"
>
<div
v-if="item.type=='vedio'"
style="width: 100%; height:100%;border-radius: 5px;cursor: pointer;"
@click="preVideo(item.src)"
>
<video
:src="item.src"
style="width: 100%; height:100%;object-fit:cover"
></video>
<div class="player">
<img
:src="require('@/assets/images/luxiang/player.png')"
alt=""
>
</div>
</div>
<el-image
v-if="item.type=='photo'"
:preview-src-list="prephotoList"
:src="item.src"
fit="cover"
style="width: 100%;height: 100%;border-radius: 5px;"
></el-image>
<div class="del-icon"> <i
class="el-icon-delete "
@click="deletePhoto(index)"
></i></div>
</div>
</div>
//手动分页
<el-pagination
small
v-if="pagation.total>18"
:pager-count="5"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
:current-page.sync="pagation.currentPage"
:page-size="pagation.pagesize"
layout="prev, pager, next,total"
:total="pagation.total"
>
</el-pagination>
</div>
<div class="text-center margin-b10 subBtn">
<el-button
@click="closeContent"
class="width-80 margin-r20"
>取 消</el-button>
<el-button
type="primary"
@click="submitContent"
class="width-80"
>确 定</el-button>
</div>
//点击视频预览
<el-dialog
modal
class="videoShowDialog"
destroy-on-close
close-on-press-escape
close-on-click-modal
:visible.sync="preVideoShow"
>
<video-player
onPlayerPlay
class="video-player vjs-custom-skin"
ref="videoPlayer"
:playsinline="true"
style="width:100%;height:100%"
:options="playerOptions"
:x5-video-player-fullscreen="true"
></video-player>
</el-dialog>
</div>
</template>
<script>
import "video.js/dist/video-js.css";
import "vue-video-player/src/custom-theme.css";
import {
videoPlayer } from "vue-video-player";
import {
dataDetailApi,
showFloderApi,
addFolderApi,
} from "@/services/api/apply.js";
import Bus from "@/assets/js/bus";
import UploadBigFile from "./vedioUpload.vue";
import {
getUserInfo, UUID } from "@/utils/helpers.js";
import {
addDataFileApi } from "@/services/api/apply.js";
import {
uploadFileApi, uploadMoreFileApi } from "@/services/api/common.js";
export default {
components: {
UploadBigFile, videoPlayer },
props: ["dragShow", "name", "datasetId"],
data() {
return {
time: null,
time1: null,
time2: null,
preVideoShow: false,
pagation: {
pagesize: 18,
total: 0,
currentPage: 1,
},
checked: false,
recordType: 0, //0 未录制 1 录制中
mediaRecorder: null,
photoAndVedioList: [],
photoList: [],
prephotoList: [],
everyTime: "",
allTime: "",
list: [],
remotePath: "",
inputName: "",
editorNameShow: false,
addPathData: {
},
id: "",
addPath: "",
data: [],
defaultProps: {
children: "children",
label: "name",
},
backFloader: [{
}],
firstPath: "",
nowPath: "",
lastPath: "",
fileData: {
listDirectory: [], listFile: [], fileServer: "" },
playerOptions: {
playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
autoplay: false, //如果true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 导致视频一结束就重新开始。
preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: "zh-CN",
aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [
{
type: "video/mp4", //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
src: "", //url地址
},
],
poster: "", //你的封面地址
// width: document.documentElement.clientWidth, //播放器宽度
notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
controlBar: {
timeDivider: true,
durationDisplay: true,
remainingTimeDisplay: false,
fullscreenToggle: true, //全屏按钮
},
},
};
},
methods: {
//弹窗预览视频
preVideo(url) {
this.preVideoShow = true;
this.playerOptions.sources[0].src = url;
},
handleSizeChange(size) {
this.pagation.pagesize = size;
},
//切换页码
handleCurrentChange(currentPage) {
this.pagation.currentPage = currentPage;
},
//视频录制按钮控制
recordMedia() {
if (this.recordType == 0) {
this.startMakeVideo();
} else {
this.stopMakeVideo();
}
},
// 调用摄像头
callCamera() {
// H5调用电脑摄像头API
navigator.mediaDevices
.getUserMedia({
video: true,
// audio: true,
})
.then((success) => {
// 摄像头开启成功
this.$refs["video"].srcObject = success;
this.mediaRecorder = new MediaRecorder(success, {
//因为视频和音频分开录制这里直接注释了
// audioBitsPerSecond: 128000, // 音频码率
videoBitsPerSecond: 1000000, // 视频码率
mimeType: "video/webm;codecs=h264", // 编码格式
});
// 实时拍照效果
this.$refs["video"].play();
})
.catch((error) => {
console.error("摄像头开启失败,请检查摄像头是否可用!");
});
},
//录制视频
startMakeVideo() {
this.mediaRecorder.start();
console.log("开始采集");
this.recordType = 1;
this.time = window.setTimeout(() => {
this.stopMakeVideo();
}, 60 * 1000);
},
//停止录制并上传
stopMakeVideo() {
this.mediaRecorder.stop();
// 事件
let _this = this;
this.mediaRecorder.ondataavailable = function (e) {
console.log(e);
//构建文件blob流
let blob = new Blob([e.data], {
type: "video/mp4" });
//调用转base64函数
_this.blobToBase64(blob).then((res) => {
_this.photoList.push({
src: res, type: "vedio" });
_this.pagation.total = _this.photoList.length;
// 转化后的base64
console.log("base64", res);
});
console.log("停止采集视频");
};
this.recordType = 0;
window.clearTimeout(this.time);
},
//拍照
takephoto() {
if (this.checked) {
if (this.everyTime && this.allTime) {
//设置参数后定时拍照
let time1 = window.setInterval(() => {
this.savePhoto();
}, this.everyTime * 1000);
let time2 = setTimeout(() => {
window.clearInterval(time1);
}, this.allTime * 1000);
} else {
this.$message.error("请设置连拍参数");
}
} else {
this.savePhoto();
}
},
//图片展示在右侧 并保存在数组里
savePhoto() {
let ctx = this.$refs["canvas"].getContext("2d");
// 把当前视频帧内容渲染到canvas上
ctx.drawImage(this.$refs["video"], 0, 0, 350, 265);
// 转base64格式、图片格式转换、图片质量压缩
let imgBase64 = this.$refs["canvas"].toDataURL("image/png", 0.7); // 由字节转换为KB 判断大小
console.log(this.dataURLtoFile(imgBase64));
this.photoList.push({
src: imgBase64, type: "photo" });
this.prephotoList.push(imgBase64);
this.pagation.total = this.photoList.length;
},
//blob转base64
blobToBase64(blob) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
// readAsDataURL
fileReader.readAsDataURL(blob);
fileReader.onerror = () => {
reject(new Error("blobToBase64 error"));
};
});
},
//base64转文件
dataURLtoFile(dataurl) {
let arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], UUID(), {
type: mime });
},
//多文件列表上传
async uploadMoreFile() {
let formData = new FormData();
this.photoList.forEach((it) => {
formData.append(