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
|
class WebRTC {
constructor(server) {
this.configuration = {iceServers: [{urls: 'stun:stun.l.google.com:19302'}]}
this.server = server
this.peerConnections = {}
this.streams = []
// generate an identity identifier
const numbers = window.crypto.getRandomValues(new Uint8Array(4))
const symbols = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
this.identity = Array.prototype.slice.call(numbers).map(x => symbols[x % 62]).join('')
// announce ourselves via the channel
server.sendMessage({announce: true, sender: this.identity})
}
addStream(stream) {
this.streams.push(stream)
// register stream with existing connections
Object.keys(this.peerConnections).forEach(identity => {
const peerConnection = this.peerConnections[identity]
stream.getTracks().forEach(track => { this._addTrack(peerConnection, track, stream) })
})
}
_addTrack(peerConnection, track, stream) {
// send the track over the peer connection
peerConnection.addTrack(track, stream)
}
getPeerConnection(identity) {
const self = this
if (!(identity in self.peerConnections)) {
const peerConnection = new RTCPeerConnection(self.configuration)
// handle connection state changes
peerConnection.addEventListener('connectionstatechange', event => {
})
// set up event handler to handled incoming tracks
peerConnection.addEventListener('track', event => {
})
// support delivering messages through the control channel for ICE
peerConnection.addEventListener('icecandidate', event => {
self.server.sendMessage({icecandidate: event.candidate, sender: self.identity, receipient: identity})
})
// re-send the offer if renegotiation is needed
peerConnection.addEventListener('negotiationneeded', event => {
peerConnection.createOffer().then(offer => {
peerConnection.setLocalDescription(offer).then(x => {
self.server.sendMessage({offer: offer, sender: self.identity, receipient: identity})
})
})
})
// add any existing streams to the connection
this.streams.forEach(stream => {
stream.getTracks().forEach(track => { this._addTrack(peerConnection, track, stream) })
})
this.peerConnections[identity] = peerConnection
}
return this.peerConnections[identity]
}
handleMessage(msg) {
const self = this
var peerConnection
// ignore our own messages
if (msg.sender === self.identity || !msg.sender) { return }
if (msg.announce && !(msg.sender in self.peerConnections)) {
// handle new announce message by sending an offer
peerConnection = self.getPeerConnection(msg.sender)
peerConnection.createOffer().then(offer => {
peerConnection.setLocalDescription(offer).then(x => {
self.server.sendMessage({offer: offer, sender: self.identity, receipient: msg.sender})
})
})
} else if (msg.offer && msg.receipient === self.identity) {
// handle offers for us and answer
peerConnection = self.getPeerConnection(msg.sender)
peerConnection.setRemoteDescription(new RTCSessionDescription(msg.offer))
peerConnection.createAnswer().then(answer => {
peerConnection.setLocalDescription(answer).then(function () {
self.server.sendMessage({answer: answer, sender: self.identity, recipient: msg.sender})
})
})
} else if (msg.answer && msg.recipient === self.identity && msg.sender in self.peerConnections) {
// handle answers to offers
peerConnection = self.getPeerConnection(msg.sender)
peerConnection.setRemoteDescription(new RTCSessionDescription(msg.answer)).then(x => {})
} else if ('icecandidate' in msg && msg.receipient === self.identity && msg.sender in self.peerConnections) {
// handle ICE candidate messages
peerConnection = self.getPeerConnection(msg.sender)
peerConnection.addIceCandidate(msg.icecandidate)
}
}
}
module.exports = WebRTC
|