const socket = io('http://localhost:4000');
const startButton = document.getElementById('startButton');
const hangupButton = document.getElementById('hangupButton');
const joinRoom = document.getElementById('joinRoom');
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
joinRoom.onclick = async () => {
socket.emit('joinRoom', document.getElementById('companionId').value);
}
let pc;
let localStream;
const onMessage = data => {
if (!localStream) {
console.log('not ready yet');
return;
}
console.log(data);
switch (data.type) {
case 'offer':
handleOffer(data);
break;
case 'answer':
handleAnswer(data);
break;
case 'candidate':
handleCandidate(data);
break;
case 'ready':
// A second tab joined. This tab will initiate a call unless in a call already.
if (pc) {
console.log('already in call, ignoring');
return;
}
makeCall(data);
break;
case 'bye':
if (pc) {
hangup(data);
}
break;
default:
console.log('unhandled', data);
break;
}
}
socket.on('message', onMessage);
startButton.onclick = async () => {
localStream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
localVideo.srcObject = localStream;
startButton.disabled = true;
hangupButton.disabled = false;
const companionId = document.getElementById('companionId').value;
socket.emit('message',
{
id: companionId,
message: {
type: 'ready'
}
});
}
hangupButton.onclick = async () => {
hangup();
socket.emit('message',
{
id: companionId,
message: {
type: 'bye'
}
});
}
async function hangup() {
if (pc) {
pc.close();
pc = null;
}
localStream.getTracks().forEach(track => track.stop());
localStream = null;
startButton.disabled = false;
hangupButton.disabled = true;
}
function createPeerConnection() {
pc = new RTCPeerConnection();
pc.onicecandidate = e => {
const message = {
type: 'candidate',
candidate: null
};
if (e.candidate) {
message.candidate = e.candidate.candidate;
message.sdpMid = e.candidate.sdpMid;
message.sdpMLineIndex = e.candidate.sdpMLineIndex;
}
const companionId = document.getElementById('companionId').value;
socket.emit('message', {
id: companionId,
message
});
}
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];
localStream.getTracks().forEach(track => pc.addTrack(track, localStream));
}
async function makeCall() {
await createPeerConnection();
const companionId = document.getElementById('companionId').value;
const offer = await pc.createOffer();
socket.emit('message', {
id: companionId,
message: {
type: 'offer',
sdp: offer.sdp
}
});
await pc.setLocalDescription(offer);
}
async function handleOffer(offer) {
if (pc) {
console.error('existing peerconnection');
return;
}
await createPeerConnection();
await pc.setRemoteDescription(offer);
const companionId = document.getElementById('companionId').value;
const answer = await pc.createAnswer();
socket.emit('message', {
id: companionId,
message: {
type: 'answer',
sdp: answer.sdp
}
});
await pc.setLocalDescription(answer);
}
async function handleAnswer(answer) {
if (!pc) {
console.error('no peerconnection');
return;
}
await pc.setRemoteDescription(answer);
}
async function handleCandidate(candidate) {
console.log('Candidate', candidate)
if (!pc) {
console.error('no peerconnection');
return;
}
if (!candidate.candidate) {
console.log('Candidate null');
await pc.addIceCandidate(null);
} else {
await pc.addIceCandidate(candidate);
}
}
peerConnection.addEventListener('connectionstatechange', event => {
console.log('PeerState: ', peerConnection.connectionState)
if (peerConnection.connectionState === 'connected') {
console.log('Peer connected')
}
});
socket.on('message', data => {
console.log(data);
socket.emit(data.type, data.message);
})
import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js";
const socket = io('http://localhost:4000');
const pc = () => {
const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
return new RTCPeerConnection(configuration);
}
const peerConnection = pc();
socket.on('answer', async answer => {
console.log(peerConnection.signalingState, peerConnection.connectionState)
const remoteDesc = new RTCSessionDescription(answer);
await peerConnection.setRemoteDescription(remoteDesc);
});
socket.on('iceCandidate', async iceCandidate => {
await peerConnection.addIceCandidate(iceCandidate);
});
peerConnection.addEventListener('icecandidate', event => {
console.log(event);
if (event.candidate) {
console.log('candidate')
socket.emit('message', {type: 'iceCandidate', message: event.candidate});
}
});
peerConnection.addEventListener('connectionstatechange', event => {
console.log('PeerState: ', peerConnection.connectionState)
if (peerConnection.connectionState === 'connected') {
console.log('Peer connected')
}
});
document.querySelector('#listen').onclick = () => {
socket.on('offer', async offer => {
await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit('message', {type: 'answer', message: answer});
});
}
document.querySelector('#makeCall').onclick = async() => {
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
socket.emit('message', {type: 'offer', message: offer});
}
import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js";
const socket = io('http://localhost:4000');
const pc = () => {
const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
return new RTCPeerConnection(configuration);
}
const peerConnection = pc();
async function makeCall(state) {
socket.on('response', async message => {
console.log(message);
//console.log(peerConnection.signalingState, peerConnection.connectionState)
if (message.answer) {
try {
console.log(peerConnection.signalingState, peerConnection.connectionState)
console.log('not stable')
const remoteDesc = new RTCSessionDescription(message.answer);
await peerConnection.setRemoteDescription(remoteDesc);
} catch (e) {
console.error('Answer error', e)
}
}
if (message.offer) {
console.log(peerConnection.signalingState, peerConnection.connectionState)
try {
await peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
if(state) socket.emit('message', {answer});
} catch (e) {
console.error('Offer error', e)
}
}
if (message.iceCandidate) {
try {
await peerConnection.addIceCandidate(message.iceCandidate);
} catch (e) {
console.error('Error adding received ice candidate', e);
}
}
});
peerConnection.addEventListener('icecandidate', event => {
console.log(event);
if (event.candidate) {
console.log('candidate')
socket.emit('message', {iceCandidate: event.candidate});
}
});
peerConnection.addEventListener('connectionstatechange', event => {
console.log('PeerState: ', peerConnection.connectionState)
if (peerConnection.connectionState === 'connected') {
console.log('Peer connected')
}
});
}
document.querySelector('#makeCall').onclick = async() => {
makeCall(true);
console.log('wait listening')
}
document.querySelector('#listen').onclick = async() => {
makeCall(false)
console.log('start listening')
//const peerConnection = pc();
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
socket.emit('message', {offer});
}
{offer: {…}}
index.js:13 have-local-offer new
index.js:11 {answer: {…}}
index.js:13 stable new
index.js:18 stable new
index.js:20 not stable
peerConnection.addEventListener('icecandidate', event => {
console.log(event);
if (event.candidate) {
console.log('candidate')
socket.emit('message', {iceCandidate: event.candidate});
}
});
peerConnection.addEventListener('connectionstatechange', event => {
console.log('PeerState: ', peerConnection.connectionState)
if (peerConnection.connectionState === 'connected') {
console.log('Peer connected')
}
});