I am creating a test web rtc video call app with vanilla js using vite. I am handling the rtc connection flow as this. First globally I initialize a RTCPeerConnection with iceservers. Then I have a button to start the cam and I handle adding local and remote streams to PC.
Then I have a create Offer button, in that button. Then I create offer and set as local description and send the offer via web sockets.
I am listening to web sockets messages. I handle candidates, offers, and answers. In offers when web sockets receives an offer I create a new RTCSessionDescription and then set it as remote description. And in offer I am creating an answer with pc.createAnswer() and set it as pc.setLocalDescription and then send the answer. And in answer I create a new RTCSessionDescription and then set it as remote description. And when I receive a candidate create new RTCIceCandidate add that candidate to PC using addIceCandidate. that’s how I handle signaling.
And also I listen for pc.onicecandidate event and then I send the candidate via web sockets
This is the code
const servers = {
iceServers: [
{
urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'],
},
],
iceCandidatePoolSize: 10,
};
// Global State
const pc = new RTCPeerConnection(servers);
let localStream = null;
let remoteStream = null;
const webcamButton = document.getElementById('webcamButton');
const webcamVideo = document.getElementById('webcamVideo');
const callButton = document.getElementById('callButton');
const answerButton = document.getElementById('answerButton');
const remoteVideo = document.getElementById('remoteVideo');
const socket = new WebSocket("ws://localhost:8000/ws/" + 123);
const generateRandomNumber = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
socket.onopen = () => {
console.log('connected to socket');
socket.send(JSON.stringify({
type: 'join',
userId: generateRandomNumber(0, 100)
}));
}
Signaling with sockets
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'offer') {
console.log('has offer', data);
const offerDescription = new RTCSessionDescription(data)
pc.setRemoteDescription(offerDescription)
const answerDescription = pc.createAnswer()
pc.setLocalDescription(answerDescription)
const answer = {
type: answerDescription.type,
sdp: answerDescription.sdp,
};
socket.send(JSON.stringify(answer));
} else if (data.type === 'answer') {
console.log('has answer', data);
if (!pc.currentRemoteDescription) {
const answerDescription = new RTCSessionDescription(data)
pc.setRemoteDescription(answerDescription)
}
} else if (data.type === 'candidate') {
console.log('has candidate', data);
const candidate = new RTCIceCandidate(data.candidate)
if (candidate) pc.addIceCandidate(candidate)
}
}
Getting feed from camera
webcamButton.onclick = async () => {
localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
remoteStream = new MediaStream();
// Push tracks from local stream to peer connection
localStream.getTracks().forEach((track) => {
pc.addTrack(track, localStream);
});
// Pull tracks from remote stream, add to video stream
pc.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track);
});
};
webcamVideo.srcObject = localStream;
remoteVideo.srcObject = remoteStream;
};
// 2. Create an offer
callButton.onclick = async () => {
console.log('in call button func');
// Create offer
const offerDescription = await pc.createOffer();
await pc.setLocalDescription(offerDescription);
const offer = {
sdp: offerDescription.sdp,
type: offerDescription.type,
};
socket.send(JSON.stringify(offer))
};
pc.onicecandidate = (event) => {
if (event.candidate) {
socket.send(JSON.stringify({
type: 'candidate',
candidate: event.candidate,
}));
}
};
Is my web rtc flow correct ? And it does not seems like even if I send the offer, it is not creating an answer because I don’t see 'has answer'
in the console.
I want to answer the call automatically when the 2nd user join the room, even if I create a separate function to answer the call it’s confusing to me where should I call the function.