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