vue 调用pc端本地摄像头、麦克风实现拍照、录视频、录音 并上传到服务器指定树文件夹

本文介绍如何在Vue项目中利用HTML5 API调用PC端的摄像头和麦克风,实现拍照、录视频及录音功能,并详细说明了依赖安装、样式覆盖以及录音组件的创建过程。
摘要由CSDN通过智能技术生成

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(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值