I am trying to implement the zoom clone with WebRTC using peerjs, the problem is myPeer.on("call", (call) => { its never calling, this same code is executing on the locahost for the others who made the tutorial on zoom clone, I am not sure where is the problem with my code,
Its working on heroku though its not working on localhost, so where is the problem with my localhost setup, why i am not getting the other users who joined the room on localhost, I am not understanding.
script.js
const socket = io("/");
const videoGrid = document.getElementById("video-grid");
const myPeer = new Peer(undefined, {
path: "/peerjs",
host: "/",
port: "3030",
});
let myVideoStream;
const myVideo = document.createElement("video");
myVideo.muted = true;
const peers = {};
navigator.mediaDevices
.getUserMedia({
video: true,
audio: true,
})
.then((stream) => {
myVideoStream = stream;
addVideoStream(myVideo, stream);
// Never Called this myPeer.on call
myPeer.on("call", (call) => {
call.answer(stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
});
socket.on("user-connected", (userId) => {
console.log("User Connected", userId);
connectToNewUser(userId, stream);
});
// input value
let text = $("input");
// when press enter send message
$("html").keydown(function (e) {
if (e.which == 13 && text.val().length !== 0) {
socket.emit("message", text.val());
text.val("");
}
});
socket.on("createMessage", (message) => {
$("ul").append(`<li class="message"><b>user</b><br/>${message}</li>`);
scrollToBottom();
});
});
socket.on("user-disconnected", (userId) => {
if (peers[userId]) peers[userId].close();
});
myPeer.on("open", (id) => {
socket.emit("join-room", ROOM_ID, id);
});
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
call.on("close", () => {
video.remove();
});
peers[userId] = call;
}
function addVideoStream(video, stream) {
video.srcObject = stream;
video.addEventListener("loadedmetadata", () => {
video.play();
});
videoGrid.append(video);
}
const scrollToBottom = () => {
var d = $(".main__chat_window");
d.scrollTop(d.prop("scrollHeight"));
};
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
};
const playStop = () => {
console.log("object");
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo();
} else {
setStopVideo();
myVideoStream.getVideoTracks()[0].enabled = true;
}
};
const setMuteButton = () => {
const html = `
<i class="fas fa-microphone"></i>
<span>Mute</span>
`;
document.querySelector(".main__mute_button").innerHTML = html;
};
const setUnmuteButton = () => {
const html = `
<i class="unmute fas fa-microphone-slash"></i>
<span>Unmute</span>
`;
document.querySelector(".main__mute_button").innerHTML = html;
};
const setStopVideo = () => {
const html = `
<i class="fas fa-video"></i>
<span>Stop Video</span>
`;
document.querySelector(".main__video_button").innerHTML = html;
};
const setPlayVideo = () => {
const html = `
<i class="stop fas fa-video-slash"></i>
<span>Play Video</span>
`;
document.querySelector(".main__video_button").innerHTML = html;
};
server.js
const express = require("express");
const app = express();
// const cors = require('cors')
// app.use(cors())
const server = require("http").Server(app);
const io = require("socket.io")(server);
const { ExpressPeerServer } = require("peer");
const peerServer = ExpressPeerServer(server, {
debug: true,
});
const { v4: uuidV4 } = require("uuid");
app.use("/peerjs", peerServer);
app.set("view engine", "ejs");
app.use(express.static("public"));
app.get("/", (req, res) => {
res.redirect(`/${uuidV4()}`);
});
app.get("/:room", (req, res) => {
res.render("room", { roomId: req.params.room });
});
io.on("connection", (socket) => {
socket.on("join-room", (roomId, userId) => {
socket.join(roomId);
socket.to(roomId).broadcast.emit("user-connected", userId);
// messages
socket.on("message", (message) => {
//send message to the same room
io.to(roomId).emit("createMessage", message);
});
socket.on("disconnect", () => {
socket.to(roomId).broadcast.emit("user-disconnected", userId);
});
});
});
server.listen(process.env.PORT || 3030);
the function connectToNewUser get executed before the myPeer.on('call')
try modifying the function as
socket.on("user-connected", (userId) => {
console.log("User Connected", userId);
//connectToNewUser(userId, stream);
setTimeout(connectToNewUser,1000,userId,stream)
});
I had the same issue and it seemed to be a race condition with socket.emit("join-room", ROOM_ID, id);.
The server will emit socket.to(roomId).broadcast.emit("user-connected", userId); which will trigger the function that calls the peer in connectToNewUser before the user has finished the navigator promise.
I solved my issue by making sure the navigator promise has finished before firing socket.emit("join-room", ROOM_ID, id);.
Related
I basicly Implemented a simple webrtc application where a client streams a video and then another client can connect and access the remote stream from each other.
The first implementation I did was with AgoraRTM signaling and it worked well in all browsers. After that I wanted to try to move to websockets as the signaling part.
When I finished moving to websockets, I noticed that it kept working at Firefox, but at Chrome and Edge (probably chromium browsers) the remoteStream doesn't show up (it shows up the first and second time, but stops working if I disconnect and connect a new client)
Here is my code:
main.js (frontend logic, webrtc)
let token = null;
let uid = String(Math.floor(Math.random() * 10000));
let queryString = window.location.search;
const urlSearch = new URLSearchParams(queryString);
const room = urlSearch.get("room");
if (!room) {
window.location = "lobby.html";
}
let client;
let channel;
let socket;
const constraints = {
video: {
width: { min: 640, ideal: 1920, max: 1920 },
height: { min: 480, ideal: 1080, max: 1920 },
aspectRatio: 1.777777778,
},
audio: false,
};
const servers = {
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
],
},
],
};
const localVideoRef = document.getElementById("localVideo");
const remoteVideoRef = document.getElementById("remoteVideo");
let localStream;
let remoteStream;
let peerConnection;
const configureSignaling = async () => {
socket = await io.connect("http://localhost:4000");
socket.emit("join", { room, uid });
socket.on("MemberJoined", handleMemberJoined);
socket.on("MessageFromPeer", handleMessageFromPeer);
socket.on("MemberLeft", handleMemberLeft)
};
const handleMemberLeft = async () => {
remoteVideoRef.style.display = "none";
};
const handleMessageFromPeer = (m, uid) => {
const message = JSON.parse(m.text);
if(message.type !== "candidate") {
console.log('handleMessageFromPeer: ', message, uid)
}
if (message.type === "offer") {
createAnswer(uid, message.offer);
}
if (message.type === "answer") {
addAnswer(message.answer);
}
if (message.type === "candidate") {
if (peerConnection && peerConnection.currentRemoteDescription) {
peerConnection.addIceCandidate(message.candidate);
}
}
};
const createLocalStream = async () => {
localStream = await navigator.mediaDevices.getUserMedia(constraints);
localVideoRef.srcObject = localStream;
localVideoRef.play()
remoteVideoRef.play()
};
const init = async () => {
await configureSignaling();
await createLocalStream();
};
const handleMemberJoined = async (uid) => {
createOffer(uid);
};
let createOffer = async (uid) => {
await createPeerConnection(uid);
let offer = await peerConnection.createOffer();
console.log({ offer })
await peerConnection.setLocalDescription(offer);
console.log('localStream: ', offer)
socket.emit(
"sendMessageToPeer",
{ text: JSON.stringify({ type: "offer", offer: offer }) },
uid
);
};
let createPeerConnection = async (uid) => {
peerConnection = new RTCPeerConnection(servers);
remoteStream = new MediaStream();
remoteVideoRef.srcObject = remoteStream;
remoteVideoRef.style.display = "block";
remoteVideoRef.classList.add("remoteFrame");
if (!localStream) {
await createLocalStream();
}
localStream.getTracks().forEach((track) => {
peerConnection.addTrack(track, localStream);
});
peerConnection.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track);
});
};
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit(
"sendMessageToPeer",
{
text: JSON.stringify({
type: "candidate",
candidate: event.candidate,
}),
},
uid
);
}
};
};
let createAnswer = async (uid, offer) => {
await createPeerConnection(uid);
await peerConnection.setRemoteDescription(offer);
console.log('remoteStream: ', offer)
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
console.log('localStream: ', answer)
socket.emit(
"sendMessageToPeer",
{ text: JSON.stringify({ type: "answer", answer: answer }) },
uid
);
};
let addAnswer = async (answer) => {
if (!peerConnection.currentRemoteDescription) {
peerConnection.setRemoteDescription(answer);
}
console.log(peerConnection)
};
let onLogout = async () => {
peerConnection.close()
remoteVideoRef.classList.remove("remoteFrame");
await socket.emit('onLeaveRoom', room)
};
let onToggleCamera = async () => {
const videoTrack = localStream
.getTracks()
.find((track) => track.kind === "video");
if (videoTrack.enabled) {
videoTrack.enabled = false;
document.getElementById("camera-btn").style.backgroundColor =
"rgb(255, 80, 80)";
} else {
videoTrack.enabled = true;
document.getElementById("camera-btn").style.backgroundColor =
"rgb(179, 102, 249, .9)";
}
};
let onToggleMic = async () => {
const audioTrack = localStream
.getTracks()
.find((track) => track.kind === "audio");
if (audioTrack.enabled) {
audioTrack.enabled = false;
document.getElementById("mic-btn").style.backgroundColor =
"rgb(255, 80, 80)";
} else {
audioTrack.enabled = true;
document.getElementById("mic-btn").style.backgroundColor =
"rgb(179, 102, 249, .9)";
}
};
window.addEventListener("beforeunload", onLogout);
init();
index.js (server logic, websockets)
const express = require("express");
const app = express();
const PORT = 4000;
const http = require("http").Server(app);
const cors = require("cors");
const users = []
app.use(cors());
const socketIO = require("socket.io")(http, {
cors: {
origin: "http://127.0.0.1:5501",
},
});
//Add this before the app.get() block
socketIO.on("connection", (socket) => {
socket.on('join', async ({room, uid}) => {
users.push(uid)
await socket.join(room);
socket.broadcast.to(room).emit('MemberJoined', uid)
})
socket.on("onLeaveRoom", async (room) => {
socket.broadcast.to(room).emit('MemberLeft')
})
socket.on("disconnect", async (room) => {
socket.broadcast.to(room).emit('MemberLeft')
})
socket.on('sendMessageToPeer', (data, uid) => {
socket.broadcast.emit('MessageFromPeer', data, uid )
});
});
app.get("/api", (req, res) => {
res.json({
message: "Hello world",
});
});
http.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
I tried a couple of things like:
Checking if the offer and the answer was sended correctly, just 1 time and with the correct SDP
Checked if there was a problem disconnecting from a socket that leads to have more clients in a room
Tried also to add localVideoRef.play() to check if there is an issue with the autoplay for chrome, since there was a similar thread at stackoverflow (Ended up not working)
Basicly I am working in building a webRTC application using socket.io as signaling, I had previously agoraRTM and the app worked fine.
The app is quite simple, basicly I have the client streaming video and when I open a new tab I want to show a small screen with the local stream and a bigger one with the remote stream.
The application works the first time I enter, so, I open a first tab (I see my local Stream), I open the second one and I see both (remote and local stream).
The problems start happening when I close one of the tabs and try to duplicate the other tab again (basicaly leaving the room and joining again)
I tried a couple of things, like disconnecting the socket, leaving the room. I am not sure why it is happening. (I am learning websockets, it might be something that I am missing)
Here is my code:
client (main.js)
let token = null;
let uid = String(Math.floor(Math.random() * 10000));
let queryString = window.location.search;
const urlSearch = new URLSearchParams(queryString);
const room = urlSearch.get("room");
if (!room) {
window.location = "lobby.html";
}
let client;
let channel;
let socket;
const constraints = {
video: {
width: { min: 640, ideal: 1920, max: 1920 },
height: { min: 480, ideal: 1080, max: 1920 },
aspectRatio: 1.777777778,
},
audio: false,
};
const servers = {
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
],
},
],
};
const localVideoRef = document.getElementById("localVideo");
const remoteVideoRef = document.getElementById("remoteVideo");
let localStream;
let remoteStream;
let peerConnection;
const configureSignaling = async () => {
socket = await io.connect("http://localhost:4000");
socket.emit("join", { room, uid });
socket.on("MemberJoined", handleMemberJoined);
socket.on("MessageFromPeer", handleMessageFromPeer);
socket.on("MemberLeft", handleMemberLeft)
};
const handleMemberLeft = async () => {
remoteVideoRef.style.display = "none";
};
const handleMessageFromPeer = (m, uid) => {
const message = JSON.parse(m.text);
if(message.type !== "candidate") {
console.log('handleMessageFromPeer: ', message, uid)
}
if (message.type === "offer") {
createAnswer(uid, message.offer);
}
if (message.type === "answer") {
addAnswer(message.answer);
}
if (message.type === "candidate") {
if (peerConnection && peerConnection.currentRemoteDescription) {
peerConnection.addIceCandidate(message.candidate);
}
}
};
const createLocalStream = async () => {
localStream = await navigator.mediaDevices.getUserMedia(constraints);
localVideoRef.srcObject = localStream;
};
const init = async () => {
await configureSignaling();
await createLocalStream();
};
const handleMemberJoined = async (uid) => {
createOffer(uid);
};
let createOffer = async (uid) => {
await createPeerConnection(uid);
let offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
socket.emit(
"sendMessageToPeer",
{ text: JSON.stringify({ type: "offer", offer: offer }) },
uid
);
};
let createPeerConnection = async (uid) => {
peerConnection = new RTCPeerConnection(servers);
remoteStream = new MediaStream();
remoteVideoRef.srcObject = remoteStream;
remoteVideoRef.style.display = "block";
remoteVideoRef.classList.add("remoteFrame");
if (!localStream) {
await createLocalStream();
}
localStream.getTracks().forEach((track) => {
peerConnection.addTrack(track, localStream);
});
peerConnection.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track);
});
};
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit(
"sendMessageToPeer",
{
text: JSON.stringify({
type: "candidate",
candidate: event.candidate,
}),
},
uid
);
}
};
};
let createAnswer = async (uid, offer) => {
await createPeerConnection(uid);
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit(
"sendMessageToPeer",
{ text: JSON.stringify({ type: "answer", answer: answer }) },
uid
);
};
let addAnswer = async (answer) => {
console.log(!peerConnection.currentRemoteDescription, answer)
if (!peerConnection.currentRemoteDescription) {
peerConnection.setRemoteDescription(answer);
}
};
let onLogout = async () => {
remoteVideoRef.classList.remove("remoteFrame");
await socket.emit('onLeaveRoom', room)
};
let onToggleCamera = async () => {
const videoTrack = localStream
.getTracks()
.find((track) => track.kind === "video");
if (videoTrack.enabled) {
videoTrack.enabled = false;
document.getElementById("camera-btn").style.backgroundColor =
"rgb(255, 80, 80)";
} else {
videoTrack.enabled = true;
document.getElementById("camera-btn").style.backgroundColor =
"rgb(179, 102, 249, .9)";
}
};
let onToggleMic = async () => {
const audioTrack = localStream
.getTracks()
.find((track) => track.kind === "audio");
if (audioTrack.enabled) {
audioTrack.enabled = false;
document.getElementById("mic-btn").style.backgroundColor =
"rgb(255, 80, 80)";
} else {
audioTrack.enabled = true;
document.getElementById("mic-btn").style.backgroundColor =
"rgb(179, 102, 249, .9)";
}
};
window.addEventListener("beforeunload", onLogout);
init();
server (index.js)
const express = require("express");
const app = express();
const PORT = 4000;
const http = require("http").Server(app);
const cors = require("cors");
app.use(cors());
const socketIO = require("socket.io")(http, {
cors: {
origin: "http://127.0.0.1:5501",
},
});
//Add this before the app.get() block
socketIO.on("connection", (socket) => {
console.log(`⚡: ${socket.id} user just connected!`);
console.log('CONNECTED')
socket.on('join', async ({room, uid}) => {
console.log({ room })
await socket.join(room);
socket.broadcast.emit('MemberJoined', uid)
})
socket.on("onLeaveRoom", async (room) => {
await socket.leave(room)
socket.broadcast.emit('MemberLeft')
})
socket.on("disconnect", async (room) => {
await socket.leave('123')
socket.broadcast.emit('MemberLeft')
})
socket.on('sendMessageToPeer', (data, uid) => socket.broadcast.emit('MessageFromPeer', data, uid));
});
app.get("/api", (req, res) => {
res.json({
message: "Hello world",
});
});
http.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
I tried a couple of things, like disconnecting the socket.io when the user leaves, leaving the room . I feel like I am missing something websockets related in this case. Would like to know if that is the case :)
I am trying to create a video conference app using the tutorial https://youtu.be/DvlyzDZDEq4 . I want to add one feature to change the quality of video( not the resolution of the video ) user can see like we have in YouTube videos auto, high picture quality etc. I am very new to WebRTC and Peer library so any help is appreciated. Thank you.
script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '3001'
})
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
})
})
socket.on('user-disconnected', userId => {
if (peers[userId]) peers[userId].close()
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
room.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<script defer src="https://unpkg.com/peerjs#1.2.0/dist/peerjs.min.js"></script>
<script src="/socket.io/socket.io.js" defer></script>
<script src="script.js" defer></script>
</head>
<body>
<div id="video-grid"></div>
</body>
</html>
server.js
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4 } = require('uuid')
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
io.on('connection', socket => {
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
socket.to(roomId).broadcast.emit('user-connected', userId)
socket.on('disconnect', () => {
socket.to(roomId).broadcast.emit('user-disconnected', userId)
})
})
})
server.listen(3000)
Updated Script.js
const socket = io('/zoom')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '3002'
})
let pc;
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
let myVideoStream;
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
myVideoStream = stream;
addVideoStream(myVideo, stream,user)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream,user)
})
})
socket.on("user-connected", (userId, username) =>
{ console.log("checking "+ username);
setTimeout(function ()
{ connectToNewUser(userId, stream);
console.log("zoooom "+ username);
}, 1000);
});
let text = $("input");
// when press enter send message
$('html').keydown(function (e) {
if (e.which == 13 && text.val().length !== 0) {
socket.emit('message', text.val());
text.val('')
}
});
socket.on("createMessage", (message, username) => {
//$("ul").append(`<li class="message"><b>${username}</b><br/>${message}</li>`);
scrollToBottom()
})
})
socket.on('user-disconnected', (userId, username) => {
$("ul").append(`<span class="messageHeader"><small>${username} Left Meeting</small></span>`);
if (peers[userId]) peers[userId].close()
})
const offerOptions = {
offerToReceiveAudio: 0,
offerToReceiveVideo: 1
};
myPeer.on('open', id => {
const servers = null;
pc1 = new RTCPeerConnection(servers);
pc1.createOffer({
offerToReceiveAudio: 0,
offerToReceiveVideo: 1
})
.then((desc)=>{const videobitrate = 20000;
return pc1.setLocalDescription(desc);
/* let offer = pc1.localDescription;
console.log("offer "+pc1.localDescription);
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `$1b=AS:125\r\n`);
pc1.setLocalDescription(offer);*/}).then(()=>{
let offer = pc1.localDescription;
console.log("offer "+pc1.localDescription);
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `$1b=AS:20000\r\n`);
return pc1.setLocalDescription(offer);
}).then(()=>{console.log(pc1.localDescription.sdp)})
.catch((err)=>{ console.log('Failed to set session description: ' + err);});
console.log("user mypeer"+user);
socket.emit('join-room', ROOM_ID,id,user);
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const servers = null;
pc2 = new RTCPeerConnection(servers);
pc2.createOffer({
offerToReceiveAudio: 0,
offerToReceiveVideo: 1
})
.then((desc)=>{const videobitrate = 20000;
return pc2.setLocalDescription(desc);
/* let offer = pc1.localDescription;
console.log("offer "+pc1.localDescription);
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `$1b=AS:125\r\n`);
pc1.setLocalDescription(offer);*/}).then(()=>{
let offer = pc2.localDescription;
console.log("offer "+pc2.localDescription);
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `$1b=AS:20000\r\n`);
return pc2.setLocalDescription(offer);
}).then(()=>{console.log(pc2.localDescription.sdp)})
.catch((err)=>{ console.log('Failed to set session description: ' + err);});
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream,user)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream,username) {
video.srcObject = stream
console.log("adding video")
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
video.insertAdjacentHTML('beforebegin',`<b style="color:white">${username}</b>`);
}
const scrollToBottom = () => {
var d = $('.main__chat_window');
d.scrollTop(d.prop("scrollHeight"));
}
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
}
const playStop = () => {
console.log('object')
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo()
} else {
setStopVideo()
myVideoStream.getVideoTracks()[0].enabled = true;
}
}
const setMuteButton = () => {
const html = `
<i class="fas fa-microphone"></i>
<span>Mute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setUnmuteButton = () => {
const html = `
<i class="unmute fas fa-microphone-slash"></i>
<span>Unmute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setStopVideo = () => {
const html = `
<i class="fas fa-video"></i>
<span>Stop Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
const setPlayVideo = () => {
const html = `
<i class="stop fas fa-video-slash"></i>
<span>Play Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
I am making chatroulette website. right now whenever i will send link other user can connect to my call, but i want it to work as follow:
Whenever user will click button it will find random user and join it to that room.
How can i implement this feature in socketio and peerjs libraries.
server.js
if (process.env.NODE_ENV !== "production") {
require('dotenv').config();
}
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const {v4:uuidV4} = require('uuid');
const {MongoClient} = require('mongodb');
const client = new MongoClient(process.env["DB_URL"], {
useUnifiedTopology: true
});
app.set('view engine', 'ejs');
app.use(express.static('public'));
// app.get('/', (req, res)=>{
// res.render("landing")
// });
app.get('/', (req, res)=>{
res.render('landing');
});
app.get('/chat', (req, res)=>{
res.redirect(`/${uuidV4()}`)
});
app.get('/:room', (req, res)=>{
res.render('room', {roomId: req.params.room});
});
io.on('connection', socket => {
let room = '';
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
socket.to(roomId).broadcast.emit('user-connected', userId)
socket.on('message', message =>{
io.to(roomId).emit('createMessage', message);
});
socket.on('disconnect', () => {
socket.to(roomId).broadcast.emit('user-disconnected', userId)
})
})
})
const port = process.env.port || 3000;
server.listen(port, ()=> {
console.log("3000")
});
script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '3001'
})
let myVideoStream;
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {};
navigator.mediaDevices.getUserMedia({
video: true,
audio: false
}).then(stream => {
myVideoStream = stream;
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
})
let text = $('input');
$('html').keydown((e)=>{
if(e.which == 13 && text.val().length !== 0){
console.log(text.val());
socket.emit('message', text.val());
text.val('')
}
});
socket.on('createMessage', message =>{
$('ul').append(`<li class="message"><b>user</b><br/>${message}</li>`);
scrollToBottom();
});
});
socket.on('user-disconnected', userId => {
if (peers[userId]) peers[userId].close()
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
const scrollToBottom = () => {
let d = $('.main__chat_window');
d.scrollTop(d.prop("scrollHeight"));
}
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
}
const playStop = () => {
console.log('object')
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo()
} else {
setStopVideo()
myVideoStream.getVideoTracks()[0].enabled = true;
}
}
const setMuteButton = () => {
const html = `
<i class="fas fa-microphone"></i>
<span>Mute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setUnmuteButton = () => {
const html = `
<i class="unmute fas fa-microphone-slash"></i>
<span>Unmute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setStopVideo = () => {
const html = `
<i class="fas fa-video"></i>
<span>Stop Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
const setPlayVideo = () => {
const html = `
<i class="stop fas fa-video-slash"></i>
<span>Play Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
You Have to Save All PeerJS Id in Array & then Choose a random Id and Send it to the Client. And If Any User Disconnects remove the ID From the Array.
server.js
if (process.env.NODE_ENV !== "production") {
require('dotenv').config();
}
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const {v4:uuidV4} = require('uuid');
const {MongoClient} = require('mongodb');
const client = new MongoClient(process.env["DB_URL"], {
useUnifiedTopology: true
});
app.set('view engine', 'ejs');
app.use(express.static('public'));
// app.get('/', (req, res)=>{
// res.render("landing")
// });
app.get('/', (req, res)=>{
res.render('landing');
});
app.get('/chat', (req, res)=>{
res.redirect(`/${uuidV4()}`)
});
app.get('/:room', (req, res)=>{
res.render('room', {roomId: req.params.room});
});
let Ids = []; // Creating Ids Array
io.on('connection', socket => {
let room = '';
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
Ids.push(userId); // Push Id in Ids Array when User Join
socket.to(roomId).broadcast.emit('user-connected', userId)
socket.on('message', message =>{
io.to(roomId).emit('createMessage', message);
});
socket.on('getId',() =>{
id = Ids[Math.floor(Math.random() * Ids.length)]; // Taking a Random Id
io.to(roomId).emit('recieveId', {id:id,userId:userId}); //Sending The Id with The UserId
});
socket.on('disconnect', () => {
socket.to(roomId).broadcast.emit('user-disconnected', userId)
const index = Ids.indexOf(userId);
if (index > -1) {
Ids.splice(index, 1);
} // removing Id From Ids Array When User Disconnect
})
})
})
//arr[Math.floor(Math.random() * arr.length)];
const port = process.env.port || 3000;
server.listen(port, ()=> {
console.log("3000")
});
script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '3001'
})
let myVideoStream;
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {};
navigator.mediaDevices.getUserMedia({
video: true,
audio: false
}).then(stream => {
myVideoStream = stream;
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
socket.emit('getId'); //sending GetId Event
})
let text = $('input');
$('html').keydown((e)=>{
if(e.which == 13 && text.val().length !== 0){
console.log(text.val());
socket.emit('message', text.val());
text.val('')
}
});
socket.on('createMessage', message =>{
$('ul').append(`<li class="message"><b>user</b><br/>${message}</li>`);
scrollToBottom();
});
});
socket.on('user-disconnected', userId => {
if (peers[userId]) peers[userId].close()
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
socket.on('reciveId', data => {
if data['userId'] == id{
connectToNewUser(data['id'],stream) // send Your Stream Here
}
}); // Recieving Recive Id Event
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
const scrollToBottom = () => {
let d = $('.main__chat_window');
d.scrollTop(d.prop("scrollHeight"));
}
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
}
const playStop = () => {
console.log('object')
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo()
} else {
setStopVideo()
myVideoStream.getVideoTracks()[0].enabled = true;
}
}
const setMuteButton = () => {
const html = `
<i class="fas fa-microphone"></i>
<span>Mute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setUnmuteButton = () => {
const html = `
<i class="unmute fas fa-microphone-slash"></i>
<span>Unmute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setStopVideo = () => {
const html = `
<i class="fas fa-video"></i>
<span>Stop Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
const setPlayVideo = () => {
const html = `
<i class="stop fas fa-video-slash"></i>
<span>Play Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
Note: Please Follow The Comments in The Code
In this chat app I want the server to disconnect clients who's been inactive for x (configurable) amount of time.
I think the default is 60 sec, I think I should use page Timeout as you can see in my server file but not sure how to make it work.
The idea is to have a timer for let's say 2 minutes of inactivity and you get kicked out with a message appearing afterwards in the feedbackBox function in name.js file.
My code is below:
server.js
const PORT = 3000;
const io = require('socket.io')(PORT, {
pingTimeout: 60000
});
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
const users = {};
io.on('connection', socket => {
socket.on('new-user', name => {
if (Object.values(users).includes(name)) {
socket.emit('name-taken', name);
}
if (!Object.values(users).includes(name)) {
users[socket.id] = name;
socket.emit('user-accepted');
socket.broadcast.emit('user-connected', name);
}
});
socket.on('send-chat-message', message => {
socket.broadcast.emit('chat-message', {
message: message,
name: users[socket.id]
});
});
socket.on('disconnect', reason => {
socket.broadcast.emit('user-disconnected', users[socket.id]);
delete users[socket.id];
});
});
name.js
const container = document.querySelector('.container');
const nameContainer = document.createElement('section');
const title = document.createElement('h1');
const form = document.createElement('form');
const nameInput = document.createElement('input');
const submitName = document.createElement('button');
title.innerText = 'Enter Your Name';
submitName.innerText = 'Connect';
const getName = () => {
form.addEventListener('submit', e => {
e.preventDefault();
const name = nameInput.value;
socket.emit('new-user', name);
nameInput.value = '';
socket.off('name-taken');
socket.on('name-taken', () => {
feedbackBox('Nickname already taken');
});
socket.off('user-accepted');
socket.on('user-accepted', () => {
title.classList.add('hide');
nameContainer.classList.add('hide');
addMessageForm();
});
});
};
const appendForm = () => {
nameInput.classList.add('name_input');
form.appendChild(nameInput);
submitName.classList.add('submit_name');
form.appendChild(submitName);
nameContainer.appendChild(form);
nameContainer.classList.add('name_container');
nameContainer.classList.remove('hide');
title.classList.remove('hide');
title.classList.add('name_title');
container.appendChild(title);
container.appendChild(nameContainer);
getName();
};
chat.js
//const socket = io('http://localhost:3000');
const showFeedback = document.createElement('section');
const messageContainer = document.createElement('section');
const messageForm = document.createElement('form');
const messageInput = document.createElement('input');
const submitMessage = document.createElement('button');
const disconnectButton = document.createElement('button');
submitMessage.innerText = 'Send';
disconnectButton.innerText = 'X';
messageContainer.classList.add('message-container');
messageForm.classList.add('send-container');
messageInput.classList.add('message-input');
submitMessage.classList.add('send-button');
disconnectButton.classList.add('disconnect-button');
const addMessageForm = () => {
showFeedback.classList.add('hide');
messageContainer.classList.remove('hide');
messageForm.classList.remove('hide');
disconnectButton.classList.remove('hide');
messageForm.appendChild(messageInput);
messageForm.appendChild(submitMessage);
container.appendChild(disconnectButton);
container.appendChild(messageContainer);
container.appendChild(messageForm);
appendMessage('You joined');
};
//const name = prompt('What is your name?');
//socket.emit('new-user', name);
socket.on('chat-message', data => {
appendMessage(`${data.name}: ${data.message}`);
});
socket.on('user-connected', name => {
appendMessage(`${name} connected`);
});
socket.on('user-disconnected', name => {
appendMessage(`${name} left the chat`);
});
messageForm.addEventListener('submit', e => {
e.preventDefault();
const message = messageInput.value;
appendMessage(`You: ${message}`);
socket.emit('send-chat-message', message);
messageInput.value = '';
});
function appendMessage(message) {
const messageElement = document.createElement('section');
messageElement.innerText = message;
messageContainer.append(messageElement);
}
const feedbackBox = message => {
showFeedback.innerText = message;
showFeedback.classList.add('feedback-I-disconnect');
showFeedback.classList.remove('hide');
container.appendChild(showFeedback);
};
disconnectButton.addEventListener('click', event => {
if (event.target.classList.contains('disconnect-button')) {
socket.disconnect();
messageContainer.classList.add('hide');
messageForm.classList.add('hide');
disconnectButton.classList.add('hide');
appendForm();
feedbackBox('You disconnected from the chat');
}
});
You could set a timeout on the socket and clear the timeout every time you receive a message from that client:
socket.on("sent-chat-message", message => {
clearTimeout(socket.inactivityTimeout);
socket.inactivityTimeout = setTimeout(() => socket.disconnect(true), 1000 * 60);
});
I'm not sure if the connection will be re-established automatically, in which case you'd have to do this on the client-side as well.