import React, { useEffect, useRef, useState, useCallback } from ‘react’;
import io from ‘socket.io-client’;
import * as mediasoupClient from “mediasoup-client”;
import ReactPlayer from ‘react-player’;
import {increment, decrement, reset} from ‘…/src/redux-elements/Features/demoSlice’;
import { useDispatch, useSelector } from ‘react-redux’;
import { setLocalStream, addRemoteStream, removeRemoteStream, setError } from ‘…/src/redux-elements/Features/streamSlice’;
import VideoPlayer from ‘./VideoPlayer’;
import {selectRoomName, setRoomName} from ‘./redux-elements/Features/roomReducer’
// install react-video-stream k
const Controls = ({ startVideo, stopVideo, startScreenShare, stopScreenShare }) => (
//dvff
const App = () => {
const dispatch = useDispatch();
//State Variables
const count = useSelector(state => state.demo.count);
const roomName = useSelector(selectRoomName)
const socket = useRef(io(“/mediasoup”));
const videoContainerRef = useRef(null);
const [socketInstance, setSocketInstance] = useState(null);
const [connected, setConnected] = useState(false);
const [localStream, setLocalStream] = useState(null);
const [remoteStreams, setRemoteStreams] = useState();
const [error, setError] = useState(null);
const [mediaSoupdevice, setmediaSoupdevice] = useState({});
const [producerTransport, setProducerTransport] = useState(null);
const [consumerTransports, setConsumerTransports] = useState();
const [audioProducer, setAudioProducer] = useState(null);
const [videoProducer, setVideoProducer] = useState(null);
const [consumer, setConsumer] = useState(null);
const [consumingTransports, setConsumingTransports] = useState();
const [ routerRtpCapabilities, setRouterRtpCapabilities] = useState(null);
const [ newConsumerTransport, setNewConsumerTransport] = useState(null)
const [params, setParams] = useState(null);
useEffect(() => {
const socketInstance = io('https://localhost:9000/mediasoup');
socketInstance.on('connect', () => {
console.log('Connected to server!');
socket.current = socketInstance;
setSocketInstance(socket.current);
setConnected(true);
getLocalStream();
const room = window.location.pathname.split('/')[2];
dispatch(setRoomName(room));
console.log("room name from the path paramter is", roomName);
});
socketInstance.on('connection-success', ({ socketId }) => {
console.log('Connection successful! Socket ID:', socketId);
});
return () => {
socketInstance.disconnect();
};
}, );
//UseEffect to call the join room function
useEffect(() => {
if (socketInstance && roomName) {
joinRoom();
}
}, [socketInstance, roomName]);
const joinRoom = () => {
socket.current.emit('joinRoom', { roomName: roomName }, (data) => {
if (data && data.rtpCapabilities) {
console.log("RTP Capabilities received from server:", data.rtpCapabilities);
setRouterRtpCapabilities(data.rtpCapabilities);
} else {
console.log("Unable to retrieve RTP capabilities from server");
}
});
};
useEffect(() => {
if (routerRtpCapabilities) {
createDevice();
}
}, [routerRtpCapabilities]);
const createDevice = useCallback(async () => {
try {
const device = new mediasoupClient.Device();
setmediaSoupdevice(device);
await device.load({ routerRtpCapabilities });
} catch (error) {
console.error(error);
if (error.name === ‘UnsupportedError’) {
console.warn(‘Browser not supported’);
}
}
}, [routerRtpCapabilities]);
//UseEffect to call CreateSendTransport Function
useEffect(()=> {
createSendTransport();
}, [mediaSoupdevice])
const createSendTransport = () => {
socket.current.emit(‘createWebRtcTransport’, { consumer: false }, async ({ params}) => {
if (params.error) {
console.error(“params error”, params.error);
return;
}
console.log("Params received from Server to create sendTransport", params);
console.log("mediasoup device::", mediaSoupdevice);
const newProducerTransport = await mediaSoupdevice.createSendTransport(params);
console.log("newProducerTransport", newProducerTransport); // Log the newProducerTransport
if (newProducerTransport) {
setProducerTransport(newProducerTransport);
newProducerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try {
await socket.current.emit('transport-connect', { dtlsParameters });
callback();
} catch (error) {
errback(error);
}
});
newProducerTransport.on('produce', async (parameters, callback, errback) => {
console.log("New Producer Transport :::>>", parameters);
try {
await socket.current.emit('transport-produce', {
kind: parameters.kind,
rtpParameters: parameters.rtpParameters,
appData: parameters.appData,
}, ({ id, producersExist }) => {
callback({ id });
if (producersExist) getProducers();
});
} catch (error) {
errback(error);
}
});
} else {
console.log("Unable to create Producer Transport");
}
});
};
//function to get the localStream ::>>
// let VidParams = {
// // mediasoup params
// encodings: [
// {
// rid: ‘r0’,
// maxBitrate: 100000,
// scalabilityMode: ‘S1T3’,
// },
// {
// rid: ‘r1’,
// maxBitrate: 300000,
// scalabilityMode: ‘S1T3’,
// },
// {
// rid: ‘r2’,
// maxBitrate: 900000,
// scalabilityMode: ‘S1T3’,
// },
// ],
// // mediasoup :: API
// codecOptions: {
// videoGoogleStartBitrate: 1000
// }
// }
// let audioParams;
// let videoParams = { VidParams };
const getLocalStream = () => {
navigator.mediaDevices.getUserMedia({
audio: true,
video: {
width: { min: 640, max: 1920 },
height: { min: 400, max: 1080 }
}
}).then((stream) => {
setLocalStream(stream);
// const AudioTracks = stream.getAudioTracks()[0];
// const videoTracks = stream.getVideoTracks()[0];
// audioParams = { track: stream.getAudioTracks()[0], …audioParams };
// videoParams = { track: stream.getVideoTracks()[0], …videoParams };
}).catch(error => {
console.error(‘Error accessing media devices:’, error);
setError(error.message);
});
};
//Connect Send Tranport function::>
useEffect(() => {
const connectSendTransport = async() => {
if (!producerTransport || !localStream) return; // Add null check for localStream
console.log("producerTransport", producerTransport);
const videoTracks = localStream.getVideoTracks(); // Access video tracks
console.log("vd tracks", videoTracks);
if (videoTracks.length > 0) { // Check if video tracks exist
const videoTrack = videoTracks[0]; // Get the first video track
const stats = await producerTransport.getStats();
console.log("Stats for producerTransport:", stats);
const newProducer = await producerTransport.produce({
track: videoTrack,
encodings: [
{
rid: 'r0',
maxBitrate: 100000,
scalabilityMode: 'S1T3',
},
{
rid: 'r1',
maxBitrate: 300000,
scalabilityMode: 'S1T3',
},
{
rid: 'r2',
maxBitrate: 900000,
scalabilityMode: 'S1T3',
},
],
codecOptions: {
videoGoogleStartBitrate: 1000
}
});
console.log("Log for new Producer >>>>>>>>>>>>>.", newProducer);
setVideoProducer(newProducer);
newProducer.on('trackended', () => console.log('Video track ended'));
newProducer.on('transportclose', () => console.log('Video transport ended'));
} else {
console.error('No video tracks found in localStream');
}
};
connectSendTransport();
}, [producerTransport, localStream]);
useEffect(()=> {
signalNewConsumerTransport
}, [mediaSoupdevice])
//Function for SignalNewConsumer Transport::>
const signalNewConsumerTransport = async (remoteProducerId) => {
if (consumingTransports.includes(remoteProducerId)) return;
consumingTransports.push(remoteProducerId);
await socket.current.emit(‘createWebRtcTransport’, { consumer: true }, async ({ params }) => {
if (!params || params.error) {
console.error(params ? params.error : “Params are null or undefined”);
return;
}
console.log(`Signal PARAMS received from Server... ${params}`);
setParams(params);
let ConsumerTransport;
try {
ConsumerTransport = await mediaSoupdevice.createRecvTransport(params);
setNewConsumerTransport(ConsumerTransport);
ConsumerTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
try {
await socket.current.emit('transport-recv-connect', {
dtlsParameters,
serverConsumerTransportId: params.id,
});
callback();
} catch (error) {
errback(error);
}
});
connectRecvTransport(ConsumerTransport, remoteProducerId, params.id);
} catch (error) {
console.log("error in Consumer Transport", error)
}
});
};
// server informs the client of a new producer just joined
socket.current.on(‘new-producer’, ({ producerId }) => signalNewConsumerTransport(producerId))
const getProducers = () => {
socket.current.emit(‘getProducers’, producerIds => {
console.log(“Get Producers Information ::>>>>>”, producerIds)
// for each of the producer create a consumer
// producerIds.forEach(id => signalNewConsumerTransport(id))
producerIds.forEach(signalNewConsumerTransport)
})
}
const connectRecvTransport = async (consumerTransport, remoteProducerId, serverConsumerTransportId) => {
// for consumer, we need to tell the server first
// to create a consumer based on the rtpCapabilities and consume
// if the router can consume, it will send back a set of params as below
await socket.current.emit(‘consume’, {
rtpCapabilities: mediaSoupdevice.rtpCapabilities,
remoteProducerId,
serverConsumerTransportId,
}, async ({ params }) => {
if (params.error) {
console.error(‘Cannot Consume’);
return;
}
console.log(Consumer Params ${params}
);
// then consume with the local consumer transport
// which creates a consumer
const consumer = await consumerTransport.consume({
id: params.id,
producerId: params.producerId,
kind: params.kind,
rtpParameters: params.rtpParameters
});
setConsumerTransports([...consumerTransports, {
consumerTransport,
serverConsumerTransportId: params.id,
producerId: remoteProducerId,
consumer
}]);
// destructure and retrieve the video track from the producer
const { track } = consumer
const rstream = new MediaStream([track]);
console.log("Remote Stream after adding track:", rstream);
setRemoteStreams(prevStreams => [...prevStreams, rstream]); // Update using functional form of setState
});
};
//Video Controller Functionss
const startVideo = () => {
if (localStream) {
localStream.getVideoTracks().forEach(track => {
track.enabled = true;
});
}
};
const stopVideo = () => {
if (localStream) {
localStream.getVideoTracks().forEach(track => {
track.enabled = false;
});
}
};
const startScreenShare = () => {
navigator.mediaDevices.getDisplayMedia({ video: true }).then((stream) => {
const screenTrack = stream.getVideoTracks()[0];
setVideoParams({ …videoParams, track: screenTrack });
if (videoContainerRef.current) {
videoContainerRef.current.srcObject = stream;
}
screenTrack.onended = () => {
setVideoParams({ …videoParams, track: localStream.getVideoTracks()[0] });
if (videoContainerRef.current) {
videoContainerRef.current.srcObject = localStream;
}
};
}).catch(error => {
console.error(‘Error accessing screen share:’, error);
});
};
const stopScreenShare = () => {
if (!localStream || !localStream.getVideoTracks().length) {
console.error(‘Error: localStream does not exist or does not have any video tracks’);
return;
}
try {
setVideoParams({ ...videoParams, track: localStream.getVideoTracks()[0] });
if (videoContainerRef.current) {
videoContainerRef.current.srcObject = localStream;
}
} catch (error) {
console.error('Error in stopScreenShare:', error);
}
};
useEffect(()=> {
console.log(“remote stream received…>”);
}, [remoteStreams])
//Function to render remote Streams::>
const renderRemoteStreams = () => {
return remoteStreams.map((remoteStream, index) => (
));
};
return (
{error && <div>Error: {error}</div>}
{/* Controls for starting/stopping video and sdreen sharing */}
<Controls
startVideo={startVideo}
stopVideo={stopVideo}
startScreenShare={startScreenShare}
stopScreenShare={stopScreenShare}
/>
{/* Container for local fvidfffffeoffffff stddream*/}
<div>
<ReactPlayer url={localStream} width="300px" height="200px" playing={true} />
</div>
<div>
{remoteStreams.length > 0 ? (
renderRemoteStreams()
) : (
<h5>No Remote Streams Available</h5>
)}
</div>
</div>
);
};
export default App;