aboutsummaryrefslogtreecommitdiff
path: root/extension/peers.js
blob: 5ba5c487b20533ba1c0cbb3f76e5c1056c2fe793 (plain)
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());
}