OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. |
| 5 */ |
| 6 'use strict'; |
| 7 |
| 8 var localConnection; |
| 9 var remoteConnection; |
| 10 var sendChannel; |
| 11 var receiveChannel; |
| 12 var pcConstraint; |
| 13 var megsToSend = document.querySelector('input#megsToSend'); |
| 14 var sendButton = document.querySelector('button#sendTheData'); |
| 15 var orderedCheckbox = document.querySelector('input#ordered'); |
| 16 var sendProgress = document.querySelector('progress#sendProgress'); |
| 17 var receiveProgress = document.querySelector('progress#receiveProgress'); |
| 18 var errorMessage = document.querySelector('div#errorMsg'); |
| 19 |
| 20 var receivedSize = 0; |
| 21 var bytesToSend = 0; |
| 22 |
| 23 sendButton.onclick = createConnection; |
| 24 |
| 25 // Prevent data sent to be set to 0. |
| 26 megsToSend.addEventListener('change', function(e) { |
| 27 if (this.value <= 0) { |
| 28 sendButton.disabled = true; |
| 29 errorMessage.innerHTML = '<p>Please enter a number greater than zero.</p>'; |
| 30 } else { |
| 31 errorMessage.innerHTML = ''; |
| 32 sendButton.disabled = false; |
| 33 } |
| 34 }); |
| 35 |
| 36 function createConnection() { |
| 37 sendButton.disabled = true; |
| 38 megsToSend.disabled = true; |
| 39 var servers = null; |
| 40 pcConstraint = null; |
| 41 |
| 42 bytesToSend = Math.round(megsToSend.value) * 1024 * 1024; |
| 43 |
| 44 // Add localConnection to global scope to make it visible |
| 45 // from the browser console. |
| 46 window.localConnection = localConnection = new RTCPeerConnection(servers, |
| 47 pcConstraint); |
| 48 trace('Created local peer connection object localConnection'); |
| 49 |
| 50 var dataChannelParams = {ordered: false}; |
| 51 if (orderedCheckbox.checked) { |
| 52 dataChannelParams.ordered = true; |
| 53 } |
| 54 |
| 55 sendChannel = localConnection.createDataChannel( |
| 56 'sendDataChannel', dataChannelParams); |
| 57 sendChannel.binaryType = 'arraybuffer'; |
| 58 trace('Created send data channel'); |
| 59 |
| 60 sendChannel.onopen = onSendChannelStateChange; |
| 61 sendChannel.onclose = onSendChannelStateChange; |
| 62 localConnection.onicecandidate = function(e) { |
| 63 onIceCandidate(localConnection, e); |
| 64 }; |
| 65 |
| 66 localConnection.createOffer().then( |
| 67 gotDescription1, |
| 68 onCreateSessionDescriptionError |
| 69 ); |
| 70 |
| 71 // Add remoteConnection to global scope to make it visible |
| 72 // from the browser console. |
| 73 window.remoteConnection = remoteConnection = new RTCPeerConnection(servers, |
| 74 pcConstraint); |
| 75 trace('Created remote peer connection object remoteConnection'); |
| 76 |
| 77 remoteConnection.onicecandidate = function(e) { |
| 78 onIceCandidate(remoteConnection, e); |
| 79 }; |
| 80 remoteConnection.ondatachannel = receiveChannelCallback; |
| 81 } |
| 82 |
| 83 function onCreateSessionDescriptionError(error) { |
| 84 trace('Failed to create session description: ' + error.toString()); |
| 85 } |
| 86 |
| 87 function randomAsciiString(length) { |
| 88 var result = ''; |
| 89 for (var i = 0; i < length; i++) { |
| 90 // Visible ASCII chars are between 33 and 126. |
| 91 result += String.fromCharCode(33 + Math.random() * 93); |
| 92 } |
| 93 return result; |
| 94 } |
| 95 |
| 96 function sendGeneratedData() { |
| 97 sendProgress.max = bytesToSend; |
| 98 receiveProgress.max = sendProgress.max; |
| 99 sendProgress.value = 0; |
| 100 receiveProgress.value = 0; |
| 101 |
| 102 var chunkSize = 16384; |
| 103 var stringToSendRepeatedly = randomAsciiString(chunkSize); |
| 104 var bufferFullThreshold = 5 * chunkSize; |
| 105 var usePolling = true; |
| 106 if (typeof sendChannel.bufferedAmountLowThreshold === 'number') { |
| 107 trace('Using the bufferedamountlow event for flow control'); |
| 108 usePolling = false; |
| 109 |
| 110 // Reduce the buffer fullness threshold, since we now have more efficient |
| 111 // buffer management. |
| 112 bufferFullThreshold = chunkSize / 2; |
| 113 |
| 114 // This is "overcontrol": our high and low thresholds are the same. |
| 115 sendChannel.bufferedAmountLowThreshold = bufferFullThreshold; |
| 116 } |
| 117 // Listen for one bufferedamountlow event. |
| 118 var listener = function() { |
| 119 sendChannel.removeEventListener('bufferedamountlow', listener); |
| 120 sendAllData(); |
| 121 }; |
| 122 var sendAllData = function() { |
| 123 // Try to queue up a bunch of data and back off when the channel starts to |
| 124 // fill up. We don't setTimeout after each send since this lowers our |
| 125 // throughput quite a bit (setTimeout(fn, 0) can take hundreds of milli- |
| 126 // seconds to execute). |
| 127 while (sendProgress.value < sendProgress.max) { |
| 128 if (sendChannel.bufferedAmount > bufferFullThreshold) { |
| 129 if (usePolling) { |
| 130 setTimeout(sendAllData, 250); |
| 131 } else { |
| 132 sendChannel.addEventListener('bufferedamountlow', listener); |
| 133 } |
| 134 return; |
| 135 } |
| 136 sendProgress.value += chunkSize; |
| 137 sendChannel.send(stringToSendRepeatedly); |
| 138 } |
| 139 }; |
| 140 setTimeout(sendAllData, 0); |
| 141 } |
| 142 |
| 143 function closeDataChannels() { |
| 144 trace('Closing data channels'); |
| 145 sendChannel.close(); |
| 146 trace('Closed data channel with label: ' + sendChannel.label); |
| 147 receiveChannel.close(); |
| 148 trace('Closed data channel with label: ' + receiveChannel.label); |
| 149 localConnection.close(); |
| 150 remoteConnection.close(); |
| 151 localConnection = null; |
| 152 remoteConnection = null; |
| 153 trace('Closed peer connections'); |
| 154 } |
| 155 |
| 156 function gotDescription1(desc) { |
| 157 localConnection.setLocalDescription(desc); |
| 158 trace('Offer from localConnection \n' + desc.sdp); |
| 159 remoteConnection.setRemoteDescription(desc); |
| 160 remoteConnection.createAnswer().then( |
| 161 gotDescription2, |
| 162 onCreateSessionDescriptionError |
| 163 ); |
| 164 } |
| 165 |
| 166 function gotDescription2(desc) { |
| 167 remoteConnection.setLocalDescription(desc); |
| 168 trace('Answer from remoteConnection \n' + desc.sdp); |
| 169 localConnection.setRemoteDescription(desc); |
| 170 } |
| 171 |
| 172 function getOtherPc(pc) { |
| 173 return (pc === localConnection) ? remoteConnection : localConnection; |
| 174 } |
| 175 |
| 176 function getName(pc) { |
| 177 return (pc === localConnection) ? 'localPeerConnection' : |
| 178 'remotePeerConnection'; |
| 179 } |
| 180 |
| 181 function onIceCandidate(pc, event) { |
| 182 getOtherPc(pc).addIceCandidate(event.candidate) |
| 183 .then( |
| 184 function() { |
| 185 onAddIceCandidateSuccess(pc); |
| 186 }, |
| 187 function(err) { |
| 188 onAddIceCandidateError(pc, err); |
| 189 } |
| 190 ); |
| 191 trace(getName(pc) + ' ICE candidate: \n' + (event.candidate ? |
| 192 event.candidate.candidate : '(null)')); |
| 193 } |
| 194 |
| 195 function onAddIceCandidateSuccess() { |
| 196 trace('AddIceCandidate success.'); |
| 197 } |
| 198 |
| 199 function onAddIceCandidateError(error) { |
| 200 trace('Failed to add Ice Candidate: ' + error.toString()); |
| 201 } |
| 202 |
| 203 function receiveChannelCallback(event) { |
| 204 trace('Receive Channel Callback'); |
| 205 receiveChannel = event.channel; |
| 206 receiveChannel.binaryType = 'arraybuffer'; |
| 207 receiveChannel.onmessage = onReceiveMessageCallback; |
| 208 |
| 209 receivedSize = 0; |
| 210 } |
| 211 |
| 212 function onReceiveMessageCallback(event) { |
| 213 receivedSize += event.data.length; |
| 214 receiveProgress.value = receivedSize; |
| 215 |
| 216 if (receivedSize === bytesToSend) { |
| 217 closeDataChannels(); |
| 218 sendButton.disabled = false; |
| 219 megsToSend.disabled = false; |
| 220 } |
| 221 } |
| 222 |
| 223 function onSendChannelStateChange() { |
| 224 var readyState = sendChannel.readyState; |
| 225 trace('Send channel state is: ' + readyState); |
| 226 if (readyState === 'open') { |
| 227 sendGeneratedData(); |
| 228 } |
| 229 } |
OLD | NEW |