This repository has been archived on 2023-11-14. You can view files and clone it, but cannot push or open issues/pull-requests.
viscord/packages/renderer/src/contexts/EphemeralState/UserMediaState.tsx

121 lines
3.1 KiB
TypeScript

import React from "react";
import { createContext, ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import { Video } from "/@/components/Video";
export const UserMediaContext = createContext<{
enabled: boolean;
mediaStream: MediaStream | null;
enable: () => void;
disable: () => void;
mute: () => void;
unmute: () => void;
muted: boolean;
enableCamera: () => void;
disableCamera: () => void;
cameraEnabled: boolean;
videoElement: HTMLVideoElement | null;
}>({
enabled: false,
mediaStream: null,
enable: () => {},
disable: () => {},
mute: () => {},
unmute: () => {},
muted: false,
enableCamera: () => {},
disableCamera: () => {},
cameraEnabled: false,
videoElement: null,
});
export default function UserMediaState(props: any) {
const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
const [enabled, setEnabled] = useState(false);
const [muted, setMuted] = useState(false);
const [cameraEnabled, setCameraEnabled] = useState(false);
const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(null);
const createBlankVideoTrack = () => {
const canvas = document.createElement('canvas');
canvas.width = 40;
canvas.height = 30;
return canvas.captureStream(60).getVideoTracks()[0];
}
const updateMediaStream = (mediaStream: MediaStream | null) => {
setMediaStream(old => {
if(old !== null) {
for(const track of old.getTracks()) {
track.stop();
}
}
return mediaStream;
});
if(mediaStream !== null) {
const videoElement = document.createElement('video');
videoElement.muted = true;
videoElement.autoplay = true;
videoElement.srcObject = mediaStream;
videoElement.style.height = '100%';
setVideoElement(videoElement);
} else {
setVideoElement(null);
}
}
// maintaining the mediaStream...
useEffect(() => {
(async () => {
if(enabled) {
const newStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: cameraEnabled,
});
if(!cameraEnabled) {
newStream.addTrack(createBlankVideoTrack());
}
if(muted) {
newStream.getAudioTracks()[0].enabled = false;
}
updateMediaStream(newStream);
} else {
updateMediaStream(null);
}
})()
}, [enabled, cameraEnabled]);
const mute = () => {
if(mediaStream === null) return;
mediaStream.getAudioTracks()[0].enabled = false;
setMuted(true);
}
const unmute = () => {
if(mediaStream === null) return;
mediaStream.getAudioTracks()[0].enabled = true;
setMuted(false);
}
const value = useMemo(() => ({
enabled,
mediaStream,
enable: () => setEnabled(true),
disable: () => setEnabled(false),
mute,
unmute,
muted,
enableCamera: () => setCameraEnabled(true),
disableCamera: () => setCameraEnabled(false),
cameraEnabled,
videoElement
}), [enabled, mediaStream, muted]);
return <UserMediaContext.Provider value={value}>
{props.children}
</UserMediaContext.Provider>
}