James Kuszmaul | 4a42b18 | 2021-01-17 11:32:46 -0800 | [diff] [blame^] | 1 | 'use strict'; |
| 2 | |
| 3 | /** |
| 4 | * A WebRTC peer connection helper. Tightly coupled with the signaling |
| 5 | * class. |
| 6 | */ |
| 7 | class WebRTCPeerConnection { |
| 8 | constructor(signaling, offering, configuration = null) { |
| 9 | // Set default configuration (if none provided) |
| 10 | if (configuration === null) { |
| 11 | configuration = { |
| 12 | iceServers: [{ |
| 13 | urls: 'stun:stun.services.mozilla.com', |
| 14 | }], |
| 15 | }; |
| 16 | } |
| 17 | |
| 18 | // Create peer connection and bind events |
| 19 | const pc = new RTCPeerConnection(configuration); |
| 20 | pc._offering = offering; // Meh! |
| 21 | signaling.pc = pc; |
| 22 | pc.onnegotiationneeded = async () => { |
| 23 | console.log('Negotiation needed'); |
| 24 | |
| 25 | // Create offer (if required) |
| 26 | if (offering) { |
| 27 | console.log('Creating offer'); |
| 28 | const description = await pc.createOffer(); |
| 29 | await pc.setLocalDescription(description); |
| 30 | signaling.handleLocalDescription(description); |
| 31 | } |
| 32 | }; |
| 33 | pc.signalingstatechange = () => { |
| 34 | console.log('Signaling state:', pc.signalingState); |
| 35 | }; |
| 36 | pc.oniceconnectionstatechange = () => { |
| 37 | console.log('ICE connection state:', pc.iceConnectionState); |
| 38 | }; |
| 39 | pc.onicegatheringstatechange = () => { |
| 40 | console.log('ICE gathering state:', pc.iceGatheringState); |
| 41 | }; |
| 42 | pc.onconnectionstatechange = () => { |
| 43 | console.log('Connection state:', pc.connectionState); |
| 44 | }; |
| 45 | pc.onicecandidate = (event) => { |
| 46 | signaling.handleLocalCandidate(event.candidate); |
| 47 | }; |
| 48 | pc.onicecandidateerror = (event) => { |
| 49 | console.error('ICE candidate error:', event); |
| 50 | }; |
| 51 | pc.ondatachannel = (event) => { |
| 52 | const dc = event.channel; |
| 53 | console.log('Incoming data channel:', dc.label); |
| 54 | |
| 55 | // Bind events |
| 56 | this.bindDataChannelEvents(dc); |
| 57 | }; |
| 58 | |
| 59 | // Store configuration & signalling instance |
| 60 | this.pc = pc; |
| 61 | this.dcs = {}; |
| 62 | } |
| 63 | |
| 64 | createDataChannel(name, options = null) { |
| 65 | const pc = this.pc; |
| 66 | |
| 67 | // Create data channel and bind events |
| 68 | const dc = pc.createDataChannel(name, options); |
| 69 | this.bindDataChannelEvents(dc); |
| 70 | |
| 71 | // Store data channel and return |
| 72 | this.dcs[name] = dc; |
| 73 | return dc; |
| 74 | } |
| 75 | |
| 76 | bindDataChannelEvents(dc) { |
| 77 | dc._name = dc.label; // Meh! |
| 78 | dc.onopen = () => { |
| 79 | console.log(dc._name, 'open'); |
| 80 | }; |
| 81 | dc.onclose = () => { |
| 82 | console.log(dc._name, 'closed'); |
| 83 | }; |
| 84 | dc.onerror = (event) => { |
| 85 | console.log(dc._name, 'error:', event); |
| 86 | }; |
| 87 | dc.onbufferedamountlow = () => { |
| 88 | console.log(dc._name, 'buffered amount low:', dc.bufferedAmount); |
| 89 | }; |
| 90 | dc.onmessage = (event) => { |
| 91 | const size = event.data.byteLength || event.data.size; |
| 92 | console.log(dc._name, 'incoming message (' + size + ' bytes)'); |
| 93 | }; |
| 94 | } |
| 95 | } |