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