1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
const internals = {
peers: {},
createAudioElement(source) {
const audioElement = document.createElement("audio");
audioElement.setAttribute("class", "junction-call-audio");
audioElement.autoplay = "autoplay";
audioElement.srcObject = source;
document.querySelector("body").appendChild(audioElement);
return audioElement;
},
};
export function addPeer({
peerId,
shouldCreateOffer,
mediaStream,
onOffer,
socket,
}) {
const peerConnection = new RTCPeerConnection(
{ iceServers: internals.kIceServers },
{ optional: [{ DtlsSrtpKeyAgreement: true }] },
);
internals.peers[peerId] = peerConnection;
mediaStream.getTracks().forEach((track) => {
peerConnection.addTrack(track, mediaStream);
});
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit("relayICECandidate", {
peerId: peerId,
candidate: event.candidate,
});
}
};
const remoteStream = new MediaStream();
peerConnection.ontrack = (event) => {
remoteStream.addTrack(event.track);
const remoteAudioElement = new Audio();
remoteAudioElement.srcObject = remoteStream;
remoteAudioElement.play();
};
peerConnection.onnegotiationneeded = async () => {
if (shouldCreateOffer) {
console.debug("Creating RTC offer to ", peerId);
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
onOffer({ peerId, offer });
}
};
console.info(`There are now ${countPeers()} participants`);
}
export function removePeer({ peerId }) {
delete internals.peers[peerId];
console.info(`There are now ${countPeers()} participants`);
}
export async function answerPeerOffer({ peerId, offer }) {
console.info(`Answering peer ${peerId}`);
const peerConnection = internals.peers[peerId];
const remoteDescription = new RTCSessionDescription(offer);
await peerConnection.setRemoteDescription(remoteDescription);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
return { peerId, answer };
}
export async function processPeerAnswer({ peerId, answer }) {
console.info(`Processing answer for peer ${peerId}`);
const peerConnection = internals.peers[peerId];
const remoteDescription = new RTCSessionDescription(answer);
await peerConnection.setRemoteDescription(remoteDescription);
}
export async function addIceCandidate({ peerId, candidate }) {
console.info(`Adding ICE candidate for peer ${peerId}`);
const peerConnection = internals.peers[peerId];
console.info(peerConnection.signalingState);
const iceCandidate = new RTCIceCandidate(candidate);
await peerConnection.addIceCandidate(iceCandidate);
}
export function countPeers() {
return Object.keys(internals.peers).length;
}
export function resetPeers() {
for (const connection of Object.values(internals.peers)) {
connection.close();
}
internals.peers = {};
document
.querySelectorAll(".junction-call-audio")
.forEach((audioElement) => audioElement.remove());
}
|