Reputation: 23
I've recently been exploring WebRTC using React.js and the simple-peer library.
Intro: My goal is to make a basic group video chat application and I started with making a regular socket.io server that would transfer messages from one user in a group to the others without WebRTC and then took it another step by implementing the server.js purely as a signaling server and allowed connections for all the users through WebRTC once signals had been sent and established. This was successfully accomplished and was able to handle 4 unique visitors.
The Problem:
My issue now is attempting to include Video / Audio using WebRTC. At first I started with the same template from my chat room WebRTC example and pulled in the navigator
to get user media and pass that to the other peers but anytime I attempt to join the same room (copy url to another browser tab) I receive this error Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: stable
. I'm pretty lost as to what I could be doing wrong and anything online says that I'm not handling my peer connections properly but can't seem to figure it out.
Code: server.js
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 io = socket(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
allowedHeaders: ["my-custom-header"],
credentials: true
}
});
const rooms = {};
io.on("connection", socket => {
socket.on("join room", roomID => {
console.log('\n## user joining room ##')
if (rooms[roomID]) {
if (rooms[roomID].length === 4) {
socket.emit("room full");
return;
}
if (!rooms[roomID].includes(socket.id)) {
rooms[roomID].push(socket.id);
}
} else {
rooms[roomID] = [socket.id];
}
socket.join(roomID);
const otherUsers = rooms[roomID].filter(id => id !== socket.id);
socket.emit("all users", otherUsers);
console.log('current rooms: ', rooms)
});
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("send message", message => {
console.log('## sending message ##');
io.to(message.roomID).emit("receive message", message);
});
socket.on("disconnect", () => {
for (const roomID in rooms) {
rooms[roomID] = rooms[roomID].filter(id => id !== socket.id);
if (rooms[roomID].length === 0) {
delete rooms[roomID];
}
}
});
});
server.listen(process.env.PORT || 8000, () => console.log('server is running on port 8000'));
Room-Simple-Peer-Video.js
import React, { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
import { useParams } from "react-router-dom";
const Room = () => {
const { roomID } = useParams();
const [peers, setPeers] = useState([]);
const socketRef = useRef();
const userVideoRef = useRef();
const peersRef = useRef([]);
useEffect(() => {
socketRef.current = io.connect("http://localhost:8000");
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
userVideoRef.current.srcObject = stream;
socketRef.current.emit("join room", roomID);
socketRef.current.on("all users", users => {
const peers = [];
users.forEach(userID => {
if (userID !== socketRef.current.id) {
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);
if (item) {
item.peer.signal(payload.signal);
}
});
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
return () => {
socketRef.current.disconnect();
};
}, [roomID]);
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 (
<div>
<h1>Video Chat Room</h1>
<div>
<video muted ref={userVideoRef} autoPlay playsInline />
{console.log('peers: ', peers)}
{peers.map((peer, index) => {
return (
<video key={index} playsInline autoPlay />
);
})}
</div>
</div>
);
};
export default Room;
If you're curious about my implementation of everything including my other examples I made a public GitHub repository which is linked below containing the minimal code I have with all 3 examples I explained. Any help would be appreciated and I can answer any questions if anyone has any. Thanks again!
As I mentioned in the details of the problem before, I've tried multiple solutions for building up into using WebRTC with video/audio (chat room) and I even tried different ways of handling the Peer mappings.
Upvotes: 0
Views: 129