UNPKG

@fishjam-cloud/react-client

Version:
122 lines (121 loc) 5.35 kB
import { useCallback, useEffect, useRef, useState } from "react"; import { prepareConstraints } from "../../../devices/constraints"; import { correctDevicesOnSafari, getAvailableMedia } from "../../../devices/mediaInitializer"; import { useDeviceManager } from "./useDeviceManager"; export const useMediaDevices = ({ videoConstraints, audioConstraints, persistHandlers, logger }) => { const [deviceList, setDeviceList] = useState([]); const [videoStream, setVideoStream] = useState(null); const [audioStream, setAudioStream] = useState(null); const [videoError, setVideoError] = useState(null); const [audioError, setAudioError] = useState(null); const [selectedCamera, setSelectedCamera] = useState(persistHandlers?.getLastDevice("video") ?? null); const [selectedMic, setSelectedMic] = useState(persistHandlers?.getLastDevice("audio") ?? null); const isInitializedRef = useRef(false); const initializationRef = useRef(null); const selectCamera = useCallback((deviceInfo) => { setSelectedCamera(deviceInfo); persistHandlers?.saveLastDevice(deviceInfo, "video"); }, [persistHandlers]); const selectMic = useCallback((deviceInfo) => { setSelectedMic(deviceInfo); persistHandlers?.saveLastDevice(deviceInfo, "audio"); }, [persistHandlers]); const initializeDevices = useCallback(async (settings) => { if (isInitializedRef.current) { return { stream: null, errors: null, status: "already_initialized" }; } if (initializationRef.current) { return initializationRef.current; } const lastUsed = { audio: persistHandlers?.getLastDevice("audio") ?? null, video: persistHandlers?.getLastDevice("video") ?? null, }; const constraints = { video: settings?.enableVideo !== false && prepareConstraints(lastUsed.video?.deviceId, videoConstraints), audio: settings?.enableAudio !== false && prepareConstraints(lastUsed.audio?.deviceId, audioConstraints), }; const intitialize = async () => { let media = await getAvailableMedia(constraints); const fetchedDevices = await navigator.mediaDevices.enumerateDevices(); setDeviceList(fetchedDevices); if (media.stream) { media = await correctDevicesOnSafari(media.stream, media.errors, fetchedDevices, constraints, lastUsed); } const { stream, errors } = media; const videoDeviceId = stream?.getVideoTracks()[0]?.getSettings().deviceId; const audioDeviceId = stream?.getAudioTracks()[0]?.getSettings().deviceId; const videoDevice = fetchedDevices.find((device) => device.deviceId === videoDeviceId); const audioDevice = fetchedDevices.find((device) => device.deviceId === audioDeviceId); if (videoDevice) { selectCamera(videoDevice); } if (audioDevice) { selectMic(audioDevice); } setVideoStream(stream); setAudioStream(stream); setVideoError(errors.video); setAudioError(errors.audio); if (!stream) { return { status: "failed", errors, stream: null }; } else if (errors.video || errors.audio) { return { status: "initialized_with_errors", errors, stream: null }; } else { return { status: "initialized", errors: null, stream }; } }; const initializePromise = intitialize().then((result) => { isInitializedRef.current = true; return result; }, (error) => { initializationRef.current = null; throw error; }); initializationRef.current = initializePromise; return await initializePromise; }, [videoConstraints, audioConstraints, selectCamera, selectMic, persistHandlers]); useEffect(() => { const isInitialStreamIrrelevant = videoStream !== audioStream; if (isInitialStreamIrrelevant) { initializationRef.current = null; } }, [videoStream, audioStream]); const getInitialStream = useCallback(async () => { const result = await initializationRef.current; return result?.stream ?? null; }, []); const cameraManager = useDeviceManager({ mediaStream: videoStream, setMediaStream: setVideoStream, deviceError: videoError, setDeviceError: setVideoError, getInitialStream, deviceType: "video", allDevicesList: deviceList, constraints: videoConstraints, setSelectedDevice: selectCamera, selectedDevice: selectedCamera, logger, }); const microphoneManager = useDeviceManager({ mediaStream: audioStream, setMediaStream: setAudioStream, deviceError: audioError, setDeviceError: setAudioError, getInitialStream, deviceType: "audio", allDevicesList: deviceList, constraints: audioConstraints, setSelectedDevice: selectMic, selectedDevice: selectedMic, logger, }); return { initializeDevices, cameraManager, microphoneManager, }; };