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);
}
}