Bootstrap

JS PC端调用摄像头录视频截图上传文件

创建 Catcher 类

class Catcher {
  URL; // 上传地址
  dataStream; // 流数据
  mediaStream; //媒体流
  mediaRecorder; //录制对象
  state; // 录制状态

  constructor(options) {
    this.video = document.getElementById(options.video);
    this.URL = options.URL;
    this.uploadParams = options.params;
  }

  /**
   * 获取用户媒体设备并处理兼容的问题
   * @param videoEnable {boolean} 是否启用摄像头
   * @param audioEnable {boolean} 是否启用麦克风
   * @param callback {Function} 处理回调
   */
  getMedia(videoEnable, audioEnable, callback) {
    navigator.getMedia =
      navigator.getMedia ||
      navigator.webkitGetUserMedia ||
      navigator.mozGetUserMedia ||
      navigator.msGetUserMedia ||
      window.getMedia;

    const constraints = {
      video: videoEnable,
      audio: audioEnable,
    };

    if (navigator.mediaDevices && navigator.mediaDevices.getMedia) {
      navigator.mediaDevices
        .getMedia(constraints)
        .then(function (stream) {
          callback(false, stream);
        })
        ["catch"](function (err) {
          callback(err);
        });
    } else if (navigator.getMedia) {
      navigator.getMedia(
        constraints,
        function (stream) {
          callback(false, stream);
        },
        function (err) {
          callback(err);
        }
      );
    } else {
      callback(new Error("Not support userMedia"));
    }
  }
  /**
   * 关闭媒体流
   * @param stream {MediaStream} 需要关闭的流
   */
  closeStream(stream) {
    if (typeof stream.stop === "function") {
      stream.stop();
    } else {
      const trackList = [stream.getAudioTracks(), stream.getVideoTracks()];
      for (let i = 0; i < trackList.length; i++) {
        const 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();
            }
          }
        }
      }
    }
  }

  init() {
    if (this.video != null) {
      this.state = false;
      this.getMedia(true, true, (err, stream) => {
        if (err) {
          throw err;
        } else {
          this.mediaRecorder = new MediaRecorder(stream); // 创建 MediaRecorder 实例,记录获取到的媒体流
          this.mediaStream = stream;
          const chunks = [];
          this.video.srcObject = stream;
          this.video.play();
          this.mediaRecorder.ondataavailable = function (e) {
            this.blobs.push(e.data);
            chunks.push(e.data);
          };
          this.mediaRecorder.blobs = [];
          this.mediaRecorder.onstop = (e) => {
            this.dataStream = new Blob(chunks, {
              type: this.mediaRecorder.mimeType,
            });
            chunks = [];
            this.uploadFile(this.dataStream);
          };
        }
      });
    }
  }
  //录制上传服务器
  uploadFile(dataStream) {
    const file = new File(
      [dataStream],
      "msr-" + new Date().toISOString().replace(/:|\./g, "-") + ".mp4",
      {
        type: "video/mp4",
      }
    );
    const url = this.URL;
    if ((url != undefined) & (url != null) & (url != "")) {
      if (confirm("录制完成,是否上传录制文件?")) {
        alert("正在上传录制文件!");
        const formData = new FormData();
        formData.append("file", file);
        const params = this.uploadParams;
        if (
          (params != undefined) &
          (params != null) &
          (typeof params == "object")
        ) {
          for (param in params) {
            formData.append(param, params[params]);
          }
        }
        fetch(url, {
          method: "post",
          headers: { "content-type": "application/x-www-form-urlencoded" },
          body: formData,
        }).then((response) => {
          if (response.status === 200) {
            this.mediaRecorder = null;
            this.mediaStream = null;
            this.dataStream = null;
            alert("上传成功");
          } else {
            alert("上传失败!");
          }
        });
      }
    }
  }
  // 截图并返回图片地址
  screenshot() {
    const canvas = document.createElement("canvas");
    canvas.width = this.video.videoWidth;
    canvas.height = this.video.videoHeight;
    canvas
      .getContext("2d")
      .drawImage(this.video, 0, 0, canvas.width, canvas.height);
    return canvas.toDataURL("image/png");
  }
  // 录制开始
  startRecorder() {
    if ((this.mediaRecorder != undefined) & (this.mediaRecorder != null)) {
      this.state = true;
      this.mediaRecorder.start();
    }
  }
  // 录制结束
  stopRecorder() {
    if ((this.mediaRecorder != undefined) & (this.mediaRecorder != null)) {
      this.state = false;
      this.mediaRecorder.stop();
      this.closeStream(this.mediaStream);
    }
  }
  // 关闭摄像头
  closeMedia() {
    if ((this.mediaStream != undefined) & (this.mediaStream != null)) {
      this.closeStream(this.mediaStream);
    }
  }
}

直接在HTML文件中调用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>View catch</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }

      .container {
        display: flex;
      }

      #video {
        background: skyblue;
      }

      .operation {
        padding: 20px;
      }

      .operation button {
        display: block;
        margin-bottom: 10px;
      }
    </style>
  </head>

  <body>
    <div class="container">
      <video
        id="video"
        width="500px"
        height="300px"
        autoplay="autoplay"
      ></video>
      <div class="operation">
        <button id="open">开启摄像头</button>
        <button id="recorder">点击录制</button>
        <button id="close">关闭摄像头</button>
        <button id="screenshot">截图</button>
      </div>
    </div>

    <script src="./js/catcher.js"></script>
    <script>
      const catcher = new Catcher({
        video: "video",
        uploadURI: "123",
        params: {},
      });
      const open = document.getElementById("open");
      const screenshot = document.getElementById("screenshot");
      const recorder = document.getElementById("recorder");
      const close = document.getElementById("close");
      open.addEventListener("click", () => {
        catcher.init();
      });
      screenshot.addEventListener("click", () => {
        const src = catcher.screenshot();
        const img = document.createElement("img");
        img.src = src;
        document.getElementsByTagName("body")[0].appendChild(img);
      });
      recorder.addEventListener("click", () => {
        if (catcher.recorderState == false) {
          recorder.innerText = "录制中";
          catcher.startRecorder();
        } else if (catcher.recorderState == true) {
          recorder.innerText = "点击录制";
          catcher.stopRecorder();
        }
      });
      close.addEventListener("click", () => {
        catcher.closeMedia();
      });
    </script>
  </body>
</html>

;