Biswas Prasana Swain
Biswas Prasana Swain

Reputation: 23

I’ve encountered an issue where each remote user’s video stream is being displayed twice in the room

I’m building a peer-to-peer video chat application using React, PeerJS, and Socket.IO. I’ve encountered an issue where each remote user’s video stream is being displayed twice in the room. I believe the problem lies in the way call.on('stream', () => {}) works.

Here’s the relevant code from my VideoChat component:

// client\src\components\Room.js
import React, { useEffect, useState } from 'react';
import { useSocket } from '../context/SocketProvider';
import { useParams } from 'react-router-dom';
import Peer from 'peerjs';

const VideoChat = () => {
  const [streams, setStreams] = useState([]);
  const [peers, setPeers] = useState([]);

  const { roomId } = useParams();
  const socket = useSocket();

  function connectToNewUser(myPeer, peerId, stream) {
    const call = myPeer.call(peerId, stream);
    call.on('stream', userVideoStream => {
      console.log("userVideoStream", userVideoStream); // This runs twice, I know
      // I want to make sure, for each remote user only once the state gets updated.
      setStreams(prevStreams => [...prevStreams, userVideoStream]);
      setPeers(prevPeers => [...prevPeers, userVideoStream.peerId]);
    });
  }

  useEffect(() => {
    const myPeer = new Peer();
    navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    }).then(stream => {
      setStreams(prevStreams => [...prevStreams, stream]);

      myPeer.on('call', call => {
        call.answer(stream);
        call.on('stream', userVideoStream => {
          console.log("userVideoStream", userVideoStream); // This is also obviously runs twice
          // Same here, I want to make sure, for each remote user only once the state gets updated.
          setStreams(prevStreams => [...prevStreams, userVideoStream]);
          setPeers(prevPeers => [...prevPeers, userVideoStream.peerId]);
        });
      });

      socket.on('user-connected', peerId => {
        connectToNewUser(myPeer, peerId, stream);
      });
    });

    socket.on('user-disconnected', peerId => {
      setPeers(prevPeers => prevPeers.filter(peer => peer !== peerId));
      setStreams(prevStreams => prevStreams.filter(stream => stream.peerId !== peerId));
    });

    myPeer.on('open', peerId => {
      socket.emit('join-room', roomId, peerId);
    });

    return () => {
      myPeer.off('open');
      myPeer.off('call');
      socket.off('user-connected');
      socket.off('join-room');
      socket.off('user-disconnected');
      socket.disconnect();
      myPeer.destroy();
    };
  }, []);

  return (
    <div>
      {streams.map((stream, index) => (
        stream.active && <video key={index} autoPlay playsInline ref={video => {
          if (video) video.srcObject = stream;
        }} />
      ))}
    </div>
  );
};

export default VideoChat;

And here’s the relevant code from my server: Everything is perfectly fine here, it's just to understand how the server is serving frontend.

// server\server.js
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server, {
    cors: true,
})

io.on('connection', socket => {
    socket.on('join-room', (roomId, peerId) => {
        socket.join(roomId)
        socket.broadcast.to(roomId).emit('user-connected', peerId)

        socket.on('disconnect', () => {
            socket.broadcast.to(roomId).emit('user-disconnected', peerId)
        })
    })
})

server.listen(5000, () => console.log('server is running on port 5000'))

The issue occurs in the connectToNewUser function and the myPeer.on('call', userVideoStream => {...}) event handler inside the useEffect hook. In both places, I’m updating the streams state to display the streams of remote users in the room. However, for each remote user, their stream is being displayed twice.

How can I ensure that each remote user’s stream is only displayed once?

I initially thought that each stream would only be added once to the streams state when a new user connects or when a call is received. I expected that each remote user's stream would be displayed only once in the room. However, each stream is being added twice, causing each remote user's video to be displayed twice.

To debug this, I tried logging the stream and peerId values each time they were added to their respective states. I noticed that each stream and peerId was indeed being logged twice, confirming that they were being added to the states twice.

I'm not sure what else to try at this point, which is why I'm seeking help here.

Upvotes: 0

Views: 42

Answers (0)

Related Questions