I am having a video chat application using react js in the frontend and i have one SFU server which is based on Node js with WRTC library for supporting webRTC apis in the server side(M87).
App.js
import React, { useState, useRef, useEffect, useCallback } from "react";
import io from "socket.io-client";
import Video from "./Components/Video";
import { WebRTCUser } from "./types";
const pc_config = {
iceServers: [
// {
// urls: 'stun:[STUN_IP]:[PORT]',
// 'credentials': '[YOR CREDENTIALS]',
// 'username': '[USERNAME]'
// },
{
urls: "stun:stun.l.google.com:19302",
},
],
};
const SOCKET_SERVER_URL = "https://192.168.132.29:8080";
const App = () => {
const socketRef = useRef<SocketIOClient.Socket>();
......
..........
const createReceiverPeerConnection = useCallback((socketID: string) => {
try {
const pc = new RTCPeerConnection(pc_config);
// add pc to peerConnections object
receivePCsRef.current = { ...receivePCsRef.current, [socketID]: pc };
pc.onicecandidate = (e) => {
if (!(e.candidate && socketRef.current)) return;
console.log("receiver PC onicecandidate");
socketRef.current.emit("receiverCandidate", {
candidate: e.candidate,
receiverSocketID: socketRef.current.id,
senderSocketID: socketID,
});
};
pc.oniceconnectionstatechange = (e) => {
console.log(e);
};
pc.ontrack = (e) => {
console.log("ontrack success");
setUsers((oldUsers) =>
oldUsers
.filter((user) => user.id !== socketID)
.concat({
id: socketID,
stream: e.streams[0],
})
);
};
// return pc
return pc;
} catch (e) {
console.error(e);
return undefined;
}
}, []);
......
............
return (
<div>
<video
style={{
width: 240,
height: 240,
margin: 5,
backgroundColor: "black",
}}
muted
ref={localVideoRef}
autoPlay
/>
{users.map((user, index) => (
<Video key={index} stream={user.stream} />
))}
</div>
);
};
export default App;
server.js
let http = require("http");
let express = require("express");
let cors = require("cors");
let socketio = require("socket.io");
let wrtc = require("wrtc");
const fs = require("fs");
let https = require("https");
const port=8080;
const options = {
key: fs.readFileSync("../cert/cert.priv.key"),
cert: fs.readFileSync("../cert/cert.chain.pem"),
};
const app = express();
const server = https.createServer(options, app);
app.use(cors());
let receiverPCs = {};
let senderPCs = {};
let users = {};
let socketToRoom = {};
const pc_config = {
iceServers: [
// {
// urls: 'stun:[STUN_IP]:[PORT]',
// 'credentials': '[YOR CREDENTIALS]',
// 'username': '[USERNAME]'
// },
{
urls: "stun:stun.l.google.com:19302",
},
],
};
const isIncluded = (array, id) => array.some((item) => item.id === id);
const createReceiverPeerConnection = (socketID, socket, roomID) => {
const pc = new wrtc.RTCPeerConnection(pc_config);
if (receiverPCs[socketID]) receiverPCs[socketID] = pc;
else receiverPCs = { ...receiverPCs, [socketID]: pc };
pc.onicecandidate = (e) => {
//console.log(`socketID: ${socketID}'s receiverPeerConnection icecandidate`);
socket.to(socketID).emit("getSenderCandidate", {
candidate: e.candidate,
});
};
pc.oniceconnectionstatechange = (e) => {
//console.log(e);
};
pc.ontrack = (e) => {
if (users[roomID]) {
if (!isIncluded(users[roomID], socketID)) {
users[roomID].push({
id: socketID,
stream: e.streams[0],
});
} else return;
} else {
users[roomID] = [
{
id: socketID,
stream: e.streams[0],
},
];
}
socket.broadcast.to(roomID).emit("userEnter", { id: socketID });
};
return pc;
};
const createSenderPeerConnection = (
receiverSocketID,
senderSocketID,
socket,
roomID
) => {
const pc = new wrtc.RTCPeerConnection(pc_config);
if (senderPCs[senderSocketID]) {
senderPCs[senderSocketID].filter((user) => user.id !== receiverSocketID);
senderPCs[senderSocketID].push({ id: receiverSocketID, pc });
} else
senderPCs = {
...senderPCs,
[senderSocketID]: [{ id: receiverSocketID, pc }],
};
pc.onicecandidate = (e) => {
//console.log(`socketID: ${receiverSocketID}'s senderPeerConnection icecandidate`);
socket.to(receiverSocketID).emit("getReceiverCandidate", {
id: senderSocketID,
candidate: e.candidate,
});
};
pc.oniceconnectionstatechange = (e) => {
//console.log(e);
};
...
......
const closeReceiverPC = (socketID) => {
if (!receiverPCs[socketID]) return;
receiverPCs[socketID].close();
delete receiverPCs[socketID];
};
const closeSenderPCs = (socketID) => {
if (!senderPCs[socketID]) return;
senderPCs[socketID].forEach((senderPC) => {
senderPC.pc.close();
const eachSenderPC = senderPCs[senderPC.id].filter(
(sPC) => sPC.id === socketID
)[0];
if (!eachSenderPC) return;
eachSenderPC.pc.close();
senderPCs[senderPC.id] = senderPCs[senderPC.id].filter(
(sPC) => sPC.id !== socketID
);
});
delete senderPCs[socketID];
};
const io = socketio.listen(server);
io.sockets.on("connection", (socket) => {
socket.on("joinRoom", (data) => {
try {
let allUsers = getOtherUsersInRoom(data.id, data.roomID);
io.to(data.id).emit("allUsers", { users: allUsers });
} catch (error) {
console.log(error);
}
});
socket.on("senderOffer", async (data) => {
try {
socketToRoom[data.senderSocketID] = data.roomID;
let pc = createReceiverPeerConnection(
data.senderSocketID,
socket,
data.roomID
);
await pc.setRemoteDescription(data.sdp);
let sdp = await pc.createAnswer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
});
await pc.setLocalDescription(sdp);
socket.join(data.roomID);
io.to(data.senderSocketID).emit("getSenderAnswer", { sdp });
} catch (error) {
console.log(error);
}
});
socket.on("senderCandidate", async (data) => {
try {
let pc = receiverPCs[data.senderSocketID];
await pc.addIceCandidate(new wrtc.RTCIceCandidate(data.candidate));
} catch (error) {
console.log(error);
}
});
.....
.........
startServer(port);
function startServer(port) {
server.listen(port, () => {
console.log(`[INFO] Server app listening on port ${port}`);
});
}