Remote video not showing the stream - javascript

Im not sure what is wrong. The caller and callee both receive the same stream that is getting set in local video but it wont set the remote video.
I have seen it worked before when I clicked the call button twice but I changed the code and dont know how to recreate it.
Any help is appreciated. Thank you.
video.js
//Starts by calling the user
const callUser = (socketId, mySocket) => {
navigator.getUserMedia({ video: true, audio: true }, stream => {
console.log("My Stream: ", stream)
const localVideo = document.getElementById("local-video");
localVideo.srcObject = stream;
localStream = stream
createAndSendOffer(socketId, mySocket);
})
}
//Create Offer
const createAndSendOffer = async (socketId, mySocket) => {
peerConnection = new RTCPeerConnection()
peerConnection.ontrack = function(event) {
console.log("Remote Stream", event.streams[0])
const remoteVideo = document.getElementById("remoteVideo")
if(remoteVideo.srcObject !== event.stream[0]) {
remoteVideo.srcObject = event.streams[0];
}
}
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream))
await peerConnection.createOffer( offer => {
peerConnection.setLocalDescription(new RTCSessionDescription(offer),
function() {
mySocket.emit("callUser", {offer,to: socketId});
})
})
}
//Callee Is getting called
const answerCall = (data, mySocket, inCall) => {
navigator.getUserMedia({ video: true, audio: true }, stream => {
console.log("My Stream: ", stream)
const localVideo = document.getElementById("local-video");
localVideo.srcObject = stream;
localStream = stream
createAndSendAnswer(data, mySocket, inCall);
})
}
//Create Answer
const createAndSendAnswer = async (data, mySocket, inCall) => {
peerConnection = new RTCPeerConnection()
peerConnection.ontrack = function(event) {
console.log("Remote Stream", event.streams[0])
const remoteVideo = document.getElementById("remoteVideo")
if(remoteVideo.srcObject !== event.stream[0]) {
console.log(event.streams[0])
remoteVideo.srcObject = event.streams[0];
}
inCall(true)
}
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream))
await peerConnection.setRemoteDescription(new RTCSessionDescription(data.offer))
await peerConnection.createAnswer(answer => {
peerConnection.setLocalDescription(new RTCSessionDescription(answer),
function() {
mySocket.emit("makeAnswer", {answer, to: data.socket});
})
})
}
const startSockets = (socket, inCall) => {
socket = socket
socket.on("gettingCalled", data => {
console.log("You are getting called with socketId", data.socket)
answerCall(data, socket, inCall);
})
socket.on("answerMade", async data => {
await peerConnection.setRemoteDescription(new RTCSessionDescription(data.answer))
})
}
class Video extends Component {
constructor(props){
super(props);
this.state={
friends: [],
mySocket: [],
inCall: false
}
}
componentDidMount() {
if(this.props.user) {
startSockets(this.props.user.socket, this.inCallChange)
}
}
inCallChange = boolean => this.setState({inCall: boolean})
callUser = socketId => callUser(socketId, this.state.mySocket, this.state.inCall)
render() {
if(!this.props.user) {
return (<div>No User</div>)
} else {
return (
<div>`enter code here`
<div className="video-chat-container">
<div className="video-container">
<video
autoPlay
className="remote-video"
id="remoteVideo"
></video>
<video
autoPlay
muted
className="local-video"
id="local-video"
></video>
</div>
</div>
</div>
);
}
}
}
export default withRouter(connect(mapStateToProps)(Video))
Update
Im not sure why this is the case but I have to send just the offer and receive the answer again. For now, I resend an offer createAndSendOffer(), starting the process again. I dont think this is the best practice and if you find a better solution that would be great :)

first check your pc.ontrack() firing or not. pc.ontrack() event is fired after pc.addTrack() and remotedescription is set.and also make sure tracks are added before creating answer/offer

Related

webRTC not working when call initiated by FireFox

I am developing a simple webRTC application, using my own server for signaling.
the javascript code is as follow (I have removed the signaling process and unnecessary logic):
const configuration = {
iceServers: [
{
urls: [
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
],
},
],
iceCandidatePoolSize: 10,
};
const callerCandidatesString = "callerCandidates";
const calleeCandidatesString = "calleeCandidates";
var received_offer = null;
var offer = null;
var answer = null;
var peerConnection = null;
let localStream = null;
let remoteStream = null;
var constraints = {
optional: [],
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
}
async function startMedia(e) {
const localStream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
document.getElementById("video1").srcObject = localStream;
remoteStream = new MediaStream();
document.getElementById("video2").srcObject = remoteStream;
if (I am the caller) {
create_the_offer();
}
if (I am the callee) {
get_the_offer();
}
}
async function create_the_offer() {
peerConnection = new RTCPeerConnection(configuration);
registerPeerConnectionListeners();
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
var offer = await peerConnection.createOffer(constraints);
peerConnection.setLocalDescription(offer);
peerConnection.onicecandidate = function(candidate) {
if (candidate.candidate == null) {
//save the offer in the server
--> offer: JSON.stringify(peerConnection.localDescription)},
}
}
check_if_there_is_an_answer();
}
async function get_the_offer() {
// --> retrieve the offer from the server, then
create_answer(offer_from_server);
}
async function create_answer(received_offer) {
console.log("Create PeerConnection with configuration: ", configuration);
peerConnection = new RTCPeerConnection(configuration);
registerPeerConnectionListeners();
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
console.log('received offer:' + received_offer)
my_offer = new RTCSessionDescription(JSON.parse(received_offer));
peerConnection.setRemoteDescription(my_offer);
// collectIceCandidates(peerConnection, calleeCandidatesString, callerCandidatesString);
peerConnection.addEventListener("track", event => {
console.log("Got remote track:", event.streams[0]);
event.streams[0].getTracks().forEach(track => {
console.log("Add a track to the remoteStream:", track);
remoteStream.addTrack(track);
});
});
const answer = await peerConnection.createAnswer(constraints);
console.log("Created answer:", answer);
await peerConnection.setLocalDescription(answer);
peerConnection.onicecandidate = function (e) {
if (e.candidate == null) {
// --> send the answer to the server
}
}
function check_if_there_is_an_answer() {
// retrieve answer from server. this function is executed several times until the answer is received.
// when there is an aswer:
start_remote_connection(answer);
}
async function start_remote_connection(passed_answer) {
my_answer = new RTCSessionDescription(JSON.parse(passed_answer));
peerConnection.setRemoteDescription(my_answer);
peerConnection.addEventListener("track", event => {
console.log("Got remote track:", event.streams[0]);
event.streams[0].getTracks().forEach(track => {
console.log("Add a track to the remoteStream:", track);
remoteStream.addTrack(track);
});
console.log("stream remoto: " + JSON.stringify(remoteStream.getVideoTracks()));
});
document.getElementById("video1").srcObject = localStream;
document.getElementById("video2").srcObject = remoteStream;
}
async function hangUp(e) {
const tracks = document.getElementById("video1").srcObject.getTracks();
tracks.forEach(track => {
track.stop();
});
remoteStream.getTracks().forEach(track => track.stop());
peerConnection.close();
document.getElementById("video1").srcObject = null;
document.getElementById("video2").srcObject = null;
}
// collect ICE Candidates function below
async function collectIceCandidates(peerConnection, localName, remoteName) {
const candidatesCollection = null;
peerConnection.addEventListener("icecandidate", event => {
if (event.candidate) {
const json = event.candidate.toJSON();
candidatesCollection.add(json);
}
});
}
// collect ICE Candidates function above
function registerPeerConnectionListeners() {
peerConnection.addEventListener("icegatheringstatechange", () => {
console.log(
`ICE gathering state changed: ${peerConnection.iceGatheringState}`);
});
peerConnection.addEventListener("connectionstatechange", () => {
console.log(`Connection state change: ${peerConnection.connectionState}`);
});
peerConnection.addEventListener("signalingstatechange", () => {
console.log(`Signaling state change: ${peerConnection.signalingState}`);
});
peerConnection.addEventListener("iceconnectionstatechange ", () => {
console.log(
`ICE connection state change: ${peerConnection.iceConnectionState}`);
});
}
window.onload = startMedia();
If the caller uses chrome and the callee uses FireFox (on localhost, same PC) the code works fine and both users can share their screen.
output with chrome
If the caller uses FireFox and the callee uses Chrome (still on localhost) the code still works fine, but the connection is not established and users cannot see the screen of the other person. I get no error in the console.
output with FireFox
In particular, with FF I am not getting "connection state change: connecting" and then "connection state change: connected".
My guess is that FF and chrome manage the async/await differently, and somehow with FF some values are not ready when actually needed, but cannot figure out why ...
with safari (MacOS) it does not work!!
I then tried it between the computer (with chrome) and an Android phone (chrome browser). It worked the first time I test it, and then never again :(
Does anyone of you has a clue ?

My peer's stream doesn't appear, what am I doing wrong? [WebRTC]

I'm trying to receive my partner's stream but it's not working, I don't know if I'm misimplementing the example given by WebRTC website and I can't understand it well because I'm new to this.
That I have to do?
const connection = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
async function start() {
const localStream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
localStream.getTracks().forEach(track => {
connection.addTrack(track, localStream);
});
}
const remoteVideo = document.querySelector('.remote-video');
connection.addEventListener('track', async (event) => {
const remoteStream = event.streams[0];
remoteVideo.srcObject = remoteStream;
connection.addTrack(event.track);
});
Please Try This:-
const start = async () => {try {
const constraints = { video: { cursor: "always" }, audio: false };
const localStream = await navigator.mediaDevices.getDisplayMedia(
constraints
);
screenStream = localStream;
let videoTrack = screenStream.getVideoTracks()[0];
return localStream;
} catch (error) {}}
const remoteVideo = document.querySelector('.remote-video')
function handleRemoteStreamAddedScreen(event) {
alert("Remote stream added.")
if ("srcObject" in remoteVideo) {
remoteVideo.srcObject = event.streams[0]
} else {
remoteVideo.src = window.URL.createObjectURL(event.stream)}
remoteScreenStream = event.stream}
I hope it will be work for you.

WebRTC doesn't show the video peerConnection remote video

I have tried to connect my video (No webcam) to sync with the other video remotely. But I don't know where I'm wrong. I did it taking into account that they were going to connect to the same room. One performs the stream (LocalVideo) and the other sees what the other does (Remote). Something very similar to this: https://webrtc.github.io/samples/src/content/capture/video-pc/. But I still haven't given an answer for the problem, PLEASE if someone who knows about node.js and WebRTC can rescue me!
main.js:
let divSelectRoom = document.getElementById("selectRoom")
let divConsultingRoom = document.getElementById("consultingRoom")
let divConsultingRoom2 = document.getElementById("consultingRoom2")
let inputRoomNumber = document.getElementById("roomNumber")
let btnGoRoom = document.getElementById("goRoom")
let localVideo = document.getElementById("localVideo")
let remoteVideo = document.getElementById("remoteVideo")
let locallabel = document.getElementById("locallabel")
let visitlabel = document.getElementById("visitlabel")
let roomNumber, localStream, remoteStream, rtcPeerConnection, isCaller
let stream;
const iceServers = {
'iceServer': [
{ 'urls': 'stun:stun.services.mozilla.com' },
{ 'urls': 'stun:stun.l.google.com:19302' }
]
}
const streamConstraints = {
audio: 1,
video: 1
}
let startTime;
const socket = io();
btnGoRoom.onclick = () => {
if (inputRoomNumber.value == '') {
alert("please type a room name")
} else {
roomNumber = inputRoomNumber.value
socket.emit('create or join', roomNumber)
divSelectRoom.style = "display: none"
}
}
function maybeCreateStream() {
if (stream) {
return;
}
if (localVideo.captureStream) {
stream = localVideo.captureStream();
console.log('Captured stream from localVideo with captureStream',
stream);
} else if (localVideo.mozCaptureStream) {
stream = localVideo.mozCaptureStream();
console.log('Captured stream from localVideo with mozCaptureStream()',
stream);
} else {
console.log('captureStream() not supported');
}
localStream = stream
}
localVideo.play();
socket.on('created', room => {
divConsultingRoom.style = "display: block"
// Video tag capture must be set up after video tracks are enumerated.
localVideo.oncanplay = maybeCreateStream;
if (localVideo.readyState >= 3) { // HAVE_FUTURE_DATA
// Video is already ready to play, call maybeCreateStream in case oncanplay
// fired before we registered the event handler.
maybeCreateStream();
}
isCaller = true
})
socket.on('joined', room => {
divConsultingRoom2.style = "display: block"
remoteVideo.srcObject = localStream;
isCaller = false
socket.emit('ready', roomNumber)
})
socket.on('ready', () => {
console.log('enter ready')
const videoTracks = stream.getVideoTracks();
const audioTracks = stream.getAudioTracks();
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
if (audioTracks.length > 0) {
console.log(`Using audio device: ${audioTracks[0].label}`);
}
if (isCaller) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
rtcPeerConnection.onicecandidate = onIceCandidate
rtcPeerConnection.ontrack = onAddStream
rtcPeerConnection.addTrack(localStream.getTracks()[0], localStream)
rtcPeerConnection.addTrack(localStream.getTracks()[1], localStream)
rtcPeerConnection.createOffer()
.then(sessionDescription => {
console.log('sending offer', sessionDescription)
rtcPeerConnection.setLocalDescription(sessionDescription)
socket.emit('offer', {
type: 'offer',
sdp: sessionDescription,
room: roomNumber
})
}).catch(err => {
console.log(err)
})
}
})
socket.on('offer', (event) => {
console.log('enter offer', isCaller)
if (!isCaller) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
rtcPeerConnection.onicecandidate = onIceCandidate
rtcPeerConnection.ontrack = onAddStream
//rtcPeerConnection.addTrack(localStream.getTracks()[0], localStream)
//rtcPeerConnection.addTrack(localStream.getTracks()[1], localStream)
console.log('received offer', event)
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
rtcPeerConnection.createAnswer().then(sessionDescription => {
console.log('sending answer', sessionDescription)
socket.emit('answer', {
type: 'answer',
sdp: sessionDescription,
room: roomNumber
})
}).catch(err => {
console.log(err)
})
}
})
socket.on('answer', event => {
console.log('received answer', event)
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
})
socket.on('candidate', event => {
const candidate = new RTCIceCandidate({
sdpMLineIndex: event.label,
candidate: event.candidate
})
console.log('received candidate', candidate)
rtcPeerConnection.addIceCandidate(candidate)
})
function onAddStream(event) {
console.log('Stream It')
remoteVideo.srcObject = event.streams[0];
remoteStream = event.streams[0]
console.log('visit received remote stream', event.streams[0]);
}
function onIceCandidate(event) {
if (event.candidate) {
console.log('sending ice candidate', event.candidate)
socket.emit('candidate', {
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
room: roomNumber
})
}
}
app.js:
const { Socket } = require('dgram')
const express = require('express')
const app = express()
let http = require('http').Server(app)
const port = process.env.PORT || 3000
let io = require('socket.io')(http)
app.use(express.static('public'))
http.listen(port, () => {
console.log('listening on', port)
})
io.on('connection', socket => {
console.log('a user connected')
socket.on('create or join', room => {
console.log('create or join to room', room)
const myRoom = io.sockets.adapter.rooms[room] || {length: 0}
const numClients = myRoom.length
console.log(room, 'has',numClients,'clients')
if(numClients == 0){
socket.join(room)
socket.emit('created', room)
}else if (numClients == 1){
socket.join(room)
socket.emit('joined', room)
}else{
socket.emit('full', room)
}
})
socket.on('ready', room => {
socket.broadcast.to(room).emit('ready')
})
socket.on('candidate', event => {
socket.broadcast.to(event.room).emit('candidate', event)
})
socket.on('offer', event => {
socket.broadcast.to(event.room).emit('offer', event.sdp)
})
socket.on('answer', event => {
socket.broadcast.to(event.room).emit('answer', event.sdp)
})
})
index.html:
<!DOCTYPE html>
<head>
<title>WebRTC Training</title>
</head>
<body>
<h1>WebRTC TRAINING</h1>
<div id="selectRoom">
<label>Type room name</label>
<input id="roomNumber" type="text"/>
<button id="goRoom">Go</button>
</div>
<div id="consultingRoom" style="display: none;">
<video id="localVideo" playsinline controls loop muted>
<source src="videos/dudu.webm" type="video/webm"/>
<source src="videos/dudu.mp4" type="video/mp4"/>
<p>This browser does not support the video element.</p>
</video>
<label id="locallabel">local</label>
</div>
<div id="consultingRoom2" style="display: none;">
<video id="remoteVideo" playsinline autoplay></video>
<label id="visitlabel">visit</label>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="main.js"></script>
</body>
This is a picture so you can understand it better.

Webrtc video broadcasting Viewer only works sometimes

I´m using Gabriel Tanners video broadcasting Tutorial on localhost it works great! Over Internet connection most of the times the viewer simply doesn't show anything. I spent hours testing and changing the lifecycle. Nothing works.
Here´s the Broadcaster code:
const peerConnections = {};
const config = {
iceServers: [
{
urls: ["stun:stun.l.google.com:19302"]
}
]
};
const socket = io.connect(window.location.origin);
socket.on("answer", (id, description) => {
peerConnections[id].setRemoteDescription(description);
});
socket.on("watcher", id => {
const peerConnection = new RTCPeerConnection(config);
peerConnections[id] = peerConnection;
let stream = videoElement.srcObject;
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
peerConnection
.createOffer()
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("offer", id, peerConnection.localDescription);
});
});
socket.on("candidate", (id, candidate) => {
peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on("disconnectPeer", id => {
peerConnections[id].close();
delete peerConnections[id];
});
window.onunload = window.onbeforeunload = () => {
socket.close();
};
// Get camera and microphone
const videoElement = document.querySelector("video");
const audioSelect = document.querySelector("select#audioSource");
const videoSelect = document.querySelector("select#videoSource");
audioSelect.onchange = getStream;
videoSelect.onchange = getStream;
getStream()
.then(getDevices)
.then(gotDevices);
function getDevices() {
return navigator.mediaDevices.enumerateDevices();
}
function gotDevices(deviceInfos) {
window.deviceInfos = deviceInfos;
for (const deviceInfo of deviceInfos) {
const option = document.createElement("option");
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === "audioinput") {
option.text = deviceInfo.label || `Microphone ${audioSelect.length + 1}`;
audioSelect.appendChild(option);
} else if (deviceInfo.kind === "videoinput") {
option.text = deviceInfo.label || `Camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
}
}
}
function getStream() {
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
const audioSource = audioSelect.value;
const videoSource = videoSelect.value;
const constraints = {
audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
video: { deviceId: videoSource ? { exact: videoSource } : undefined }
};
return navigator.mediaDevices
.getUserMedia(constraints)
.then(gotStream)
.catch(handleError);
}
function gotStream(stream) {
window.stream = stream;
audioSelect.selectedIndex = [...audioSelect.options].findIndex(
option => option.text === stream.getAudioTracks()[0].label
);
videoSelect.selectedIndex = [...videoSelect.options].findIndex(
option => option.text === stream.getVideoTracks()[0].label
);
videoElement.srcObject = stream;
socket.emit("broadcaster");
}
function handleError(error) {
console.error("Error: ", error);
}
The viewer:
let peerConnection;
const config = {
iceServers: [
{
urls: ["stun:stun.l.google.com:19302"]
}
]
};
const socket = io.connect(window.location.origin);
const video = document.querySelector("video");
socket.on("offer", (id, description) => {
peerConnection = new RTCPeerConnection(config);
peerConnection
.setRemoteDescription(description)
.then(() => peerConnection.createAnswer())
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("answer", id, peerConnection.localDescription);
});
peerConnection.ontrack = event => {
video.srcObject = event.streams[0];
};
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
});
socket.on("candidate", (id, candidate) => {
peerConnection
.addIceCandidate(new RTCIceCandidate(candidate))
.catch(e => console.error(e));
});
socket.on("connect", () => {
socket.emit("watcher");
});
socket.on("broadcaster", () => {
socket.emit("watcher");
});
socket.on("disconnectPeer", () => {
peerConnection.close();
});
window.onunload = window.onbeforeunload = () => {
socket.close();
};
I´m very thankful for your help
Since the two devices are not in the same network, you will need a TURN server for the establishment of the connection. A TURN server is used to relay as a relay if the peer-to-peer connection fails, which seems to be happening in your case.
Since there are no public TURN servers out there, you will probably need to create your own. For that, I can recommend the following options:
Coturn - Has a lot of possible configuration options but is thereby harder to set up.
Pion TURN - Easier to set up but doesn't feature as many possibilities
If you want to learn more about the internal of WebRTC and how they work together, checkout out webrtcforthecurious.

Remote video not showing up on one end - WebRTC Video chat app

I'm new to webrtc and react. I'm developing a peer to peer video chat app. On the calling side, both remote video and local video shows up. But on the callee side, only local video shows up. I've been trying to find out where I'm doing wrong but not able to figure it out. One thing I noticed when I console.log peerconnection variable inside handle ice candidate function is, the 'connectionState' is still 'connecting' on the callee side.('connected' on the caller side).
EDIT: I have modified code for readability and using async await. Now I am getting "Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate" error.
EDIT 2: Modified code according to answer
Here is my ORIGINAL code
//refs for my video tag
const localVideoRef = useRef("");
const remoteVideoRef = useRef("");
//video elements
<video ref={remoteVideoRef} playsInline autoPlay className="remoteVideo"></video>
<video ref={localVideoRef} playsInline autoPlay muted className="localVideo"></video>
//button to start call
<button onClick={handleCall}>Call</button>
const handleCall = async () => {
createPeerConnection();
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function(localStream) {
let videoObj = localVideoRef.current;
videoObj.srcObject = localStream;
localStream
.getTracks()
.forEach(track => myPeerConnection.addTrack(track, localStream));
})
.catch("getUserMedia() error: ");
};
let myPeerConnection = null;
function createPeerConnection() {
myPeerConnection = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.ontrack = handleTrackEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}
var isNegotiating = false;
const handleSignalingStateChangeEvent = () => {
isNegotiating = myPeerConnection.signalingState != "stable";
};
function handleNegotiationNeededEvent() {
if (isNegotiating) {
return;
}
isNegotiating = true;
myPeerConnection
.createOffer()
.then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
})
.then(function() {
socket.emit("video-offer", {
from: authContext.user.name,
to: connectedTo,
sdp: myPeerConnection.localDescription
});
});
}
//checking if socket is initialized
if (socket) {
socket.on("gotOffer", data => {
handleVideoOfferMsg(data);
});
socket.on("gotCandidate", data => {
handleNewICECandidateMsg(data);
});
socket.on("gotAnswer", data => {
console.log("inside got answer");
handleGotAnswer(data);
});
}
function handleVideoOfferMsg(msg) {
createPeerConnection();
var desc = new RTCSessionDescription(msg.sdp);
myPeerConnection
.setRemoteDescription(desc)
.then(function() {
return navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
})
.then(function(stream) {
let localStream = stream;
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
localStream
.getTracks()
.forEach(track => myPeerConnection.addTrack(track, localStream));
})
.then(function() {
return myPeerConnection.createAnswer();
})
.then(function(answer) {
return myPeerConnection.setLocalDescription(answer);
})
.then(function() {
socket.emit("video-answer", {
from: authContext.user.name,
to: connectedTo,
sdp: myPeerConnection.localDescription
});
})
.catch("error");
}
async function handleGotAnswer(msg) {
if (!myPeerConnection) return;
// if (isNegotiating) return;
//I don't know why it's not working (no remote video on the caller side too) when I add above line. So, I am checking signaling state in the below line
if (myPeerConnection.signalingState == "stable") return;
await myPeerConnection.setRemoteDescription(
new RTCSessionDescription(msg.sdp)
);
}
function handleICECandidateEvent(event) {
if (!myPeerConnection) return;
if (isNegotiating) return;
if (event.candidate) {
socket.emit("candidate", {
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
}
function handleNewICECandidateMsg(msg) {
if (myPeerConnection.signalingState == "stable") return;
var candidate = new RTCIceCandidate(msg.candidate);
myPeerConnection.addIceCandidate(candidate).catch("error");
}
function handleTrackEvent(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
}
Here is my NEW code:
let pc1 = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
let pc2 = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun2.1.google.com:19302"
}
]
});
const handleCall = async () => {
let stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
let localStream = stream;
stream
.getTracks()
.forEach(async track => await pc1.addTrack(track, localStream));
pc1.onnegotiationneeded = async function() {
let offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
socket.emit("video-offer", {
from: authContext.user.name,
to: connectedTo,
sdp: pc1.localDescription
});
pc1.onicecandidate = function(event) {
if (event.candidate) {
socket.emit("candidate", {
pc: "pc1",
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
};
};
pc1.ontrack = function(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
};
};
//listening to socket emits from server related to video chat
if (socket) {
socket.on("gotOffer", data => {
//step 1 of callee
handleVideoOfferMsg(data);
});
socket.on("gotCandidate", data => {
handleNewICECandidateMsg(data);
});
socket.on("gotAnswer", data => {
handleGotAnswer(data);
});
}
async function handleVideoOfferMsg(msg) {
var desc = new RTCSessionDescription(msg.sdp);
await pc2.setRemoteDescription(desc);
let stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
let videoObj = localVideoRef.current;
videoObj.srcObject = stream;
let localStream = stream;
stream
.getTracks()
.forEach(async track => await pc2.addTrack(track, localStream));
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
socket.emit("video-answer", {
from: authContext.user.name,
to: connectedTo,
sdp: pc2.localDescription
});
pc2.ontrack = function(event) {
let videoObj = remoteVideoRef.current;
videoObj.srcObject = event.streams[0];
};
pc2.onicecandidate = function(event) {
if (event.candidate) {
socket.emit("candidate", {
pc: "pc2",
to: connectedTo,
from: authContext.user.name,
candidate: event.candidate
});
}
};
}
async function handleGotAnswer(msg) {
if (pc1.signalingState == "stable") {
console.log("negotiating");
return;
}
await pc1.setRemoteDescription(new RTCSessionDescription(msg.sdp));
//INSERTED THIS
if (candidatesArray.length) {
candidatesArray.forEach(async msg => {
var candidate = new RTCIceCandidate(msg.candidate);
await pc1.addIceCandidate(candidate);
});
}
}
let candidatesArray = [];
async function handleNewICECandidateMsg(msg) {
if (msg.pc == "pc1") {
var candidate = new RTCIceCandidate(msg.candidate);
await pc2.addIceCandidate(candidate);
}
if (msg.pc == "pc2") {
try {
if (pc1.connectionState != "stable" && !pc1.remoteDescription) {
candidatesArray.push(msg);
return;
}
var candidate = new RTCIceCandidate(msg.candidate);
await pc1.addIceCandidate(candidate);
} catch (error) {
//this is where error is triggered.
console.log("error adding ice candidate: " + error);
}
}
}
I have not put my server side code, because I find no issues in it.
From what I understand, the error is because remotedescription is not being set when addicecandidate is called. May be because I am skipping setting remote description when signalingState is stable. But if I remove that line of code, I am getting another error - "Failed to set remote answer sdp: Called in wrong state: kStable"
Where am I going wrong?
Immediately after calling pc.setLocalDescription(), the PeerConnection will start emitting onicecandidate events, thanks to Trickle ICE. However, this means that maybe the first candidates are generated too fast and they get sent to the remote peer even before sending SDP Offer/Answer!
Maybe that's what happens in your case, and the first candidates are arriving too early from the other side. For this reason it's a good idea to check the PeerConnection signaling state: if it is stable and the remote description has been already set, then you can call pc.addIceCandidate(). If not, you store the candidate in a queue.
Later, when the remote description finally arrives, after setting it you manually add all the candidates that are waiting in the queue.
Here you can see code with this idea. Candidates are first queued, and later when the PeerConnection signaling state becomes stable, queued items are added.

Categories

Resources