Untitled
plain_text
a month ago
24 kB
2
Indexable
Never
import { useContext, useState, useEffect, useRef } from 'react'; import { setUserApp } from '../../action/action'; import { connect } from 'react-redux'; import { SocketContext } from './SocketContext'; import { Col, Row } from 'antd'; import Player from '../tchat/elements/player/Player' import Banner from '../tchat/elements/banner/Banner' import Center from '../tchat/elements/center/Center' import Middle from '../tchat/elements/middle/Middle' import sendRequest from '../socket/requestModule' import audioVideoTools from '../../audioTools/audioTools' import { loader } from '../../mediasoup/mediasoup' import JspanelCam from "../tchat/elements/jsPanel/JsPanel" import { listAllEventListeners } from '../utils/ListAllEventListeners' import { join_room } from './chatUtils/utils'; import useRefs from './ref/useRefs' const InterfaceChat = ({ user, ...props }) => { //audio & video setting let device = useRef(null) let audioContext = null; let audioFftArray = null; let id = null; let audioAnalysers = {}; let audioConsumers = useRef({}) let audioConstraints = null; let videoConstraints = null; let producerTransport = useRef({}) let consumerTransport = useRef({}) let videoTrack = useRef(null) let videoProducer = useRef(null) let socket = useContext(SocketContext); let videoEnabled = useRef(null) let videoConsumers = useRef({}) const username = useRef(null) let audioEnabled = useRef(false) let audioTrack = useRef(null) let audioProducer = useRef(null) const [renderJspanelCam, setRenderJspanelCam] = useState([]); let micro = useRef(false) let webcam = useRef(false) const localwebcamRef = useRef(); const localMicroRef = useRef(); const toggleWebcam = async () => { if (!webcam.current) { await enableLocalVideo(); localwebcamRef.current.style.color = "green"; webcam.current = true } else { disableLocalVideo(); localwebcamRef.current.style.color = "black"; webcam.current = false } }; const toggleAudio = async () => { if (!micro.current) { localMicroRef.current.style.color = "green"; micro.current = true await enableLocalAudio(); } else { stopAudioProducer(); localMicroRef.current.style.color = "black"; micro.current = false } }; const starting = async () => { let isConnected = true audioVideoTools.initializeAudioTools(audioContext, audioFftArray, id, audioAnalysers); audioVideoTools.initializeDeviceOptions(audioConstraints, videoConstraints); const routerRtpCapabilities = await sendRequest(socket, 'getRouterRtpCapabilities', {}); device.current = await loader(routerRtpCapabilities) if (!producerTransport.current.on) { let params = await sendRequest(socket, 'createProducerTransport', {}); producerTransport.current = await device.current.createSendTransport(params); console.log('producerTransport ready') } if (!consumerTransport.current.on) { let params = await sendRequest(socket, 'createConsumerTransport', {}); consumerTransport.current = await device.current.createRecvTransport(params); console.log('consumerTransport ready') } //init with mediasoup listener setupTransportListeners(); let currentInfo; try { currentInfo = await sendRequest(socket, 'getCurrentRemoteInfo', { localId: socket.id }); //todo : envoyer le array de user dans userliste currentInfo.remoteUsers.forEach(user => { // addUser(user.id, user); }); //ouvre les cams automatiquement quand on entre dans le salon currentInfo.remoteVideoIds.forEach(rId => { // const addConsumer = async (transport, remoteSocketId, prdId, trackKind, username) console.log(rId) //addConsumer(consumerTransport.current, props.consumeIdWebcam[0], null, 'video', name); addConsumer(consumerTransport, rId, null, 'video', rId); }); currentInfo.remoteAudioIds.forEach(rId => { // addConsumer(consumerTransport, rId, null, 'audio'); }); } catch (error) { console.error(`[connect] [getCurrentRemoteInfo] mensagem de erro: ${error}`); } isConnected = true // updateUI(); console.log('Socket IO Connection estabilished.'); //await enableLocalAudio() } useEffect(() => { props?.dispatch(setUserApp({ addConsumer: addConsumer, addConsumerPrivate: addConsumerPrivate })) if (socket) { const init = async () => { socket.emit('hello', 'hello world') socket.on('connect', (e) => join_room({ ...e, ...props, socket, user })); socket.on('removeJspanel', removeJSpanel); // socket.on('disconnect' , reset ) await starting() } init().then(() => { }) return () => { // socket.off('join_room', join_room); } }; }, [socket]); const setupTransportListeners = () => { try { if (producerTransport) { producerTransport.current.on('connect', async ({ dtlsParameters }, callback, errback) => { sendRequest(socket, 'connectProducerTransport', { dtlsParameters: dtlsParameters }) .then(callback) .catch(errback); }); producerTransport.current.on('produce', async ({ kind, rtpParameters }, callback, errback) => { try { const { id } = await sendRequest(socket, 'produce', { transportId: producerTransport.current.id, kind, rtpParameters }); callback({ id }); } catch (err) { errback(err); } }); producerTransport.current.on('connectionstatechange', (state) => { switch (state) { case 'connecting': console.log('producerTransport connecting...'); break; case 'connected': console.log('producerTransport connected.'); break; case 'failed': console.log('producerTransport connection failed.'); producerTransport.current.close(); break; default: break; } }); } else { console.error('[setupTransportListeners] ProducerTransport n\'a pas été initialisé (l\'application plantera probablement'); } if (consumerTransport) { consumerTransport.current.on('connect', async ({ dtlsParameters }, callback, errback) => { sendRequest(socket, 'connectConsumerTransport', { dtlsParameters: dtlsParameters }) .then(callback) .catch(errback); }); consumerTransport.current.on('connectionstatechange', (state) => { switch (state) { case 'connecting': console.log('consumerTransport connecting...'); break; case 'connected': console.log('consumerTransport connected.'); break; case 'failed': console.log('consumerTransport connection failed.'); consumerTransport.current.close(); break; default: break; } }); } else { console.error('[setupTransportListeners] consumerTransport n\'a pas été initialisé (l\'application va probablement planter'); } } catch (err) { console.log(err) } } function addUser(id, user) { if (!user.isGhost) { //let userFeedCheck = $(`.user-feed[data-id="${id}"]`); } } const enableLocalVideo = async () => { if (!videoEnabled.current) { //props.localWebcamUserRef.current.style.backgroundColor = "green"; await enableLocalVideoTrack() setRenderJspanelCam(component => ([...component, <JspanelCam toggleWebcam={toggleWebcam} toggleAudio={toggleAudio} key={'local'} removeJspanel={disableLocalVideoTrack} webcam={webcam} micro={micro} cam={{ id: 'local', track: videoTrack.current, username: username.current }} />])) await startVideoProducer(); videoEnabled.current = true; } } async function disableLocalVideo() { if (videoEnabled.current) { await stopVideoProducer(); disableLocalVideoTrack(); // isScreensharing = false; videoEnabled.current = false; } } async function stopVideoProducer() { if (videoProducer.current) { videoProducer.current.close(); videoProducer.current = null; await sendRequest(socket, 'stopVideoProducer', {}); } } function disableLocalVideoTrack() { if (videoTrack.current) { // videoTrack.current.stop(); videoTrack.current = null; const jsPanel = document.getElementById("jsPanel_local") if (jsPanel) { // jsPanel.close() removeJSpanel("local", "video") } } else { console.warn('[disableLocalVideoTrack] videoTrack n\'existe pas (ne devrait poser aucun problème)'); } } const enableLocalVideoTrack = async () => { if (!videoTrack.current) { let stream = null; let localVideo = document.getElementById('local_video') try { stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true }); if (stream) { let tracks = stream.getVideoTracks(); if (tracks && tracks.length) { videoTrack.current = tracks[0]; for (let i = 1; i < tracks.length; i++) { tracks[i].stop(); tracks[i] = null; } tracks = null; stream = null; } } } catch (error) { videoTrack.current = null; console.error(`[enableLocalVideoTrack] la caméra n'a pas pu être activée. Message d'erreur ${error}`); } } } function disableLocalVideoTrack() { if (videoTrack.current) { videoTrack.current = null; const jsPanel = document.getElementById("jsPanel_local") if (jsPanel) { removeJSpanel("local", "video") } } else { console.warn('[disableLocalVideoTrack] videoTrack n\'existe pas (ne devrait poser aucun problème)'); } } const startVideoProducer = async (value) => { if (!videoProducer.current && videoTrack.current) { try { videoProducer.current = await producerTransport.current.produce( { track: videoTrack.current, encodings: [ { maxBitrate: 100000 } ], oneToOne: value } ); console.log('Started videoProducer'); } catch (error) { console.error(`[startVideoProducer] La vidéo de la caméra n'a pas pu être envoyée au serveur. Message d'erreur: ${error}`); } } } async function enableLocalAudio() { if (!audioEnabled.current) { // props.localMicroUserRef.current.style.backgroundColor = "green"; await enableLocalAudioTrack(); await startAudioProducer(); } } const enableLocalAudioTrack = async () => { if (!audioTrack.current) { let stream = null; try { stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); if (stream) { let tracks = stream.getAudioTracks(); if (tracks && tracks.length) { audioTrack.current = tracks[0]; for (let i = 1; i < tracks.length; i++) { tracks[i].stop(); tracks[i] = null; } tracks = null; stream = null; } } } catch (error) { audioTrack.current = null; console.error(`[enableLocalAudioTrack] Le microphone n'a pas pu être activé. Message d'erreur: ${error}`); } } } async function startAudioProducer() { if (!audioProducer.current && audioTrack.current) { try { audioProducer.current = await producerTransport.current.produce( { track: audioTrack.current, encodings: [ { maxBitrate: 7500 } ] } ); console.log('Started audioProducer'); } catch (error) { console.error(`[startAudioProducer] L'audio du microphone n'a pas pu être envoyé au serveur. Message d'erreur: ${error}`); } } } async function stopAudioProducer() { if (audioProducer.current) { // props.localMicroUserRef.current.style.backgroundColor = "transparent"; audioProducer.current.close() audioProducer.current = false; audioTrack.current = null await sendRequest(socket, 'stopAudioProducer', {}); } } const addConsumer = async (transport, remoteSocketId, prdId, trackKind, username, socketContext) => { if (!socket) { socket = socketContext; } const { rtpCapabilities } = device.current; const data = await sendRequest(socket, 'consumeAdd', { rtpCapabilities: rtpCapabilities, remoteId: remoteSocketId, kind: trackKind }) .catch(error => { console.error(`[addConsumer] consumeAdd retornou um erro: ${error}`); }); let { producerId, id, kind, rtpParameters, isTeacher } = data; if (prdId && (prdId !== producerId)) { console.warn('[addConsumer] producteurId ne correspond pas, problème de synchronisation de serveur possible (problématique, ne devrait pas se produire'); } let codecOptions = {}; const consumer = await consumerTransport.current.consume({ id, producerId, kind, rtpParameters, codecOptions }); if (kind === 'video') { props?.dispatch(setUserApp({ userConsumer: { id: remoteSocketId, consumer: consumer } })); videoConsumers.current[remoteSocketId] = consumer; } else if (kind === 'audio') { audioConsumers.current[remoteSocketId] = consumer; } else { console.error(`[addConsumer] . (erreur critique, résultat imprévisible)`); } consumer.remoteId = remoteSocketId; consumer.on("transportclose", () => { alert("transportclose"); }); consumer.on("producerclosed", () => { alert("producerclose"); }); consumer.on("close", () => { alert("producerclose"); }); consumer.on('trackended', () => { alert('trackended'); //removeConsumer(consumer.remoteId, kind); }); if (kind === 'video') { sendRequest(socket, 'resumeAdd', { remoteId: remoteSocketId, kind: kind }) .then(() => { // console.log('resumeAdd OK'); }) .catch(err => { console.error(`[addConsumer] erreur dans l'action resumeAdd (l'utilisateur n'aura plus de vidéo`); }); } let elementjs = document.getElementById(`${remoteSocketId}`) if (elementjs) { // Récupérer la piste vidéo existante const videoElement = document.getElementById(`${remoteSocketId}`); const videoStream = videoElement.captureStream(); const videoTrack = videoStream.getVideoTracks()[0]; // Créer une nouvelle piste audio const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true }); const audioTrack = audioStream.getAudioTracks()[0]; // Ajouter la nouvelle piste audio à la piste vidéo existante videoStream.addTrack(consumer.track); // Mettre à jour la source vidéo avec la nouvelle piste audio videoElement.srcObject = videoStream; videoElement.muted = false videoElement.play(); /* audioTools.initializeAudioTools() audioTools.createAudioAnalyser(remoteSocketId, videoElement.srcObject) setInterval(() => audioTools.watchAudioTracks(), 100); */ } // Ajout une webcam unique par instance, et le fait d'avoir une seule et unique instance permet de ne pas avoir de re-render de la webcam if (kind === 'video' && !elementjs) { setRenderJspanelCam(component => ([...component, <JspanelCam key={remoteSocketId} removeJSpanel={removeJSpanel} cam={{ id: remoteSocketId, track: consumer.track, username: username }} />])) /* let webcam = props.forwardedRef['IconeWebcamRef'][remoteSocketId] if (webcam) { webcam.style.display = "initial" } */ } if (kind === 'audio' && !elementjs) { setRenderJspanelCam(component => ([...component, <JspanelCam key={remoteSocketId} removeJSpanel={removeJSpanel} cam={{ id: remoteSocketId, track: consumer.track, username: username }} />])) // let audio = props.forwardedRef['IconeMicroRef'][remoteSocketId] if (micro.current) { micro.style.display = "initial" } } } const addConsumerPrivate = async (transport, remoteSocketId, prdId, trackKind, username, socketContext) => { if (!socket) { socket = socketContext; } const { rtpCapabilities } = device.current; const data = await sendRequest(socket, 'consumeAdd', { rtpCapabilities: rtpCapabilities, remoteId: remoteSocketId, kind: trackKind }) .catch(error => { console.error(`[addConsumer] consumeAdd retornou um erro: ${error}`); }); let { producerId, id, kind, rtpParameters, isTeacher } = data; if (prdId && (prdId !== producerId)) { console.warn('[addConsumer] producteurId ne correspond pas, problème de synchronisation de serveur possible (problématique, ne devrait pas se produire'); } let codecOptions = {}; const consumer = await consumerTransport.current.consume({ id, producerId, kind, rtpParameters, codecOptions }); user?.updateConsumer({ id: id, consumer: consumer }) //update fonction user and past consumer update({id:id , consumer: consumer }) if (kind === 'video') { videoConsumers.current[remoteSocketId] = consumer; } else if (kind === 'audio') { audioConsumers.current[remoteSocketId] = consumer; } else { console.error(`[addConsumer] . (erreur critique, résultat imprévisible)`); } consumer.remoteId = remoteSocketId; consumer.on("transportclose", () => { alert("transportclose"); }); consumer.on("producerclosed", () => { alert("producerclose"); }); consumer.on("close", () => { alert("producerclose"); }); consumer.on('trackended', () => { alert('trackended'); //removeConsumer(consumer.remoteId, kind); }); if (kind === 'video') { sendRequest(socket, 'resumeAdd', { remoteId: remoteSocketId, kind: kind }) .then(() => { // console.log('resumeAdd OK'); }) .catch(err => { console.error(`[addConsumer] erreur dans l'action resumeAdd (l'utilisateur n'aura plus de vidéo`); }); } } const removeJSpanel = (id, kind) => { console.log(id, kind) if (id === 'local') { // stopVideoProducer(); } /* if (kind === 'video') { videoEnabled.current = null } */ if (kind === 'video' && id != 'local') { videoConsumers.current[id].close() socket.emit("stopConsume", id, socket.id, kind) } if (kind === 'audio') { audioEnabled.current = null audioConsumers.current[id].close() socket.emit("stopConsume", id, socket.id, kind) } let element = document.getElementById(`jsPanel_${id}`) if (element) { element.close() setRenderJspanelCam((prevComponent) => { return prevComponent.filter((item) => { return item.key !== id; }); }); console.log(listAllEventListeners()) } } return ( <> {renderJspanelCam} { /* mediasoupTrack ? <TestTrack track={mediasoupTrack} /> : '' */} <Row style={{ maxHeight: '25vh' }} > <Middle toggleWebcam={toggleWebcam} toggleAudio={toggleAudio} localMicroRef={localMicroRef} localwebcamRef={localwebcamRef} /> </Row> <Row style={{ height:'94vh'}} > <Col style={{ textAlign: 'center', backgroundColor: 'white', color: 'black' }} span={24} > <Center ref={useRefs} startVideoProducer={startVideoProducer} /> </Col> </Row> </> ); }; const mapStateToProps = ({ user }) => ({ user }); export default connect(mapStateToProps)(InterfaceChat);