Source: webcam.js

import * as THREE from "three";
import EventEmitter from "./event-emitter.js";

/** Class to setup the webcam. */
class Webcam extends EventEmitter {
  #video;

  /**
   * Create a Webcam.
   * @param constraints {Object} - options to use for initialising the camera.
   * This is the same constraints object as used by standard MediaDevices API.
   * @param {string} videoElementSelector - selector to obtain the HTML video
   * element to render the webcam feed. If a falsy value (e.g. null or
   * undefined), a video element will be created.
   */

  constructor(
    constraints = { video: { facingMode: "environment" } },
    videoElementSelector,
  ) {
    super();
    this.sceneWebcam = new THREE.Scene();
    if (!videoElementSelector) {
      this.#video = document.createElement("video");
      this.#video.setAttribute("autoplay", true);
      this.#video.setAttribute("playsinline", true);
      this.#video.style.display = "none";
      document.body.appendChild(this.#video);
    } else {
      this.#video = document.querySelector(videoElementSelector);
    }
    this.texture = new THREE.VideoTexture(this.#video);
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices
        .getUserMedia(constraints)
        .then((stream) => {
          this.#video.addEventListener("loadedmetadata", () => {
            this.#video.setAttribute("width", this.#video.videoWidth);
            this.#video.setAttribute("height", this.#video.videoHeight);
            this.#video.play();
            /**
             * Webcam started event.
             * @event Webcam#webcamstarted
             * @param {Object} event object containing 'texture' - the texture the webcam will stream to.
             */
            this.emit("webcamstarted", { texture: this.texture });
          });
          this.#video.srcObject = stream;
        })
        .catch((e) => {
          /**
           * Webcam error event.
           * @event Webcam#webcamerror
           * @param {Object} event object with 'code' and 'message' fields.
           */
          this.emit("webcamerror", {
            code: e.name,
            message: e.message,
          });
        });
    } else {
      this.emit("webcamerror", {
        code: "LOCAR_NO_MEDIA_DEVICES_API",
        message: "Media devices API not supported",
      });
    }
  }

  /**
   * Free up the memory associated with the webcam.
   * Should be called when your application closes.
   */
  dispose() {
    this.texture.dispose();
  }
}

export default Webcam;