**************FLOW , CONNECTIONS AND DISPLAYING ID'S******************
IN THE FRONTEND , CONNECT BUTTON IS CLICKED,
<Button variant="contained" onClick={connect}>
Connect
</Button>
let connect = () => {
setAskForUsername(false); // AFTER BUTTON IS CLICKED , THE USERNAME IS SET TO FALSE
getMedia();
getUserMedia();
};
let getMedia = () => {
setVideo(videoAvailable);
setAudio(audioAvailable);
connectToSocketServer(); // CONNNECTING TO SERVER
};
INITIALLY, IN THE CONNECT TO SOCKET SERVER,
let connectToSocketServer = () => {
socketRef.current = io(server_Url);
socketRef.current.on("signal", gotMessageFromServer);
IN THE BACKEND, WHEN A signal IS SENT,
socket.on("signal", (toId, message) => {
// console.log(socket.id);
// console.log(message);
io.to(toId).emit("signal", socket.id, message);
});
gotMessageFromServer
let gotMessageFromServer = (Id, Message) => {
let signal = JSON.parse(Message);
console.log(signal);
console.log(Id, socketIdRef.current);
if (Id !== socketIdRef.current) {
console.log(signal.sdp);
if (signal.sdp) {
connections[Id].setRemoteDescription(
new RTCSessionDescription(signal.sdp)
)
.then(() => {
if (signal.sdp.type === "offer") {
connections[Id].createAnswer()
.then((description) => {
connections[Id].setLocalDescription(description)
.then(() => {
socketRef.current.emit(
"signal",
Id,
JSON.stringify({
sdp: connections[Id].localDescription,
})
);
})
.catch((e) => console.log(e));
})
.catch((e) => console.log(e));
}
})
.catch((e) => console.log(e));
}
if (signal.ice) {
console.log("iceeeeeeeeee");
connections[Id].addIceCandidate(new RTCIceCandidate(signal.ice)).catch(
(e) => console.log(e)
);
}
}
};
for (let i = 0; i < connections[path].length; i++) {
io.to(connections[path][i]).emit(
"user-joined",
socket.id,
connections[path]
);
}
let connectToSocketServer = () => {
socketRef.current = io(server_Url);
socketRef.current.on("signal", gotMessageFromServer);
socketRef.current.on("connect", () => {
socketRef.current.emit("join-call", window.location.href);
socketIdRef.current = socketRef.current.id;
socketRef.current.on("chat-message", setMessage);
socketRef.current.on("user-left", (id) => {
setVideo((videos) => videos.filter((video) => video.socketId !== id));
});
socketRef.current.on("user-joined", (id, clients) => {
clients.forEach((socketListId) => {
// NEW PEER CONNECTION IS ESTABLISHED
connections[socketListId] = new RTCPeerConnection(
peerConfigConnections
);
//WHEN TWO USERS WANT TO DO A PEER TO PEER CONNECTION, THEY NEED TO KNOW HOW TO
CONTACT EACH OTHER BUT DUE TO FIREWALLS IT'S NOT STRAIGHT FORWARD. SO EACH PEER
GATHERS POSSIBLE WAYS ( CANDIDATES) TO CONNECT, THESE ARE CALLED ICE CANDIDATES.
connections[socketListId].onicecandidate = (event) => {
if (event.candidate !== null) {
socketRef.current.emit(
"signal",
socketListId,
JSON.stringify({ ice: event.candidate })
// THIS LINE WRAPS UP THE EVENT CANDIDATE LIKE THIS
{
"ice": {
"candidate": "candidate:842163049 1 udp ...",
"sdpMid": "0",
"sdpMLineIndex": 0
}
}
);
}
};
console.log(connections[socketListId]);
//ontrack
HERE, WHEN WE RECEIVE A MEDIA STREAM (AUDIO/VIDEO) FROM THE CONNECTION, WE UPDATE
THE REACT STATE. SO, IT CAN BE RENDERED ON SCREEN.
connections[socketListId].ontrack = (event) => {
setVideos((prevVideos) => {
WE CHECK, IF A VIDEO EXISTS FROM THAT SPECIFIC SOCKETLISTID, INORDER TO PREVENT
ADDING THE SAME VIDEOS.
const videoExists = prevVideos.some(
(video) => video.socketId === socketListId
);
IF STREAM EXISTS WE JUST UPDATE THE STREAM
if (videoExists) {
return prevVideos.map((video) =>
video.socketId === socketListId
? { ...video, stream: event.streams[0] }
: video
);
}
IF IT'S A NEW VIDEO, A NEW OBJECT IS CREATED
const newVideo = {
socketId: socketListId,
stream: event.streams[0],
autoPlay: true,
playsinline: true,
};
const updatedVideos = [...prevVideos, newVideo];
videoRef.current = updatedVideos;
return updatedVideos;
});
};
console.log(window.localStream);
// TO SEND LOCAL AUDIO/VIDEO TRACKS TO REMOTE PEERS
if (window.localStream) {
window.localStream.getTracks().forEach((track) => {
connections[socketListId].addTrack(track, window.localStream);
});
}
else {
WHEN THERE'S NO WEBCAM/MIC A BLACK SILENT STREAM IS SENT.
//BLACK SILENCE
let blackSilence = (...args) =>
new MediaStream([black(...args), silence()]);
window.localStream = blackSilence();
window.localStream.getTracks().forEach((track) => {
connections[socketListId].addTrack(track, window.localStream);
});
}
});
if (id === socketIdRef.current) {
for (let id2 in connections) {
if (id2 === socketIdRef.current) {
continue;
}
WHEN TWO PEERS NEED TO BE CONNECTED, AN OFFER IS CREATED. AN OFFER IS A PART
OF SDP (SESSION DESCRIPTION PROTOCOL) NEGOTIATION THAT HELPS TWO PEERS AGREE ON
HOW TO COMMUNICATE.
1.AN OFFER IS CREATED BY PEER A
connections[id2].createOffer().then((description) => {
2.THIS OFFER IS SET AS IT'S LOCAL DESCRIPTION
connections[id2]
.setLocalDescription(description)
.then(() => {
3. THEN THIS IS SENT TO PEER B AS A SIGNAL.
socketRef.current.emit(
"signal",
id2,
JSON.stringify({
sdp: connections[id2].localDescription,
})
);
})
.catch((e) => {
console.log(e);
});
});
}
}
});
});
};
getUserMedia() FUNCTION TO GET THE USER'S MEDIA
let getUserMedia = () => {
console.log(video);
if ((video && videoAvailable) || (audio && audioAvailable)) {
navigator.mediaDevices
.getUserMedia({ video: video, audio: audio })
.then(getUserMediaSuccess)
.then((stream) => {})
.catch((e) => {
console.log(e);
});
} else {
try {
let tracks = localVideoRef.current.srcObject.getTracks();
tracks.forEach((track) => track.stop());
} catch (e) {}
}
};
**************** getUserMediaSuccess ****************
const getUserMediaSuccess = (stream) => {
console.log(window.localStream.getTracks());
try {
IF ANY PREVIOUS STREAM EXISTS WE STOP IT.
window.localStream.getTracks().forEach((track) => {
track.stop();
});
} catch (e) {
console.log(e);
}
// console.log(stream);
window.localStream = stream;
// console.log(window.localStream);
// console.log(localVideoRef.current.srcObject);
localVideoRef.current.srcObject = stream;
// console.log(localVideoRef.current.srcObject);
// console.log(connections);
for (let id in connections) {
// console.log(id);
if (id === socketIdRef.current) {
continue;
}
connections[id].createOffer().then((description) => {
// console.log(description);
connections[id]
.setLocalDescription(description)
.then(() => {
socketRef.current.emit(
"signal",
id,
JSON.stringify({ sdp: connections[id].localDescription })
);
})
.catch((e) => console.log(e));
});
}
WHEN THE USER TURNS OFF MIC/WEBCAM THE STREAM IS UPDATED.
stream.getTracks().forEach(
(track) =>
(track.onended = () => {
setAudio(false);
setVideo(false);
try {
let tracks = localVideoRef.current.srcObject.getTracks();
// console.log(tracks);
tracks.forEach((track) => track.stop());
} catch (e) {
console.log(e);
}
let blackSilence = (...args) =>
new MediaStream([black(...args), silence()]);
window.localStream = blackSilence();
localVideoRef.current.srcObject = window.localStream;
for (let id in connections) {
connections[id].createOffer().then((description) => {
connections[id]
.setLocalDescription(description)
.then(() => {
socketRef.current.emit(
"signal",
id,
JSON.stringify({ sdp: connections[id].localDescription })
);
})
.catch((e) => console.log(e));
});
}
})
);
};
TO DISPLAY ID'S
<>
{askForUsername === true ? (
<div>
<h2>Enter into lobby</h2>
<TextField
className="inputsRequired"
id="outlined-basic"
label="Username"
value={username}
variant="outlined"
onChange={(e) => setUsername(e.target.value)}
/>
<Button variant="contained" onClick={connect}>
Connect
</Button>
<div>
<video ref={localVideoRef} autoPlay muted></video>
</div>
</div>
) : (
<div>
<video ref={localVideoRef} autoPlay muted></video>
{videos.map((video) => (
<div key={video.socketId}>
<h2>{video.socketId}</h2>
<video
data-socket={video.socketId}
ref={(ref) => {
if (ref && video.stream) {
ref.srcObject = video.stream;
}
}}
autoPlay
playsInline
/>
</div>
))}
</div>
)}
</>
);
Comments
Post a Comment