@fishjam-cloud/react-client
Version:
React client library for Fishjam
122 lines (121 loc) • 5.35 kB
JavaScript
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,
};
};