Whenever I am clicking on Call button, apparently call is accepted, I am getting my video but then I am getting this error
Failed to set remote answer sdp: Called in wrong state: stable
It shows the error in this function:
async setLocalDescription(ans) {
if (this.peer) {
await this.peer.setRemoteDescription(new RTCSessionDescription(ans));
}
}
in peer.js
I looked up the internet, I am setting my LocalDescription before Remote Description, so that is fine.
MDN docs says
This may mean that the RTCPeerConnection object is new, in which case both the localDescription and remoteDescription are null; it may also mean that negotiation is complete and a connection has been established.
How do I resolve it?
This is my code in React
import React, { useCallback, useEffect, useState } from "react";
import { useSocket } from "../context/SocketProvider";
import ReactPlayer from "react-player";
import peer from "../service/peer";
export default function Room() {
const socket = useSocket();
const [remoteSocketId, setRemoteSocketId] = useState(null);
const [myStream, setMyStream] = useState();
const [remoteStream, setRemoteStream] = useState(null);
const joinUser = useCallback((data) => {
const { email, id } = data;
setRemoteSocketId(id);
console.log(`Email ${email} joined the room`, remoteSocketId);
}, []);
const handleCallUser = useCallback(async () => {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
const offer = await peer.getOffer();
//console.log("Offer is", offer);
socket.emit("user:call", { to: remoteSocketId, offer: offer });
setMyStream(stream);
//stream.getTracks().forEach(track => peer.addTrack(track, stream));
}, [remoteSocketId, socket]);
const handleIncomingCall = useCallback(
async ({ from, offer }) => {
setRemoteSocketId(from);
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
setMyStream(stream);
console.log(`Incoming Call`, from, offer);
//await peer.setLocalDescription(offer);
const ans = await peer.getAnswer(offer);
socket.emit("call:accepted", { to: from, ans });
},
[socket]
);
const handleCallAccepted = useCallback(
({ from, ans }) => {
peer.setLocalDescription(ans);
console.log("Call Accepted!");
},
[ ]
);
useEffect(() => {
socket.on("user_joined", joinUser);
socket.on("incoming:call", handleIncomingCall);
socket.on("call:accepted", handleCallAccepted);
return () => {
socket.off("user_joined");
socket.off("incoming:call", handleIncomingCall);
socket.off("call:accepted", handleCallAccepted);
};
}, [
socket,
joinUser,
handleIncomingCall,
handleCallAccepted,
]);
return (
<div>
<h1> This is the Room</h1>
<h2>{remoteSocketId ? "Connected" : "Not"}</h2>
{/* {myStream && <button onClick={sendStreams}>Send Stream</button>} */}
{remoteSocketId && <button onClick={handleCallUser}>CALL</button>}
{myStream && (
<>
<h1>My Stream</h1>
<ReactPlayer
playing
muted
height="100px"
width="200px"
url={myStream}
/>
</>
)}
</div>
);
}
This is my peer.js which is using all the WebRTC APIs
class PeerService {
constructor() {
if (!this.peer) {
this.peer = new RTCPeerConnection({
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:global.stun.twilio.com:3478",
],
},
],
});
}
}
async getAnswer(offer) {
if (this.peer) {
await this.peer.setRemoteDescription(offer);
const ans = await this.peer.createAnswer();
await this.peer.setLocalDescription(new RTCSessionDescription(ans));
return ans;
}
}
async setLocalDescription(ans) {
if (this.peer) {
await this.peer.setRemoteDescription(new RTCSessionDescription(ans));
}
}
async getOffer() {
if (this.peer) {
const offer = await this.peer.createOffer();
await this.peer.setLocalDescription(new RTCSessionDescription(offer));
//console.log("Offer 2 is", offer);
return offer;
}
}
}
export default new PeerService();
This is what my webRTC internals says
http://localhost:3000/, { iceServers: [stun:stun.l.google.com:19302, stun:global.stun.twilio.com:3478], iceTransportPolicy: all, bundlePolicy: balanced, rtcpMuxPolicy: require, iceCandidatePoolSize: 0 }
ICE connection state: new
Connection state: new
Signaling state: new
ICE Candidate pair: (not connected)
ICE candidate grid
Stats Tables
Filter statistics by type including
separate multiple values by `,`
media-playout (kind=audio, id=AP)
peer-connection (id=P)
Filter statistics graphs by type including
separate multiple values by `,`
Stats graphs for media-playout (kind=audio, id=AP)
Stats graphs for peer-connection (id=P)