@jellyfish-dev/browser-media-utils
Version:
Facilitates the use of getUserMedia and enumerateDevices.
57 lines (56 loc) • 2.65 kB
JavaScript
import { useEffect, useState } from "react";
import { isAudio, isNotGranted, isVideo, prepareReturn, toMediaTrackConstraints } from "./utils";
/**
* Hook that returns the list of available devices
*
* @param video - boolean or MediaTrackConstraints with configuration for video device
* @param audio - boolean or MediaTrackConstraints with configuration for audio device
* @returns object containing devices or loading state
*/
export const useEnumerateDevices = (video, audio) => {
const [state, setState] = useState(null);
// TODO inline
const enumerateDevices = async (videoParam, audioParam) => {
if (!navigator?.mediaDevices)
throw Error("Navigator is available only in secure contexts");
const objAudio = toMediaTrackConstraints(audioParam);
const objVideo = toMediaTrackConstraints(videoParam);
const booleanAudio = !!audioParam;
const booleanVideo = !!videoParam;
setState(() => ({
audio: booleanAudio ? { type: "Loading" } : { type: "Not requested" },
video: booleanVideo ? { type: "Loading" } : { type: "Not requested" },
}));
let mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
const videoNotGranted = mediaDeviceInfos.filter(isVideo).some(isNotGranted);
const audioNotGranted = mediaDeviceInfos.filter(isAudio).some(isNotGranted);
const constraints = {
video: booleanVideo && videoNotGranted && objVideo,
audio: booleanAudio && audioNotGranted && objAudio,
};
let audioError = null;
let videoError = null;
try {
if (constraints.audio || constraints.video) {
const requestedDevices = await navigator.mediaDevices.getUserMedia(constraints);
mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
requestedDevices.getTracks().forEach((track) => {
track.stop();
});
}
}
catch (error) {
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#exceptions
videoError = booleanVideo && videoNotGranted ? error.name : null;
audioError = booleanAudio && audioNotGranted ? error.name : null;
}
setState({
video: prepareReturn(booleanVideo, mediaDeviceInfos.filter(isVideo), videoError),
audio: prepareReturn(booleanAudio, mediaDeviceInfos.filter(isAudio), audioError),
});
};
useEffect(() => {
enumerateDevices(video, audio);
}, [video, audio]);
return state;
};