Signaling Failure: 'Cannot Signal After Peer Is Destroyed' Issue in WebRTC Video Call Website Development

78 views Asked by At

Pardon me for the beginner's question. I want to create a website for group video calls using webRTC, Peer, and Next.js. I use a signaling server using Node.js to be able to connect the peer. But for some reason, when I run it, I always get the error cannot signal after the peer is destroyed.

Any tip? Thanks

This is the file handle for my room where users can join.

'use client'
import React, { useEffect, useRef, useState } from "react";
import { useRouter } from 'next/navigation'; // Fix typo in import statement
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 => {
            ref.current.srcObject = stream;
        });
    }, [props.peer]);

    return (
        <StyledVideo playsInline autoPlay ref={ref} />
    );
}

const Room = () => {
    const router = useRouter();
    const [peers, setPeers] = useState([]);
    const socketRef = useRef();
    const userVideo = useRef();
    const peersRef = useRef([]);
    const id  = router.query; // Use router.query to get the room ID
    const roomID = 123
    useEffect(() => {
        console.log(id)
        const videoConstraints = {
            height: window.innerHeight / 4,
            width: window.innerWidth / 4
        };

        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 => {
                    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);
                }
            });
        });
    }, [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 className="bg-gray-100 min-h-screen flex flex-col">
            <h1 className="text-2xl font-semibold p-4">Video Chat Room</h1>
            <div className="video-container p-4 flex-1">
                {userVideo && <StyledVideo muted ref={userVideo} autoPlay playsInline />}
                {peers.map((peer, index) => (
                    <Video key={index} peer={peer} />
                ))}
            </div>
            {/* Chat bar */}
            <div className="bg-white p-4 border-t border-gray-300">
                <div className="chat-messages"></div>
                <div className="flex mt-2">
                    <input
                        type="text"
                        placeholder="Type a message..."
                        className="p-2 border border-gray-300 rounded-lg flex-1"
                    />
                    <button
                        className="ml-2 bg-blue-500 text-white px-4 py-2 rounded-lg"
                    >
                        Send
                    </button>
                </div>
            </div>
            <div className="p-4">
                <button
                    className="bg-blue-500 text-white px-4 py-2 rounded-lg"
                >
                </button>
                <button
                    className="ml-2 bg-blue-500 text-white px-4 py-2 rounded-lg"
                >
                </button>
            </div>
        </div>
    );
};

export default Room;

Here is my signaling server

require('dotenv').config();
const express = require("express");
const http = require("http");
const app = express();
const cors = require("cors");
app.use(cors)
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server, {
    cors: {
      origin: "http://localhost:3000",
      methods: ["GET", "POST"],
    },
  });
  

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];
        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'));

I hope you can identify what my problem is in the code. I would appreciate your assistance in identifying and resolving the issue. Thank you.

1

There are 1 answers

0
Dhamith Kumara On

I think you should check PeerJS documentation as well. in that, they have mentioned under the destroy like this

This cannot be undone; the respective peer object will no longer be able to create or receive any connections, its ID will be forfeited on the (cloud) server, and all of its data and media connections will be closed.

for more information check this PeerJS