dizzleduff
dizzleduff

Reputation: 23

React-WebRTC getting "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: stable"

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!

WebRTC-React-Examples

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

Answers (0)

Related Questions