1

I'm trying to create a video chat with react.js and there is an issue that I don't know how to solve. I need to save peer stream from peer.on('stream',...) when it appears, but as it appears at the second render and I set empty array as dependency for useEffect, it will not work. I need to set the stream at the state or ref.

import { useEffect, useRef, useState } from "react";
import s from "./callPage.module.css";
import { getChat } from "../../apis/chatApis";
import { IChat } from "../../types";
import { useLocation, useParams } from "react-router-dom";
import { useStore, userMediaStream } from "../../store/store";
import { socket } from "../../socket";
import Peer from 'simple-peer/simplepeer.min.js';

const CallPage = () => {
  const location = useLocation()
  const [stream, setStream] = useStore((state) => [state.stream, state.setStream])
  const [usersStream, setUsersStream] = useStore((state) => [state.usersStream, state.setUsersStream])
  const [state, setState] = useState<IChat | null>(null);
  const [caller, setCaller] = useState<{ name: string, id: string, roomId: string } | null>(null)
  const [callerSignal, setCallerSignal] = useState<any>(null)
  const myVideo = useRef<HTMLVideoElement | null>(null)
  const userVideo = useRef<HTMLVideoElement | null>(null)
  const { id } = useParams();

  useEffect(() => {
    (async () => {
      socket.emit('join', [id]);
      if (location.search.split('=')[1] === 'answer') {
        socket.emit('acceptPeerConnection', { fullname: localStorage.fullname, acceptorId: localStorage._id, accept: true, roomId: id })

        const peer = new Peer({
          initiator: false,
          trickle: false,
          stream: stream
        })
        socket.on('recivePeerSignal', (callData) => {
          console.log(callData, 'recivePeerSignal');

          peer.signal(callData.signalData)
        })
        peer.on("signal", (signalData: any) => {
          socket.emit('sendingPeerSignal', {
            roomId: id,
            signalData,
            from: { name: localStorage.fullname, id: localStorage._id }
          })
        })

        peer.on('stream', (remoteStream: MediaStream) => {
          console.log(remoteStream, 'remoteStream');
          setUsersStream(remoteStream)
          if (userVideo.current) {
            userVideo.current.srcObject = remoteStream;
          }
        });
      }

      if (location.search.split('=')[1] === 'call') {
        const peer = new Peer({
          initiator: true,
          trickle: false,
          stream: stream
        })
        socket.on('acceptedPeerConnection', ({ fullname, acceptorId, accept, roomId }) => {

          peer.on('signal', (signalData: any) => {
            socket.emit('sendingPeerSignal', {
              roomId: id,
              signalData,
              from: { name: localStorage.fullname, id: localStorage._id }
            })
          })
  
          socket.on('recivePeerSignal', (callData) => {
            console.log(callData, 'recivePeerSignal');
            peer.signal(callData.signalData)
          })
  
          peer.on('stream', (remoteStream: MediaStream) => {
            console.log(remoteStream, 'remoteStream');
  
            if (userVideo.current) {
              userVideo.current.srcObject = remoteStream;
            }
          });
        })
      }

      const data = await getChat(id as string);
      setState(data);

      navigator.mediaDevices.enumerateDevices().then((devices) => {
        const videoDevices = devices.filter(
          (device) => device.kind === "videoinput"
        );
        const audioDevices = devices.filter(
          (device) => device.kind === "audioinput"
        );
        return navigator.mediaDevices
          .getUserMedia({
            video: videoDevices.length > 0,
            audio: audioDevices.length > 0,
          })
          .then((stream: MediaStream) => {
            setStream(stream);
            if (myVideo.current && myVideo.current.srcObject !== stream) {
              myVideo.current.srcObject = stream;
            }
            if (sessionStorage.signalData) {
              const data = JSON.parse(sessionStorage.signalData)
              setCaller({ name: data.name, id: data.id, roomId: data.roomId })
              setCallerSignal(data.signalData)
            }
          });
      });
    })();
  }, []);
  console.log(usersStream);

  const callUser = () => {
    socket.emit('callUser', {
      roomId: id,
      from: { name: localStorage.fullname, id: localStorage._id }
    })
  }

  return (
    <div className={s.call}>
      {myVideo && <video playsInline muted ref={myVideo} autoPlay style={{ width: "300px" }} />}
      {userVideo.current && <video playsInline ref={userVideo} autoPlay style={{ width: "300px" }} />}
      <div className={s.callActions}>
        <button onClick={callUser}>call</button>
      </div>
    </div>
  );
};

export default CallPage;

and also my server code (nestjs), if someone needs

import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  MessageBody,
  OnGatewayConnection,
  OnGatewayDisconnect,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
  cors: {
    origin: '*',
  },
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;

  users: { [key: string]: string } = {};

  handleConnection(@MessageBody() message: any, client: Socket) {
    // Handle client connection
    // console.log('Client connected: ' + client.id,message);
  }

  handleDisconnect(client: Socket) {
    // Handle client disconnection
    console.log('Client disconnected: ' + client.id);
    delete this.users[client.id];
    this.server.emit('users', Object.values(this.users));
  }

  @SubscribeMessage('chat')
  handleMessage(
    @MessageBody() message: any,
    @ConnectedSocket() client: Socket,
  ) {
    console.log(message);

    this.server.to(message.chat._id).emit('chat', message);
  }

  @SubscribeMessage('join')
  handleJoin(
    @MessageBody() roomId: string[],
    @ConnectedSocket() client: Socket,
  ) {
    client.join(roomId);
  }

  @SubscribeMessage('isTyping')
  isTyping(@MessageBody() userData) {
    this.server.to(userData.roomId).emit('isTyping', userData);
  }

  @SubscribeMessage('callUser')
  handleCallUser(
    @MessageBody() callData: any,
    @ConnectedSocket() client: Socket,
  ) {
    console.log(callData, '=============');
    client.broadcast.to(callData.roomId).emit('reciveCall', callData);
  }

  @SubscribeMessage('answerCall')
  handleAnswerCall(
    @MessageBody()
    callData: {
      // signal: { type: string; sdp: string };
      to: {
        name: string;
        id: string;
        roomId: string;
      };
    },
    @ConnectedSocket() client: Socket,
  ) {
    console.log(callData, '=============callAccepted');
    client.broadcast
      .to(callData.to.roomId)
      .emit('callAccepted', callData);
  }


  @SubscribeMessage('acceptPeerConnection')
  handleAcceptPeerConnection(
    @MessageBody()
    acceptorData: {
        fullname: string;
        acceptorId: string;
        accept: boolean;
        roomId:string
      },
    @ConnectedSocket() client: Socket,
  ) {
    console.log(acceptorData, '=============acceptedPeerConnection');
    client.broadcast
      .to(acceptorData.roomId)
      .emit('acceptedPeerConnection', acceptorData);
  }


  @SubscribeMessage('sendingPeerSignal')
  handleSendPeerSignal(
    @MessageBody()
    callData: {
      roomId:string,
      signal: { type: string; sdp: string };
      from: {
        name: string;
        id: string;
      };
    },
    @ConnectedSocket() client: Socket,
  ) {
    console.log(callData, '=============callAccepted');
    client.broadcast
      .to(callData.roomId)
      .emit('recivePeerSignal', callData);
  }
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Vahan
  • 151
  • 1
  • 1
  • 5

0 Answers0