1{"version":3,"file":"useWebCameraStream.js","sourceRoot":"","sources":["../src/useWebCameraStream.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-env browser */\nimport * as React from 'react';\n\nimport {\n  CameraReadyListener,\n  CameraType,\n  MountErrorListener,\n  WebCameraSettings,\n} from './Camera.types';\nimport * as Utils from './WebCameraUtils';\nimport { FacingModeToCameraType } from './WebConstants';\n\nconst VALID_SETTINGS_KEYS = [\n  'autoFocus',\n  'flashMode',\n  'exposureCompensation',\n  'colorTemperature',\n  'iso',\n  'brightness',\n  'contrast',\n  'saturation',\n  'sharpness',\n  'focusDistance',\n  'whiteBalance',\n  'zoom',\n];\n\nfunction useLoadedVideo(video: HTMLVideoElement | null, onLoaded: () => void) {\n  React.useEffect(() => {\n    if (video) {\n      video.addEventListener('loadedmetadata', () => {\n        // without this async block the constraints aren't properly applied to the camera,\n        // this means that if you were to turn on the torch and swap to the front camera,\n        // then swap back to the rear camera the torch setting wouldn't be applied.\n        requestAnimationFrame(() => {\n          onLoaded();\n        });\n      });\n    }\n  }, [video]);\n}\n\nexport function useWebCameraStream(\n  video: React.MutableRefObject<HTMLVideoElement | null>,\n  preferredType: CameraType,\n  settings: Record<string, any>,\n  {\n    onCameraReady,\n    onMountError,\n  }: { onCameraReady?: CameraReadyListener; onMountError?: MountErrorListener }\n): {\n  type: CameraType | null;\n  mediaTrackSettings: MediaTrackSettings | null;\n} {\n  const isStartingCamera = React.useRef<boolean | null>(false);\n  const activeStreams = React.useRef<MediaStream[]>([]);\n  const capabilities = React.useRef<WebCameraSettings>({\n    autoFocus: 'continuous',\n    flashMode: 'off',\n    whiteBalance: 'continuous',\n    zoom: 1,\n  });\n  const [stream, setStream] = React.useState<MediaStream | null>(null);\n\n  const mediaTrackSettings = React.useMemo(() => {\n    return stream ? stream.getTracks()[0].getSettings() : null;\n  }, [stream]);\n\n  // The actual camera type - this can be different from the incoming camera type.\n  const type = React.useMemo(() => {\n    if (!mediaTrackSettings) {\n      return null;\n    }\n    // On desktop no value will be returned, in this case we should assume the cameraType is 'front'\n    const { facingMode = 'user' } = mediaTrackSettings;\n    return FacingModeToCameraType[facingMode];\n  }, [mediaTrackSettings]);\n\n  const getStreamDeviceAsync = React.useCallback(async (): Promise<MediaStream | null> => {\n    try {\n      return await Utils.getPreferredStreamDevice(preferredType);\n    } catch (nativeEvent) {\n      if (__DEV__) {\n        console.warn(`Error requesting UserMedia for type \"${preferredType}\":`, nativeEvent);\n      }\n      if (onMountError) {\n        onMountError({ nativeEvent });\n      }\n      return null;\n    }\n  }, [preferredType, onMountError]);\n\n  const resumeAsync = React.useCallback(async (): Promise<boolean> => {\n    const nextStream = await getStreamDeviceAsync();\n    if (Utils.compareStreams(nextStream, stream)) {\n      // Do nothing if the streams are the same.\n      // This happens when the device only supports one camera (i.e. desktop) and the mode was toggled between front/back while already active.\n      // Without this check there is a screen flash while the video switches.\n      return false;\n    }\n\n    // Save a history of all active streams (usually 2+) so we can close them later.\n    // Keeping them open makes swapping camera types much faster.\n    if (!activeStreams.current.some((value) => value.id === nextStream?.id)) {\n      activeStreams.current.push(nextStream!);\n    }\n\n    // Set the new stream -> update the video, settings, and actual camera type.\n    setStream(nextStream);\n    if (onCameraReady) {\n      onCameraReady();\n    }\n    return false;\n  }, [getStreamDeviceAsync, setStream, onCameraReady, stream, activeStreams.current]);\n\n  React.useEffect(() => {\n    // Restart the camera and guard concurrent actions.\n    if (isStartingCamera.current) {\n      return;\n    }\n    isStartingCamera.current = true;\n\n    resumeAsync()\n      .then((isStarting) => {\n        isStartingCamera.current = isStarting;\n      })\n      .catch(() => {\n        // ensure the camera can be started again.\n        isStartingCamera.current = false;\n      });\n  }, [preferredType]);\n\n  // Update the native camera with any custom capabilities.\n  React.useEffect(() => {\n    const changes: WebCameraSettings = {};\n\n    for (const key of Object.keys(settings)) {\n      if (!VALID_SETTINGS_KEYS.includes(key)) {\n        continue;\n      }\n      const nextValue = settings[key];\n      if (nextValue !== capabilities.current[key]) {\n        changes[key] = nextValue;\n      }\n    }\n\n    // Only update the native camera if changes were found\n    const hasChanges = !!Object.keys(changes).length;\n\n    const nextWebCameraSettings = { ...capabilities.current, ...changes };\n    if (hasChanges) {\n      Utils.syncTrackCapabilities(preferredType, stream, changes);\n    }\n\n    capabilities.current = nextWebCameraSettings;\n  }, [\n    settings.autoFocus,\n    settings.flashMode,\n    settings.exposureCompensation,\n    settings.colorTemperature,\n    settings.iso,\n    settings.brightness,\n    settings.contrast,\n    settings.saturation,\n    settings.sharpness,\n    settings.focusDistance,\n    settings.whiteBalance,\n    settings.zoom,\n  ]);\n\n  React.useEffect(() => {\n    // set or unset the video source.\n    if (!video.current) {\n      return;\n    }\n    Utils.setVideoSource(video.current, stream);\n  }, [video.current, stream]);\n\n  React.useEffect(() => {\n    return () => {\n      // Clean up on dismount, this is important for making sure the camera light goes off when the component is removed.\n      for (const stream of activeStreams.current) {\n        // Close all open streams.\n        Utils.stopMediaStream(stream);\n      }\n      if (video.current) {\n        // Invalidate the video source.\n        Utils.setVideoSource(video.current, stream);\n      }\n    };\n  }, []);\n\n  // Update props when the video loads.\n  useLoadedVideo(video.current, () => {\n    Utils.syncTrackCapabilities(preferredType, stream, capabilities.current);\n  });\n\n  return {\n    type,\n    mediaTrackSettings,\n  };\n}\n"]}