]>
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, 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 | console.debug("Creating RTC offer to ", peerId); | |
46 | const offer = await peerConnection.createOffer(); | |
47 | await peerConnection.setLocalDescription(offer); | |
48 | ||
49 | onOffer({ peerId, offer }); | |
50 | }; | |
51 | ||
52 | console.info(`There are now ${countPeers()} participants`); | |
53 | } | |
54 | ||
55 | export function removePeer({ peerId }) { | |
56 | delete internals.peers[peerId]; | |
57 | console.info(`There are now ${countPeers()} participants`); | |
58 | } | |
59 | ||
60 | export async function answerPeerOffer({ peerId, offer }) { | |
61 | const peerConnection = internals.peers[peerId]; | |
62 | ||
63 | const remoteDescription = new RTCSessionDescription(offer); | |
64 | await peerConnection.setRemoteDescription(remoteDescription); | |
65 | ||
66 | const answer = await peerConnection.createAnswer(); | |
67 | await peerConnection.setLocalDescription(answer); | |
68 | ||
69 | return { peerId, answer }; | |
70 | } | |
71 | ||
72 | export async function processPeerAnswer({ peerId, answer }) { | |
73 | const peerConnection = internals.peers[peerId]; | |
74 | const remoteDescription = new RTCSessionDescription(answer); | |
75 | await peerConnection.setRemoteDescription(remoteDescription); | |
76 | } | |
77 | ||
78 | export async function addIceCandidate({ peerId, candidate }) { | |
79 | const peerConnection = internals.peers[peerId]; | |
80 | const iceCandidate = new RTCIceCandidate(candidate); | |
81 | await peerConnection.addIceCandidate(iceCandidate); | |
82 | } | |
83 | ||
84 | export function countPeers() { | |
85 | return Object.keys(internals.peers).length; | |
86 | } | |
87 | ||
88 | export function resetPeers() { | |
89 | internals.peers = {}; | |
90 | document | |
91 | .querySelectorAll(".junction-call-audio") | |
92 | .forEach((audioElement) => audioElement.remove()); | |
93 | } |