思路

  1. 获取摄像头权限navigator.getUserMedia({ video: true }, success(stream), error(error))
  2. 获取摄像头列表navigator.mediaDevices.enumerateDevices().then((mediaDevices)=>{})
  3. 将多个摄像头流数据展示在页面navigator.mediaDevices.getUserMedia(constraints).then((stream) => {videoElement.srcObject = stream;})

具体代码(以两个为例)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <p>
      <button id="open">打开摄像头</button>
      <button id="play">播放摄像头</button>
      <button id="snap">截取并上传</button>
    </p>
    <video id="video" autoplay playsinline></video>
    <video id="video2" autoplay playsinline></video>

    <canvas id="canvas1"></canvas>
    <canvas id="canvas2"></canvas>
  </body>
  <script>
    navigator.getUserMedia =
      navigator.getUserMedia ||
      navigator.webkitGetUserMedia ||
      (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ||
      navigator.mozGetUserMedia ||
      navigator.msGetUserMedia; // 获取媒体对象(这里指摄像头)

    let currentStream = null,
      cameraList = [],
      videoElement = document.getElementById("video"),
      videoElement2 = document.getElementById("video2");

    // 1. 获取摄像头列表
    function getCameraList() {
      navigator.mediaDevices.enumerateDevices().then((mediaDevices) => {
        cameraList = mediaDevices;
        console.log(JSON.stringify(mediaDevices));
      });
    }

    /*** 格式如下
    [
        {
            "deviceId": "4d9f6ffec7e62d5d7cf94d8943ff2b7b77de8b531822e7f514416f004079a5c6",
            "kind": "videoinput",
            "label": "LHT-820BW (0c45:6300)",
            "groupId": "cc83dc58f13c88df5a4b96bce0af43a7779179ee54508f71457ea9f900cf93d4"
        },
        {
            "deviceId": "539cbf57edd3ee728c912c222920ee9cc0971373938da4bd43545a92cb9b5c9f",
            "kind": "videoinput",
            "label": "LHT-820VM31B (0c45:6367)",
            "groupId": "49f8c5f91a4880636bbf919ee8c567e416252593a6754d84fac8614f4581e7e4"
        },
        {
            "deviceId": "",
            "kind": "audiooutput",
            "label": "",
            "groupId": "f1f9c2fc668cfe64155c501cbcdee5ef12d76a1b14134db19929a94ef1d93fcf"
        }
    ]
    */

    // 2. 切换摄像头
    function toggleCamera(deviceId) {
      if (currentStream !== null) {
        stopMediaTracks(currentStream); // 先停止当前摄像头视频流的播放
      }

      // 组装数据(固定格式)
      const constraints = {
        video: {
          deviceId: {
            exact: deviceId, // 要切换到的设备ID
          },
        },
        audio: false,
      };

      navigator.mediaDevices
        .getUserMedia(constraints) // 传入要切换到的设备
        .then((stream) => {
          currentStream = stream; // 获取当前视频流
          video.srcObject = stream; // 将当前视频流放到video标签里
          return navigator.mediaDevices.enumerateDevices(); // 获取设备列表
        })
        .then((mediaDevices) => {}) // 这里可以再次获取到摄像头设备列表
        .catch((error) => {
          console.error(error);
        });
    }

    // 2-2. 多摄像头同时播放
    function playCamera(deviceId, videoEle, env = null) {
      // 组装数据(固定格式)
      let constraints = {};
      if (!env) {
        constraints = {
          video: {
            deviceId: {
              exact: deviceId, // 要切换到的设备ID
            },
          },
          audio: false,
        };
      } else {
        constraints = {
          video: {
            facingMode: "environment",
          },
          audio: false,
        };
      }

      navigator.mediaDevices
        .getUserMedia(constraints) // 传入要切换到的设备
        .then((stream) => {
          if (videoEle) videoEle.srcObject = stream; // 将当前视频流放到video标签里
          return navigator.mediaDevices.enumerateDevices(); // 获取设备列表
        })
        .then((mediaDevices) => {}) // 这里可以再次获取到摄像头设备列表
        .catch((error) => {
          console.error(error);
        });
    }

    // 3. 停止当前视频流的播放
    function stopMediaTracks(stream) {
      stream.getTracks().forEach((track) => {
        track.stop();
      });
    }

    // 运行demo 每隔10秒切换摄像头播放数据 (两个摄像头)
    /* getCameraList();
    setInterval(() => {
      let playCount = 0;
      cameraList
        .filter((v) => v.deviceId !== "") // 清除空设备
        .map((v) => {
          if (v.isPlay !== true && playCount === 0) {
            toggleCamera(v.deviceId);
            v.isPlay = true;
            playCount++;
            console.log("toggle", v.deviceId);
          } else {
            v.isPlay = false;
          }
        });
    }, 10000); */

    // 获取摄像头权限
    document.getElementById("open").onclick = () => {
      playCamera(null, null, true);
    };

    // 运行demo 两个摄像头同时播放
    document.getElementById("play").onclick = () => {
      getCameraList();
      setTimeout(() => {
        const devs = cameraList.filter((v) => v.deviceId !== ""); // 清除空设备
        playCamera(devs[0].deviceId, document.getElementById("video"));
        playCamera(devs[1].deviceId, document.getElementById("video2"));
      });
    };

    // 截图并上传
    document.getElementById("snap").addEventListener(
      "click",
      function () {
        const can1 = document.getElementById("canvas1");
        const can2 = document.getElementById("canvas2");
        const ctx1 = can1.getContext("2d");
        const ctx2 = can2.getContext("2d");
        // 视频放到canvas里
        ctx1.drawImage(videoElement, 0, 0, 200, 150);
        ctx2.drawImage(videoElement2, 0, 0, 200, 150);

        // 转成图像blob对象 保存在formData里
        const fd = new FormData();
        can1.toBlob(function (blob) {
          fd.append("img1", blob);
        });
        can2.toBlob(function (blob) {
          fd.append("img2", blob);
        });

        // 上传到接口 有延迟需要等待blob对象转换完成 或者写成Promise方式
        setTimeout(() => {
          fetch("/api/upload/xxx", {
            method: "POST",
            body: fd,
          });
        }, 2000);
      },
      false
    );
  </script>
</html>

zpfei
186 声望7 粉丝

往事如风~