I am trying to create a video chat and using express and socket.io on the backend and react and simple-peer on the frontend.
This is my server code:
const http = require('http');
const express = require('express');
const app = express();
const httpServer = http.createServer(app);
app.use(require('cors')());
const io = require('socket.io')(httpServer, {
cors: {
origin: ['http://localhost:3000', 'http://localhost:3001'],
method: ['GET', 'POST']
}
});
const cache = {};
io.on('connection', socket => {
socket.on('join', id => {
cache[id] = socket.id;
console.log('cache', cache);
socket.join(id);
});
socket.on('disconnect', () => {
socket.broadcast.emit('callEnded');
});
socket.on('callUser', data => {
console.log('calling user', data.userToCall, cache[data.userToCall]);
io.to(data.userToCall).emit('callUser', {
signal: data.signalData,
from: data.from,
name: data.name
});
});
// socket.on('answerCall', data => {
// console.log('data', data);
// io.to(cache[data.to]).emit('callAccepted', data.signal);
// });
socket.on('answerCall', data => {
console.log('answering call', data);
io.to(data.to).emit('callAccepted', data.signal);
});
});
httpServer.listen(4000, () => 'Listening...');
I am accepting requests from port 3000 and 3001 because that is where I am running my two apps. As I don't have a login system on the backend at the moment, I am running one app for each peer.
The code for the first app looks like this:
import { useEffect, useState, useRef } from "react";
import Peer from "simple-peer";
import io from "socket.io-client";
import './App.css';
const ID = 'fey'
function App() {
const [ stream, setStream ] = useState()
const [receivingCall, setReceivingCall] = useState(false) ;
const [caller, setCaller] = useState("") ;
const [callerSignal, setCallerSignal] = useState() ;
const [callAccepted, setCallAccepted] = useState(false);
const [callEnded, setCallEnded] = useState(false);
const socket = io.connect("http://localhost:4000/");
const myVideo = useRef()
const userVideo = useRef()
const connectionRef = useRef()
useEffect(() => {
navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then((stream)=>{
setStream(stream)
myVideo.current.srcObject = stream
})
.catch((err) => console.log(err))
socket.emit('join', ID)
socket.on("callUser", (data)=>{
setReceivingCall(true)
setCaller(data.from)
setCallerSignal(data.signal)
})
}, [])
const callUser = ()=> {
const peer = new Peer({
initiator:true,
trickle:false,
stream:stream
})
peer.on("signal", (data)=>{
socket.emit("callUser",{
userToCall: 'fey-clone',
signalData: data,
from: ID,
name: "Fey"
})
})
peer.on("stream", (stream)=> {
userVideo.current.srcObject = stream
})
socket.on("callAccepted", (signal) => {
console.log('call accepted!!')
setCallAccepted(true)
peer.signal(signal)
})
connectionRef.current = peer
}
const answerCall = () => {
console.log('peer exists')
setCallAccepted(true)
const peer = new Peer({
initiator:false,
trickle:false,
stream: stream
})
peer.on("signal", (data)=> {
console.log('call answered')
socket.emit("answerCall", {signal:data, to: caller})
})
peer.on("stream", (stream) =>{
userVideo.current.srcObject = stream
})
peer.signal(callerSignal)
connectionRef.current = peer
}
const leaveCall = ()=>{
setCallEnded(true)
connectionRef.current.destroy()
}
return (
<div className="App">
<div className="video">
{stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px", height: "300px" }} />}
{callAccepted && <video playsInline muted ref={userVideo} autoPlay style={{width: "300px", height: "300px" }} />}
</div>
{callAccepted && !callEnded ? (
<button onClick={leaveCall}>
End Call
</button>
):(
<button onClick={callUser}>call meeee</button>
)}
{receivingCall && !callAccepted ? (
<div className="caller">
<h1>Fey is calling ...</h1>
<button onClick={answerCall} >
Answer
</button>
</div>
) : null
}
</div>
);
}
export default App;
The code for the other peer looks unsurprisingly similar, but the ID is different and the userToCall is the other one.
import { useEffect, useState, useRef } from "react";
import Peer from "simple-peer";
import io from "socket.io-client";
import './App.css';
const ID = 'fey-clone'
function App() {
const [ stream, setStream ] = useState()
const [receivingCall, setReceivingCall] = useState(false) ;
const [caller, setCaller] = useState("") ;
const [callerSignal, setCallerSignal] = useState() ;
const [callAccepted, setCallAccepted] = useState(false);
const [callEnded, setCallEnded] = useState(false);
const socket = io.connect("http://localhost:4000/");
const myVideo = useRef()
const userVideo = useRef()
const connectionRef = useRef()
useEffect(() => {
navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then((stream)=>{
setStream(stream)
myVideo.current.srcObject = stream
})
.catch((err) => console.log(err))
socket.emit('join', ID)
socket.on("callUser", (data)=>{
setReceivingCall(true)
setCaller(data.from)
setCallerSignal(data.signal)
})
}, [])
const callUser = ()=> {
const peer = new Peer({
initiator:true,
trickle:false,
stream:stream
})
peer.on("signal", (data)=>{
socket.emit("callUser",{
userToCall: 'fey',
signalData: data,
from: ID,
name: "fey clone"
})
})
peer.on("stream", (stream)=> {
userVideo.current.srcObject = stream
})
socket.on("callAccepted", (signal) => {
console.log('call accepted!!')
setCallAccepted(true)
peer.signal(signal)
})
connectionRef.current = peer
}
const answerCall = () => {
setCallAccepted(true)
const peer = new Peer({
initiator:false,
trickle:false,
stream: stream
})
peer.on("signal", (data)=> {
console.log(data, caller)
socket.emit("answerCall", {signal:data, to: caller})
})
peer.on("stream", (stream) =>{
console.log('I streeeam')
userVideo.current.srcObject = stream
})
peer.signal(callerSignal)
connectionRef.current = peer
}
const leaveCall = ()=>{
setCallEnded(true)
connectionRef.current.destroy()
}
return (
<div className="App">
<div className="video">
{stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px", height: "300px" }} />}
{callAccepted && <video playsInline muted ref={userVideo} autoPlay style={{width: "300px", height: "300px" }} />}
</div>
{callAccepted && !callEnded ? (
<button onClick={leaveCall}>
End Call
</button>
):(
<button onClick={callUser}>call meeee</button>
)}
{receivingCall && !callAccepted ? (
<div className="caller">
<h1>Fey is calling ...</h1>
<button onClick={answerCall} >
Answer
</button>
</div>
) : null
}
</div>
);
}
export default App;
The call seems to be going through the right recipient. When fey calls fey-clone, fey-clone can accept the call. The signal seems to work fine as well. However, it seems that the original caller fey never receives the event callAccepted from the server, so the video call cannot start. It appears with most probability that the server does not emit the event to the right peer, but I tried to debug to no avail. Is there something I am missing here?
It looks like the problem lies on the connection from the client.
The connection is happening inside the component, which means a new connection will be created every time the component re-renders. Bad mistake.
My code on the client looks like this now:
import React,{ useEffect, useRef, useState } from "react";
import Peer from "simple-peer";
import io from "socket.io-client";
const socket = io.connect('http://localhost:5000')
function Chat() {
const [ stream, setStream ] = useState()
const [ receivingCall, setReceivingCall ] = useState(false)
const [ caller, setCaller ] = useState("")
const [ callerSignal, setCallerSignal ] = useState()
const [ callAccepted, setCallAccepted ] = useState(false)
const [ idToCall, setIdToCall ] = useState("")
const [ callEnded, setCallEnded] = useState(false)
const [ name, setName ] = useState("")
const myVideo = useRef()
const userVideo = useRef()
const connectionRef= useRef()
useEffect(() => {
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then((stream) => {
setStream(stream)
myVideo.current.srcObject = stream
})
socket.emit('join', 'fey')
socket.on("callUser", (data) => {
setReceivingCall(true)
setCaller(data.from)
setName(data.name)
setCallerSignal(data.signal)
})
}, [])
const callUser = (id) => {
const peer = new Peer({
initiator: true,
trickle: false,
stream: stream
})
peer.on("signal", (data) => {
socket.emit("callUser", {
userToCall: 'fey-clone',
signalData: data,
from: 'fey',
name: 'fey'
})
})
peer.on("stream", (stream) => {
userVideo.current.srcObject = stream
})
socket.on("callAccepted", (signal) => {
setCallAccepted(true)
peer.signal(signal)
})
connectionRef.current = peer
}
const answerCall =() => {
setCallAccepted(true)
const peer = new Peer({
initiator: false,
trickle: false,
stream: stream
})
peer.on("signal", (data) => {
socket.emit("answerCall", { signal: data, to: caller })
})
peer.on("stream", (stream) => {
userVideo.current.srcObject = stream
})
peer.signal(callerSignal)
connectionRef.current = peer
}
const leaveCall = () => {
setCallEnded(true)
connectionRef.current.destroy()
}
return (
<>
<h1>zoomish</h1>
<div className="container">
<div className="video-container">
<div className="video">
{stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px" }} />}
</div>
<div className="video">
{callAccepted && !callEnded ?
<video playsInline ref={userVideo} autoPlay style={{width:"300px"}}/>
: null}
</div>
</div>
<div className="myId">
<div className="call-button">
{callAccepted && !callEnded ? (
<button onClick={leaveCall}>End Call</button>
):(
<button onClick={()=>callUser(idToCall)}>call me</button>
)}
</div>
</div>
<div>
{receivingCall && !callAccepted ? (
<div className="caller">
<h1>{name} is calling ...</h1>
<button onClick={answerCall}>Answer</button>
</div>
):null}
</div>
</div>
</>
);
}
export default Chat;
On the server nothing is particularly wrong, but I replicating the room that is already assigned to a socket once it connects with socket.join, and that does not need to be done. So I moved to maintaining a map of userId and socket.id. In a production app, this would probably be delegate to a separate storage for better handling the traffic. My server looks like this now:
const express = require("express")
const http = require("http")
const app = express()
const server = http.createServer(app)
const io = require("socket.io")(server, {
cors: {
origin: ["http://localhost:3000", "http://localhost:3001" ],
methods: [ "GET", "POST" ]
}
})
let users = {}
io.on("connection", (socket) => {
socket.on('join', (userId) => {
users[userId] = socket.id
});
socket.on("disconnect", () => {
socket.broadcast.emit("callEnded")
})
socket.on("callUser", (data) => {
io.to(users[data.userToCall]).emit("callUser", {
signal: data.signalData,
from: data.from,
name: data.name
})
})
socket.on("answerCall", (data) => {
io.to(users[data.to]).emit("callAccepted", data.signal)
})
})
server.listen(5000, () => console.log("server is running on port 5000"))
And that's it!
Related
This is Messenger.jsx page
import "./messenger.css";
import Topbar from "../../components/topbar/Topbar";
import Conversation from "../../components/conversations/Conversation";
import Message from "../../components/message/Message";
import ChatOnline from "../../components/chatOnline/ChatOnline";
import { useContext, useEffect, useRef, useState } from "react";
import { AuthContext } from "../../context/AuthContext";
import axios from "axios";
import { io } from "socket.io-client";
export default function Messenger() {
const [conversations, setConversations] = useState([]);
const [currentChat, setCurrentChat] = useState(null);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState("");
const [arrivalMessage, setArrivalMessage] = useState(null);
const [onlineUsers, setOnlineUsers] = useState([]);
const socket = useRef();
const { user } = useContext(AuthContext);
const scrollRef = useRef();
const client = axios.create({
baseURL: "http://localhost:8800/api"
});
useEffect(() => {
socket.current = io("ws://localhost:8900");
socket.current.on("getMessage", (data) => {
setArrivalMessage({
sender: data.senderId,
text: data.text,
createdAt: Date.now(),
});
});
}, []);
useEffect(() => {
arrivalMessage &&
currentChat?.members.includes(arrivalMessage.sender) &&
setMessages((prev) => [...prev, arrivalMessage]);
}, [arrivalMessage, currentChat]);
useEffect(() => {
socket.current.emit("addUser", user._id);
socket.current.on("getUsers", (users) => {
setOnlineUsers(
user.followings.filter((f) => users.some((u) => u.userId === f))
);
});
}, [user]);
useEffect(() => {
const getConversations = async () => {
try {
const res = await client.get("/conversations/" + user._id);
setConversations(res.data);
} catch (err) {
console.log(err);
}
};
getConversations();
}, [user._id]);
useEffect(() => {
const getMessages = async () => {
try {
const res = await client.get("/messages/" + currentChat?._id);
setMessages(res.data);
} catch (err) {
console.log(err);
}
};
getMessages();
}, [currentChat]);
const handleSubmit = async (e) => {
e.preventDefault();
const message = {
sender: user._id,
text: newMessage,
conversationId: currentChat._id,
};
const receiverId = currentChat.members.find(
(member) => member !== user._id
);
socket.current.emit("sendMessage", {
senderId: user._id,
receiverId,
text: newMessage,
});
try {
const res = await client.post("/messages", message);
setMessages([...messages, res.data]);
setNewMessage("");
} catch (err) {
console.log(err);
}
};
useEffect(() => {
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
return (
<>
<Topbar />
<div className="messenger">
<div className="chatMenu">
<div className="chatMenuWrapper">
<input placeholder="Search for friends" className="chatMenuInput" />
{conversations.map((c) => (
<div onClick={() => setCurrentChat(c)}>
<Conversation conversation={c} currentUser={user} />
</div>
))}
</div>
</div>
<div className="chatBox">
<div className="chatBoxWrapper">
{currentChat ? (
<>
<div className="chatBoxTop">
{messages.map((m) => (
<div ref={scrollRef}>
<Message message={m} own={m.sender === user._id} />
</div>
))}
</div>
<div className="chatBoxBottom">
<textarea
className="chatMessageInput"
placeholder="write something..."
onChange={(e) => setNewMessage(e.target.value)}
value={newMessage}
></textarea>
<button className="chatSubmitButton" onClick={handleSubmit}>
Send
</button>
</div>
</>
) : (
<span className="noConversationText">
Open a conversation to start a chat.
</span>
)}
</div>
</div>
<div className="chatOnline">
<div className="chatOnlineWrapper">
<ChatOnline
onlineUsers={onlineUsers}
currentId={user._id}
setCurrentChat={setCurrentChat}
/>
</div>
</div>
</div>
</>
);
}
This is the error iam getting:
a user connected.
a user connected.
D:\MYPROJECTS\cfg\mern tutorials\chatapp\socket\index.js:35
io.to(user.socketId).emit("getMessage", {
^
TypeError: Cannot read properties of undefined (reading 'socketId')
I have created a node app to initiate socket. In the index.js file of socket-app i made all connections. Iam having socket error, it is saying socket app crashed.
index.js page:
const io = require("socket.io")(8900, {
cors: {
origin: "http://localhost:3000",
},
});
let users = [];
const addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
const removeUser = (socketId) => {
users = users.filter((user) => user.socketId !== socketId);
};
const getUser = (userId) => {
return users.find((user) => user.userId === userId);
};
io.on("connection", (socket) => {
//when ceonnect
console.log("a user connected.");
//take userId and socketId from user
socket.on("addUser", (userId) => {
addUser(userId, socket.id);
io.emit("getUsers", users);
});
//send and get message
socket.on("sendMessage", ({ senderId, receiverId, text }) => {
const user = getUser(receiverId);
io.to(user.socketId).emit("getMessage", {
senderId,
text,
});
});
//when disconnect
socket.on("disconnect", () => {
console.log("a user disconnected!");
removeUser(socket.id);
io.emit("getUsers", users);
});
});
from this socket.on("sendMessage", ({ senderId, receiverId, text }) => {
const user = getUser(receiverId);
io.to(user.socketId).emit("getMessage", {
senderId,
text,
});
});
to
socket.on("sendMessage", ({ senderId, receiverId, text }) => {
const user = getUser(receiverId);
io.to(user?.socketId).emit("getMessage", {
senderId,
text,
});
});
I am trying to make a group video call app with the help of a youtube tutorial. I am using react and socket io,
But somehow its giving me the above error. Can't understand what exactly I am doing wrong.
Kindly correct me.
Attaching the code for reference:
Create room page- The homepage
import React from "react";
import { v1 as uuid } from "uuid";
import { useNavigate } from "react-router-dom";
const CreateRoom = (props) => {
const navigate = useNavigate();
function create() {
const id = uuid();
//props.history.push(`/room/${id}`);
navigate(`/room/${id}`);
}
return (
<button onClick={create}>Create room</button>
);
};
export default CreateRoom;
Room.js - This is the landing page once call gets started.
import React, { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
import styled from "styled-components";
import { useParams } from "react-router-dom";
const Container = styled.div`
padding: 20px;
display: flex;
height: 100vh;
width: 90%;
margin: auto;
flex-wrap: wrap;
`;
const StyledVideo = styled.video`
height: 40%;
width: 50%;
`;
const Video = (props) => {
const ref = useRef();
useEffect(() => {
props.peer.on("stream", (stream) => {
ref.current.srcObject = stream;
});
});
return <StyledVideo playsInline autoPlay ref={ref} />;
};
const videoConstraints = {
height: window.innerHeight / 2,
width: window.innerWidth / 2,
};
const Room = (props) => {
const [peers, setPeers] = useState([]);
const socketRef = useRef();
const userVideo = useRef();
const peersRef = useRef([]);
let {roomID} = useParams();
console.log(roomID);
useEffect(() => {
socketRef.current = io.connect("http://localhost:8000");
navigator.mediaDevices
.getUserMedia({ video: videoConstraints, audio: true })
.then((stream) => {
userVideo.current.srcObject = stream;
socketRef.current.emit("join room", roomID);
socketRef.current.on("all users", (users) => {
const peers = [];
users.forEach((userID) => {
const peer = createPeer(userID, socketRef.current.id, stream);
peersRef.current.push({
peerID: userID,
peer,
});
peers.push(peer);
});
setPeers(peers);
});
socketRef.current.on("user joined", (payload) => {
const peer = addPeer(payload.signal, payload.callerID, stream);
peersRef.current.push({
peerID: payload.callerID,
peer,
});
setPeers((users) => [...users, peer]);
});
socketRef.current.on("receiving returned signal", (payload) => {
const item = peersRef.current.find((p) => p.peerID === payload.id);
item.peer.signal(payload.signal);
});
});
});
function createPeer(userToSignal, callerID, stream) {
const peer = new Peer({
initiator: true,
trickle: false,
stream,
});
peer.on("signal", (signal) => {
socketRef.current.emit("sending signal", {
userToSignal,
callerID,
signal,
});
});
return peer;
}
function addPeer(incomingSignal, callerID, stream) {
const peer = new Peer({
initiator: false,
trickle: false,
stream,
});
peer.on("signal", (signal) => {
socketRef.current.emit("returning signal", { signal, callerID });
});
peer.signal(incomingSignal);
return peer;
}
return (
<Container>
<StyledVideo muted ref={userVideo} autoPlay playsInline />
{peers.map((peer, index) => {
return <Video key={index} peer={peer} />;
})}
</Container>
);
};
export default Room;
Now for server end, this is my server code:
require('dotenv').config();
const express = require("express");
const http = require("http");
const app = express();
const cors = require("cors");
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server);
const users = {};
const socketToRoom = {};
app.use(cors);
io.on('connection', socket => {
socket.on("join room", roomID => {
if (users[roomID]) {
const length = users[roomID].length;
if (length === 4) {
socket.emit("room full");
return;
}
users[roomID].push(socket.id);
} else {
users[roomID] = [socket.id];
}
socketToRoom[socket.id] = roomID;
const usersInThisRoom = users[roomID].filter(id => id !== socket.id);
socket.emit("all users", usersInThisRoom);
});
socket.on("sending signal", payload => {
io.to(payload.userToSignal).emit('user joined', { signal: payload.signal, callerID: payload.callerID });
});
socket.on("returning signal", payload => {
io.to(payload.callerID).emit('receiving returned signal', { signal: payload.signal, id: socket.id });
});
socket.on('disconnect', () => {
const roomID = socketToRoom[socket.id];
let room = users[roomID];
if (room) {
room = room.filter(id => id !== socket.id);
users[roomID] = room;
}
});
});
server.listen(process.env.PORT || 8000, () => console.log('server is running on port 8000'));
This is the link for youtube tutorial:
https://www.youtube.com/watch?v=R1sfHPwEH7A&t=154s
Here's the Room.js Code on the Client Side of the Application!.
I am maintaining two arrays one is peersRef and another is peers and all the videos are rendered from peers. While onClick button is called in destroying the peer but i guess some problem is there.
import React, { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
import styled from "styled-components";
const Container = styled.div`
padding: 20px;
display: flex;
height: 100vh;
width: 90%;
margin: auto;
flex-wrap: wrap;
`;
const StyledVideo = styled.video`
height: 40%;
width: 50%;
`;
const Video = (props) => {
const ref = useRef();
useEffect(() => {
props.peer.on("stream", stream => {
// console.log();
ref.current.srcObject = stream;
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<StyledVideo playsInline autoPlay ref={ref} />
);
}
const videoConstraints = {
height: window.innerHeight / 2,
width: window.innerWidth / 2
};
const Room = (props) => {
const{email} = props;
console.log(email);
const [peers, setPeers] = useState([]);
const [audiomute,setAudioMute] = useState(true);
const [videomute,setVideoMute]= useState(true);
const [myID,setMyID] = useState('');
const socketRef = useRef();
const userVideo = useRef();
const peersRef = useRef([]);
const roomID = props.match.params.roomID;
useEffect(() => {
socketRef.current = io.connect("/");
navigator.mediaDevices.getUserMedia({ video: videoConstraints, audio: false }).then(stream => {
userVideo.current.srcObject = stream;
socketRef.current.emit("join room", roomID);
socketRef.current.on("all users", users => {
const peers = [];
users.forEach(userID => {
const peer = createPeer(userID, socketRef.current.id, stream);
peersRef.current.push({
peerID: userID,
peer,
})
peers.push(peer);
})
setPeers(peers);
})
socketRef.current.on("user joined", payload => {
const peer = addPeer(payload.signal, payload.callerID, stream);
peersRef.current.push({
peerID: payload.callerID,
peer,
})
setPeers(users => [...users, peer]);
});
socketRef.current.on("receiving returned signal", payload => {
const item = peersRef.current.find(p => p.peerID === payload.id);
item.peer.signal(payload.signal);
});
socketRef.current.on('user-disconnected',users=>{
console.log("Event is called");
peers = users;
})
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function createPeer(userToSignal, callerID, stream) {
const peer = new Peer({
initiator: true,
trickle: false,
stream,
});
setMyID(userToSignal);
peer.on("signal", signal => {
socketRef.current.emit("sending signal", { userToSignal, callerID, signal })
})
// console.log(peer);
console.log(peersRef.current);
return peer;
}
function addPeer(incomingSignal, callerID, stream) {
const peer = new Peer({
initiator: false,
trickle: false,
stream,
})
peer.on("signal", signal => {
socketRef.current.emit("returning signal", { signal, callerID })
})
peer.signal(incomingSignal);
console.log(peersRef.current);
return peer;
}
console.log(peers);
const leavecall = ()=>{
for(let i =0;i<peersRef.length;i++){
if(peersRef.current[i].peerID===myID){
peersRef.current[i].peer.destroy();
peersRef.current.splice(i,1);
peers[i].destroy();
peers.splice(i,1);
break;
}
}
console.log(peers);
window.location = "http://localhost:3000/";
}
const muteAudio=(e)=>{
e.preventDefault();
console.log("Called")
if(audiomute){
navigator.mediaDevices.getUserMedia({ video: videoConstraints, audio: true });
}
else{
navigator.mediaDevices.getUserMedia({ video: videoConstraints, audio: false })
}
setAudioMute(!audiomute);
}
// const MuteVideo=(e)=>{
// e.preventDefault();
// setVideoMute(!videomute);
// navigator.mediaDevices.getUserMedia({ video: variable, audio: false })
// }
return (
<Container>
<StyledVideo muted ref={userVideo} autoPlay playsInline />
{peers.map((peer, index) => {
return (
<>
<Video key={index++} peer={peer} />
</>
);
})}
<button className="btn btn-danger" onClick={leavecall}>Hangup</button>
</Container>
);
};
export default Room;
Here is the Server side code!
require('dotenv').config();
const express = require("express");
const http = require("http");
const app = express();
const server = http.createServer(app);
const socket = require("socket.io");
const cors = require("cors");
const io = socket(server);
app.use(cors());
const users = {};
const socketToRoom = {};
io.on('connection', socket => {
socket.on("join room", roomID => {
if (users[roomID]) {
const length = users[roomID].length;
if (length === 4) {
socket.emit("room full");
return;
}
users[roomID].push(socket.id);
} else {
users[roomID] = [socket.id];
}
socketToRoom[socket.id] = roomID;
const usersInThisRoom = users[roomID].filter(id => id !== socket.id);
socket.emit("all users", usersInThisRoom);
});
socket.on("sending signal", payload => {
io.to(payload.userToSignal).emit('user joined', { signal: payload.signal, callerID: payload.callerID });
});
socket.on("returning signal", payload => {
io.to(payload.callerID).emit('receiving returned signal', { signal: payload.signal, id: socket.id });
});
socket.on('disconnect', () => {
const roomID = socketToRoom[socket.id];
let room = users[roomID];
console.log(users);
if (room) {
room = room.filter(id => id !== socket.id);
users[roomID] = room;
}
io.to(roomID).broadcast.emit('user-disconnected',users);
console.log(users);
});
});
server.listen(process.env.PORT || 8000, () => console.log('server is running on port 8000'));
Here's what is seen when i disconnect from other tab the video element should completely gone but this isn't happening
Your help is much appreciated.
When you terminate the connection the last frame of the video can still render, just delete the video element or just assign an empty new stream to it
I am trying to make a React web chat platform using webRTC, socket.io-client, and simple-peer. I followed a tutorial and used their code but I keep getting this error.
The error happens every second or so and I'm not sure why.
Here is my code for the component (Room.js) that I am importing in my App.js:
import React, { useEffect, useState, useRef } from 'react';
import '../App.css';
import io from "socket.io-client";
import Peer from "simple-peer";
import styled from "styled-components";
const Container = styled.div`
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
`;
const Row = styled.div`
display: flex;
width: 100%;
`;
const Video = styled.video`
border: 1px solid blue;
width: 50%;
height: 50%;
`;
function Room() {
const [yourID, setYourID] = useState("");
const [users, setUsers] = useState({});
const [stream, setStream] = useState();
const [receivingCall, setReceivingCall] = useState(false);
const [caller, setCaller] = useState("");
const [callerSignal, setCallerSignal] = useState();
const [callAccepted, setCallAccepted] = useState(false);
const userVideo = useRef();
const partnerVideo = useRef();
const socket = useRef();
useEffect(() => {
socket.current = io.connect("/");
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(stream => {
setStream(stream);
if (userVideo.current) {
userVideo.current.srcObject = stream;
}
})
socket.current.on("yourID", (id) => {
setYourID(id);
})
socket.current.on("allUsers", (users) => {
setUsers(users);
})
socket.current.on("hey", (data) => {
setReceivingCall(true);
setCaller(data.from);
setCallerSignal(data.signal);
})
}, []);
function callPeer(id) {
const peer = new Peer({
initiator: true,
trickle: false,
config: {
iceServers: [
{
urls: "stun:numb.viagenie.ca",
username: "sultan1640#gmail.com",
credential: "98376683"
},
{
urls: "turn:numb.viagenie.ca",
username: "sultan1640#gmail.com",
credential: "98376683"
}
]
},
stream: stream,
});
peer.on("signal", data => {
socket.current.emit("callUser", { userToCall: id, signalData: data, from: yourID })
})
peer.on("stream", stream => {
if (partnerVideo.current) {
partnerVideo.current.srcObject = stream;
}
});
socket.current.on("callAccepted", signal => {
setCallAccepted(true);
peer.signal(signal);
})
}
function acceptCall() {
setCallAccepted(true);
const peer = new Peer({
initiator: false,
trickle: false,
stream: stream,
});
peer.on("signal", data => {
socket.current.emit("acceptCall", { signal: data, to: caller })
})
peer.on("stream", stream => {
partnerVideo.current.srcObject = stream;
});
peer.signal(callerSignal);
}
let UserVideo;
if (stream) {
UserVideo = (
<Video playsInline muted ref={userVideo} autoPlay />
);
}
let PartnerVideo;
if (callAccepted) {
PartnerVideo = (
<Video playsInline ref={partnerVideo} autoPlay />
);
}
let incomingCall;
if (receivingCall) {
incomingCall = (
<div>
<h1>{caller} is calling you</h1>
<button onClick={acceptCall}>Accept</button>
</div>
)
}
return (
<Container>
<Row>
{UserVideo}
{PartnerVideo}
</Row>
<Row>
{Object.keys(users).map(key => {
if (key === yourID) {
return null;
}
return (
<button onClick={() => callPeer(key)}>Call {key}</button>
);
})}
</Row>
<Row>
{incomingCall}
</Row>
</Container>
);
}
export default Room;
Here is my App.js file:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import './App.css';
import { ApolloProvider } from '#apollo/react-hooks';
import ApolloClient from 'apollo-boost';
import coverImg from './assets/videochat.jpg';
//components
import Room from './pages/Room';
import Cover from './components/Cover'
const client = new ApolloClient({
request: operation => {
const token = localStorage.getItem('User_token');
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
},
uri: '/graphql'
});
function App() {
return (
<ApolloProvider client={client}>
<Router>
<>
<Navbar />
<Switch>
<Route exact path="/" component={Cover}/>
<Route path='/create-room' component={Room} />
</Switch>
</>
</Router>
</ApolloProvider>
);
}
export default App;
And then here is my server.js file:
// 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('/create-room', (req, res) => {
// console.log("does this work")
// res.redirect(`/${uuidV4()}`)
// })
// app.get('/:room', (req, res) => {
// res.render('../client/src/pages/room', { roomId: req.params.room })
// })
// io.on('connection', socket => {
// socket.on('join-room', (roomId, userId) => {
// if (!person[socket.id]) {
// person[socket.id] = socket.id;
// }
// socket.emit("yourID", socket.id);
// io.sockets.emit("allUsers", person);
// socket.on("disconnect", () => {
// delete person[socket.id];
// });
// socket.on("callUser", (data) => {
// io.to(data.userToCall).emit("hey", {
// signal: data.signalData,
// from: data.from,
// });
// });
// socket.on("acceptCall", (data) => {
// io.to(data.to).emit("callAccepted", data.signal);
// });
// 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)
const express = require("express");
const http = require("http");
const app = express();
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server);
const users = {};
io.on('connection', socket => {
if (!users[socket.id]) {
users[socket.id] = socket.id;
}
socket.emit("yourID", socket.id);
io.sockets.emit("allUsers", users);
socket.on('disconnect', () => {
delete users[socket.id];
})
socket.on("callUser", (data) => {
io.to(data.userToCall).emit('hey', {signal: data.signalData, from: data.from});
})
socket.on("acceptCall", (data) => {
io.to(data.to).emit('callAccepted', data.signal);
})
});
// For some reason the console.log is not printed either
server.listen(3000, () => console.log('server is running on port 3000'));
This is my project file structure:
project name
.env
package.json
package-lock.json
.gitignore
server
server.js
client
public
react-images
src
pages
Room.js
App.js
index.js
App.css
App.test.js
serviceWorker.js
setupTests.js
.gitIgnore
package.json
package-lock.js
README.md
I was able to figure out my error and thought to post it here for any future people.
In my server.js file I needed it to run on a different port so that my actual client side code use the server on a different port. To do this you just have to change this:
server.listen(3000, () => console.log('server is running on port 3000'));
to this:
server.listen(8000, () => console.log('server is running on port 8000'));
Changing that line means that your server will run on http://localhost:8000. Another one of my errors was that my client side code was not linked to the server, which means that all of the code I wrote in server.js was irrelevant to the client side code.
To change this you have to add a line to the package.json file in the client folder. Instead of having this:
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"#apollo/react-hooks": "^4.0.0",
change it to this:
{
"name": "client",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:8000",
"dependencies": {
"#apollo/react-hooks": "^4.0.0",
Now your code should work! Good Luck!!!
hey guys am trying to make a wrtc video conference of one to many, using socket.io nodejs react and peerjs, well so far its working but all the users are seeing each other, i want all the users to see only the person who started the connection but the person who started the connection can see everyone please help i don't know how to fix this.
for now for each video stream it creats a video element and set the srcObt to the stream but i want the users connected to get only the stream of the user who started and the user who started the connection can get all the streams of the users connected.
myNode/socket code
io.on('connection',socket =>{
socket.on('join-class',(classId,palsid)=>{
socket.join(classId)
socket.to(classId).broadcast.emit('user-connected',palsid)
socket.on('disconnect',()=>{
socket.to(classId).broadcast.emit('user-disconnect',palsid)
})
})
})
server.listen(4000, ()=>{
console.log('server is running on port 4000')
})
my frontend code using react
import React from 'react'
import io from 'socket.io-client'
import Peer from 'peerjs'
import './ClassWall.css'
import { Modal, Button } from 'antd';
import img from '../../uploads/349-3498013_laptop-personal-computer-diagram-computer-icons-download-laptop-clipart.png'
const socket = io.connect('http://localhost:4000/')
class LiveClass extends React.Component{
constructor(){
super()
this.state = {
userId :'',
classStatus: '',
videoSrc:'',
visible: false
}
}
async componentDidMount(){
//set the user id of logedin user
const videoGrid = document.getElementById('video-grid')
const Myvideo = document.createElement('video')
Myvideo.addEventListener('click',()=>{
console.log(Myvideo)
})
Myvideo.muted = true
try {
const response = await fetch('http://localhost:4000/Auth//UserID/id',{
headers:{token:localStorage.token}
})
const Parse = await response.json()
this.setState({userId:Parse})
} catch (error) {
}
//get user id to connect through peer
const myPeer = new Peer(this.state.userId,{
host: '/',
port:4001
})
//connect and share video stream
try {
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)
console.log('newUser',userId)
})
})
socket.on('user-disconnect', userId=>{
if(peers[userId])peers[userId].close()
})
myPeer.on('open',id=>{
socket.emit('join-class',this.props.match.params.id,id)
})
//get user stream and connect
function connectToNewUser(userId,stream){
const call = myPeer.call(userId,stream)
const video = document.createElement('video')
video.setAttribute("class",`pointer`)
video.addEventListener('click',()=>{
console.log(video)
})
call.on('stream',userVideostream=>{
addVideoStream(video,userVideostream)
})
call.on('close',()=>{
video.remove()
})
peers[userId]=call
}
//Add video stream
function addVideoStream(video,stream){
const videoGrid = document.getElementById('video-grid')
video.srcObject = stream
video.addEventListener('loadedmetadata', () =>{
video.play()
})
videoGrid.append(video)
}
} catch (error) {
console.log('error',error)
}
this.checkIfClassTrue()
}
//check if its class
checkIfClassTrue = async()=>{
const response = await fetch(`http://localhost:4000/liveclass/${this.props.match.params.id}`)
const Parse = await response.json()
this.setState({classStatus:Parse})
}
//modal
showModal = () => {
this.setState({
visible: true,
});
};
handleOk = e => {
console.log(e);
this.setState({
visible: false,
});
};
handleCancel = e => {
console.log(e);
this.setState({
visible: false,
});
};
render(){
return(
<div>
{
this.state.classStatus === 'Not found'?
'not found':
<div>
<div id="video-grid">
{/* <img src={img}/> */}
</div>
<Button type="primary" onClick={this.showModal}>
Open Modal
</Button>
<Modal
title="Basic Modal"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
</Modal>
</div>
}
</div>
)
}
}
export default LiveClass