Files
DiscordClone/Frontend/Electron/src/utils/streamUtils.jsx
Bryan1029384756 2201c56cb2
All checks were successful
Build and Release / build-and-release (push) Successful in 10m6s
feat: introduce a comprehensive LiveKit-based voice and video chat system with state management and screen sharing capabilities.
2026-02-12 18:49:31 -06:00

119 lines
4.3 KiB
JavaScript

import { useState, useEffect, useRef } from 'react';
import { RoomEvent } from 'livekit-client';
export const VideoRenderer = ({ track, style }) => {
const videoRef = useRef(null);
useEffect(() => {
const el = videoRef.current;
if (el && track) {
track.attach(el);
return () => {
track.detach(el);
};
}
}, [track]);
return (
<video
ref={videoRef}
style={{ width: '100%', height: '100%', objectFit: 'contain', backgroundColor: 'black', ...style }}
/>
);
};
export function findTrackPubs(participant) {
let cameraPub = null;
let screenSharePub = null;
let screenShareAudioPub = null;
const trackMap = participant.tracks || participant.trackPublications;
if (trackMap) {
for (const pub of trackMap.values()) {
const source = pub.source ? pub.source.toString().toLowerCase() : '';
const name = pub.trackName ? pub.trackName.toLowerCase() : '';
if (source === 'screen_share_audio' || name === 'screen_share_audio') {
screenShareAudioPub = pub;
} else if (source === 'screenshare' || source === 'screen_share' || name.includes('screen')) {
screenSharePub = pub;
} else if (source === 'camera' || name.includes('camera')) {
cameraPub = pub;
} else if (pub.kind === 'video' && !screenSharePub) {
screenSharePub = pub;
}
}
}
// Fallback for older SDKs
if ((!screenSharePub && !cameraPub) && participant.getTracks) {
try {
for (const pub of participant.getTracks()) {
const source = pub.source ? pub.source.toString().toLowerCase() : '';
const name = pub.trackName ? pub.trackName.toLowerCase() : '';
if (source === 'screen_share_audio' || name === 'screen_share_audio') {
screenShareAudioPub = pub;
} else if (source === 'screenshare' || source === 'screen_share' || name.includes('screen')) {
screenSharePub = pub;
} else if (source === 'camera' || name.includes('camera')) {
cameraPub = pub;
} else if (pub.kind === 'video' && !screenSharePub) {
screenSharePub = pub;
}
}
} catch (e) { /* ignore */ }
}
return { cameraPub, screenSharePub, screenShareAudioPub };
}
export function useParticipantTrack(participant, source) {
const [track, setTrack] = useState(null);
useEffect(() => {
if (!participant) { setTrack(null); return; }
const resolve = () => {
const { cameraPub, screenSharePub } = findTrackPubs(participant);
const pub = source === 'camera' ? cameraPub : screenSharePub;
if (pub && !pub.isSubscribed && source === 'camera') {
pub.setSubscribed(true);
}
setTrack(pub?.track || null);
};
resolve();
const interval = setInterval(resolve, 1000);
const timeout = setTimeout(() => clearInterval(interval), 10000);
const onPub = () => resolve();
const onUnpub = () => resolve();
participant.on(RoomEvent.TrackPublished, onPub);
participant.on(RoomEvent.TrackUnpublished, onUnpub);
participant.on(RoomEvent.TrackSubscribed, onPub);
participant.on(RoomEvent.TrackUnsubscribed, onUnpub);
participant.on(RoomEvent.TrackMuted, onPub);
participant.on(RoomEvent.TrackUnmuted, onPub);
participant.on('localTrackPublished', onPub);
participant.on('localTrackUnpublished', onUnpub);
return () => {
clearInterval(interval);
clearTimeout(timeout);
participant.off(RoomEvent.TrackPublished, onPub);
participant.off(RoomEvent.TrackUnpublished, onUnpub);
participant.off(RoomEvent.TrackSubscribed, onPub);
participant.off(RoomEvent.TrackUnsubscribed, onUnpub);
participant.off(RoomEvent.TrackMuted, onPub);
participant.off(RoomEvent.TrackUnmuted, onPub);
participant.off('localTrackPublished', onPub);
participant.off('localTrackUnpublished', onUnpub);
};
}, [participant, source]);
return track;
}