Vue调用摄像头录制视频和音频并上传给后端或下载到本地

下载插件

npm install --save webm-duration-fix

代码及作用

调用摄像头


callCamera () {
        let _this = this;
        MediaUtils.getUserMedia(true, true, function (err, stream) {
          if (err) {
            throw err;
          } else {
            // 通过 MediaRecorder 记录获取到的媒体流
            const mimeType = 'video/webm;codecs=vp8,opus';
            mediaRecorder = new MediaRecorder(stream, {
              // mimeType: "video/webm;codecs=vp9",
              mimeType: mimeType,
            });
            mediaStream = stream;
            var chunks = []
            var video = _this.$refs.videos;
            video["srcObject"] = stream;
            video.play();// 播放实时画面
            mediaRecorder.ondataavailable = function (e) {
              mediaRecorder.blobs.push(e.data);
              chunks.push(e.data);
            };
            mediaRecorder.blobs = [];

            mediaRecorder.onstop = async () => {
              recorderFile = await fixWebmDuration(new Blob(chunks, { type: mimeType }));
              console.log(recorderFile);
              var url = URL.createObjectURL(recorderFile)
              var videosreplay = _this.$refs.videosreplay;
              videosreplay.setAttribute("src", url);
              console.log('url', url)
              chunks = [];
              if (null != stopRecordCallback) {
                stopRecordCallback();
              }
            };
            _this.record()
          }
        });
      },

开始结束录制

record () {
        if (this.recordtype == "ING") {
          this.stopRecord(() => {
            console.log("结束录制");
            this.toggleReplayVideo()
          });
        }
        else if (this.recordtype == "BEGIN") {
          console.log("开始录制");
          this.startAudio();
          mediaRecorder.start();
          startTime = Date.now();
          this.recordtype = "ING";
        }
      },

对录像时长进行记录


      startAudio () {
        this.timer = setInterval(() => {
          this.recordtime += 1000;
          if (this.recordtime == 1000000) {
            this.stopRecord();
          }
          this.second++;
          if (this.second >= 60) {
            this.second = 0;
            this.minute = this.minute + 1;
          }

          if (this.minute >= 60) {
            this.minute = 0;
            this.hour = this.hour + 1;
          }
          console.log(this.recordtime)
        }, 1000);
      },

停止录像时终止录制器,关闭媒体流并清除时长记录定时器

      stopRecord (callback) {
        this.recordtype = "END";
        this.showReplay = true;
        stopRecordCallback = callback;
        clearInterval(this.timer);
        // 终止录制器
        mediaRecorder.stop();
        // 关闭媒体流
        MediaUtils.closeStream(mediaStream);
        var videosreplay = this.$refs.videosreplay;
        videosreplay.onended = () => {
          this.playtime = 0;
          this.replayVideo = false;
          clearInterval(this.playtimer);
        };
        videosreplay.onclick = () => {
          this.showReplay = !this.showReplay;
        };
      },

回放

      toggleReplayVideo () {
        console.log('播放中...')
        this.replayVideo = !this.replayVideo;
        this.showReplay = false;
        var videosreplay = this.$refs.videosreplay;
        if (this.replayVideo) {
          videosreplay.play().catch(err => {
            this.$message.error(err.message);
            console.log(err);
          });
          this.playtimer = setInterval(() => {
            this.playtime += 1000;
          }, 1000);
        } else {
          videosreplay.pause();
          clearInterval(this.playtimer);
        }
      },

下载视频

指定且只能指定,下载后的默认文件名字和文件后缀。注意,可以不指定后缀名,浏览器会根据数据类型自动为其匹配后缀名,但是最好指定后缀。

<a href="base64..." download="after">SAVE</a>

下载后的文件名为after.jpg
download属性不能指定下载路径;
当 download 属性值为空时,下载的文件的名字和扩展名与源文件一致;当href为base64编码的图像数据时,则下载后文件名也是那么离谱得长。

<a href="base64..." download>SAVE</a>

下载后的文件名为data_image_jpeg;base64,… .jpg

      download () {
        var url = URL.createObjectURL(recorderFile)
        console.log("URLLLLLL", url)
        const a = document.createElement("a");
        document.body.appendChild(a);
        a.style.display = "none";
        a.href = url;
        if (this.fileName) {
          a.download = this.fileName + ".mp4";
        } else {
          a.download = new Date() + ".mp4";
        }
        a.click();
        window.URL.revokeObjectURL(url);
      },

下载或上传给后端

      submit () {
        let that = this;
        console.log(recorderFile)
        // 下载
        this.download()
        let file = new File(
          [recorderFile],
          "msr-" + new Date().toISOString().replace(/:|\./g, "-") + ".mp4",
          {
            type: "video/mp4",
          }
        );
        let config = {
          headers: { "Content-Type": "multipart/form-data" }
        }
        console.log('file', file)
        const formdata = new FormData()
        formdata.append("file", file);
        // 传给后端
        // axios.post('/video', formdata, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },) //请求头要为表单
        //   .then(response => {
        //     console.log('video', response.data);
        //     this.yy_score = parseInt(response.data.data + 0.5)
        //     that.progress = response.data.data * 1.0 / 23 * 100
        //   })
        //   .catch(function (error) {
        //     that.$message({
        //       message: error,
        //       type: 'error'
        //     });
        //     console.log(error);
        //   })
      },
var MediaUtils = {
    /**
     * 获取用户媒体设备(处理兼容的问题)
     * @param videoEnable {boolean} - 是否启用摄像头
     * @param audioEnable {boolean} - 是否启用麦克风
     * @param callback {Function} - 处理回调
     */
    getUserMedia: function (videoEnable, audioEnable, callback) {
      navigator.getUserMedia =
        navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia ||
        window.getUserMedia;
      var constraints = { video: videoEnable, audio: audioEnable };
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices
          .getUserMedia(constraints)
          .then(function (stream) {
            callback(false, stream);
          })
        ["catch"](function (err) {
          callback(err);
        });
      } else if (navigator.getUserMedia) {
        navigator.getUserMedia(
          constraints,
          function (stream) {
            callback(false, stream);
          },
          function (err) {
            callback(err);
          }
        );
      } else {
        callback(new Error("Not support userMedia"));
      }
    },

    /**
     * 关闭媒体流
     * @param stream {MediaStream} - 需要关闭的流
     */
    closeStream: function (stream) {
      if (typeof stream.stop === "function") {
        stream.stop();
      } else {
        let trackList = [stream.getAudioTracks(), stream.getVideoTracks()];

        for (let i = 0; i < trackList.length; i++) {
          let tracks = trackList[i];
          if (tracks && tracks.length > 0) {
            for (let j = 0; j < tracks.length; j++) {
              let track = tracks[j];
              if (typeof track.stop === "function") {
                track.stop();
              }
            }
          }
        }
      }
    },
  };
  var startTime, mediaRecorder, mediaStream, stopRecordCallback, recorderFile;

页面完整代码

<template>
  <div>
    <video id="video" autoplay ref="videos" style="width: 400px;height: 400px;" muted></video>
    <video style="width: 400px;height: 400px;" id="videosreplay" src="" ref="videosreplay"></video>
    <button @click="callCamera()">开始录制</button>
    <button @click="record()">结束录制</button>
    <button @click="submit()">下载或上传</button>
  </div>
</template>
<script>
  import axios from 'axios'
  import fixWebmDuration from 'webm-duration-fix'

  export default {
    name: "Test",
    data () {
      return {
        progress: 0,
        replayVideo: false,
        recordtype: "BEGIN",
        showReplay: true,
        timer: 0,
        recordtime: 0,
        second: 0,
        minute: 0,
        hour: 0,
        playtime: 0,
        playtimer: 0,
        yy_score: 0,
        cnt_sum: 0,
        ansMaxTime: 0,
        ansBeginTime: 0,
        ansMaxBeginTime: 0,

      }
    },
    methods: {
      // 调用摄像头
      callCamera () {
        let _this = this;
        MediaUtils.getUserMedia(true, true, function (err, stream) {
          if (err) {
            throw err;
          } else {
            // 通过 MediaRecorder 记录获取到的媒体流
            const mimeType = 'video/webm;codecs=vp8,opus';
            mediaRecorder = new MediaRecorder(stream, {
              // mimeType: "video/webm;codecs=vp9",
              mimeType: mimeType,
            });
            mediaStream = stream;
            var chunks = []
            var video = _this.$refs.videos;
            video["srcObject"] = stream;
            video.play();// 播放实时画面
            mediaRecorder.ondataavailable = function (e) {
              mediaRecorder.blobs.push(e.data);
              chunks.push(e.data);
            };
            mediaRecorder.blobs = [];

            mediaRecorder.onstop = async () => {
              recorderFile = await fixWebmDuration(new Blob(chunks, { type: mimeType }));
              console.log(recorderFile);
              var url = URL.createObjectURL(recorderFile)
              var videosreplay = _this.$refs.videosreplay;
              videosreplay.setAttribute("src", url);
              console.log('url', url)
              chunks = [];
              if (null != stopRecordCallback) {
                stopRecordCallback();
              }
            };
            _this.record()
          }
        });
      },
      record () {
        if (this.recordtype == "ING") {
          this.stopRecord(() => {
            console.log("结束录制");
            this.toggleReplayVideo()
          });
        }
        else if (this.recordtype == "BEGIN") {
          console.log("开始录制");
          this.startAudio();
          mediaRecorder.start();
          startTime = Date.now();
          this.recordtype = "ING";
        }
      },

      // 对录像时长进行记录
      startAudio () {
        this.timer = setInterval(() => {
          this.recordtime += 1000;
          if (this.recordtime == 1000000) {
            this.stopRecord();
          }
          this.second++;
          if (this.second >= 60) {
            this.second = 0;
            this.minute = this.minute + 1;
          }

          if (this.minute >= 60) {
            this.minute = 0;
            this.hour = this.hour + 1;
          }
          console.log(this.recordtime)
        }, 1000);
      },

      // 停止录像时终止录制器,关闭媒体流并清除时长记录定时器
      stopRecord (callback) {
        this.recordtype = "END";
        this.showReplay = true;
        stopRecordCallback = callback;
        clearInterval(this.timer);
        // 终止录制器
        mediaRecorder.stop();
        // 关闭媒体流
        MediaUtils.closeStream(mediaStream);
        var videosreplay = this.$refs.videosreplay;
        videosreplay.onended = () => {
          this.playtime = 0;
          this.replayVideo = false;
          clearInterval(this.playtimer);
        };
        videosreplay.onclick = () => {
          this.showReplay = !this.showReplay;
        };
      },
      // 回放
      toggleReplayVideo () {
        console.log('播放中...')
        this.replayVideo = !this.replayVideo;
        this.showReplay = false;
        var videosreplay = this.$refs.videosreplay;
        if (this.replayVideo) {
          videosreplay.play().catch(err => {
            this.$message.error(err.message);
            console.log(err);
          });
          this.playtimer = setInterval(() => {
            this.playtime += 1000;
          }, 1000);
        } else {
          videosreplay.pause();
          clearInterval(this.playtimer);
        }
      },
      // 下载视频
      download () {
        var url = URL.createObjectURL(recorderFile)
        console.log("URLLLLLL", url)
        const a = document.createElement("a");
        document.body.appendChild(a);
        a.style.display = "none";
        a.href = url;
        if (this.fileName) {
          a.download = this.fileName + ".mp4";
        } else {
          a.download = new Date() + ".mp4";
        }
        a.click();
        window.URL.revokeObjectURL(url);
      },
      // 下载或上传
      submit () {
        let that = this;
        console.log(recorderFile)
        // 下载
        this.download()
        let file = new File(
          [recorderFile],
          "msr-" + new Date().toISOString().replace(/:|\./g, "-") + ".mp4",
          {
            type: "video/mp4",
          }
        );
        let config = {
          headers: { "Content-Type": "multipart/form-data" }
        }
        console.log('file', file)
        const formdata = new FormData()
        formdata.append("file", file);
        // 传给后端
        // axios.post('/video', formdata, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },) //请求头要为表单
        //   .then(response => {
        //     console.log('video', response.data);
        //     this.yy_score = parseInt(response.data.data + 0.5)
        //     that.progress = response.data.data * 1.0 / 23 * 100
        //   })
        //   .catch(function (error) {
        //     that.$message({
        //       message: error,
        //       type: 'error'
        //     });
        //     console.log(error);
        //   })
      },
    }
  }
  var MediaUtils = {
    /**
     * 获取用户媒体设备(处理兼容的问题)
     * @param videoEnable {boolean} - 是否启用摄像头
     * @param audioEnable {boolean} - 是否启用麦克风
     * @param callback {Function} - 处理回调
     */
    getUserMedia: function (videoEnable, audioEnable, callback) {
      navigator.getUserMedia =
        navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia ||
        window.getUserMedia;
      var constraints = { video: videoEnable, audio: audioEnable };
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices
          .getUserMedia(constraints)
          .then(function (stream) {
            callback(false, stream);
          })
        ["catch"](function (err) {
          callback(err);
        });
      } else if (navigator.getUserMedia) {
        navigator.getUserMedia(
          constraints,
          function (stream) {
            callback(false, stream);
          },
          function (err) {
            callback(err);
          }
        );
      } else {
        callback(new Error("Not support userMedia"));
      }
    },

    /**
     * 关闭媒体流
     * @param stream {MediaStream} - 需要关闭的流
     */
    closeStream: function (stream) {
      if (typeof stream.stop === "function") {
        stream.stop();
      } else {
        let trackList = [stream.getAudioTracks(), stream.getVideoTracks()];

        for (let i = 0; i < trackList.length; i++) {
          let tracks = trackList[i];
          if (tracks && tracks.length > 0) {
            for (let j = 0; j < tracks.length; j++) {
              let track = tracks[j];
              if (typeof track.stop === "function") {
                track.stop();
              }
            }
          }
        }
      }
    },
  };
  var startTime, mediaRecorder, mediaStream, stopRecordCallback, recorderFile;
</script>

结果

录制

Vue调用摄像头录制视频和音频并上传给后端或下载到本地_第1张图片

播放

Vue调用摄像头录制视频和音频并上传给后端或下载到本地_第2张图片

下载

Vue调用摄像头录制视频和音频并上传给后端或下载到本地_第3张图片

项目代码

https://gitee.com/yuan-hongting/video

你可能感兴趣的:(前端,vue.js,音视频,javascript)