UNPKG

@fishjam-cloud/react-client

Version:
131 lines (130 loc) 5.43 kB
import { TrackTypeError } from "@fishjam-cloud/ts-client"; import { useCallback, useEffect, useRef, useState } from "react"; export const useScreenShareManager = ({ fishjamClient, peerStatus }) => { const [state, setState] = useState({ stream: null, trackIds: null }); const cleanMiddlewareFnRef = useRef(null); const stream = state.stream ?? null; const [mediaVideoTrack, mediaAudioTrack] = stream ? getTracksFromStream(stream) : [null, null]; const getDisplayName = () => { const name = fishjamClient.getLocalPeer()?.metadata?.peer?.displayName; if (typeof name === "string") return name; }; const addTrackToFishjamClient = async (track, trackMetadata) => { try { return fishjamClient.addTrack(track, trackMetadata); } catch (err) { if (err instanceof TrackTypeError) { console.warn(err.message); return undefined; } throw err; } }; const startStreaming = async (props) => { const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: props?.videoConstraints ?? true, audio: props?.audioConstraints ?? true, }); const displayName = getDisplayName(); let [video, audio] = getTracksFromStream(displayStream); if (state.tracksMiddleware) { const { videoTrack, audioTrack, onClear } = await state.tracksMiddleware(video, audio); video = videoTrack; audio = audioTrack; cleanMiddlewareFnRef.current = onClear; } const addTrackPromises = [addTrackToFishjamClient(video, { displayName, type: "screenShareVideo", paused: false })]; if (audio) addTrackPromises.push(addTrackToFishjamClient(audio, { displayName, type: "screenShareAudio", paused: false })); const [videoId, audioId] = await Promise.all(addTrackPromises); setState({ stream: displayStream, trackIds: { videoId, audioId } }); }; const replaceTracks = async (newVideoTrack, newAudioTrack) => { if (!state?.stream) return; const addTrackPromises = []; if (newVideoTrack && state.trackIds.videoId) addTrackPromises.push(fishjamClient.replaceTrack(state.trackIds.videoId, newVideoTrack)); if (newAudioTrack && state.trackIds.audioId) addTrackPromises.push(fishjamClient.replaceTrack(state.trackIds.audioId, newAudioTrack)); await Promise.all(addTrackPromises); }; const cleanMiddleware = useCallback(() => { cleanMiddlewareFnRef.current?.(); cleanMiddlewareFnRef.current = null; }, []); const setTracksMiddleware = async (middleware) => { if (!state?.stream) return; const [video, audio] = getTracksFromStream(state.stream); cleanMiddleware(); const { videoTrack, audioTrack, onClear } = (await middleware?.(video, audio)) ?? { videoTrack: video, audioTrack: audio, onClear: null, }; cleanMiddlewareFnRef.current = onClear; await replaceTracks(videoTrack, audioTrack); }; const stopStreaming = useCallback(async () => { if (!state.stream) { console.warn("No stream to stop"); return; } const [video, audio] = getTracksFromStream(state.stream); video.stop(); if (audio) audio.stop(); if (peerStatus === "connected") { const removeTrackPromises = []; if (state.trackIds.videoId) removeTrackPromises.push(fishjamClient.removeTrack(state.trackIds.videoId)); if (state.trackIds.audioId) removeTrackPromises.push(fishjamClient.removeTrack(state.trackIds.audioId)); await Promise.all(removeTrackPromises); } cleanMiddleware(); setState(({ tracksMiddleware }) => ({ stream: null, trackIds: null, tracksMiddleware })); }, [state, fishjamClient, setState, cleanMiddleware, peerStatus]); useEffect(() => { if (!state.stream) return; const [video, audio] = getTracksFromStream(state.stream); const trackEndedHandler = () => { stopStreaming(); }; video.addEventListener("ended", trackEndedHandler); audio?.addEventListener("ended", trackEndedHandler); return () => { video.removeEventListener("ended", trackEndedHandler); audio?.removeEventListener("ended", trackEndedHandler); }; }, [state, stopStreaming]); useEffect(() => { const onDisconnected = () => { if (stream) { stopStreaming(); } }; fishjamClient.on("disconnected", onDisconnected); return () => { fishjamClient.removeListener("disconnected", onDisconnected); }; }, [stopStreaming, fishjamClient, stream]); return { startStreaming, stopStreaming, stream, videoTrack: mediaVideoTrack, audioTrack: mediaAudioTrack, setTracksMiddleware, currentTracksMiddleware: state?.tracksMiddleware ?? null, }; }; const getTracksFromStream = (stream) => { const video = stream.getVideoTracks()[0]; const audio = stream.getAudioTracks()[0] ?? null; return [video, audio]; };