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 getMediaButton = document.querySelector('button#getMedia'); |
| 12 var connectButton = document.querySelector('button#connect'); |
| 13 var hangupButton = document.querySelector('button#hangup'); |
| 14 |
| 15 getMediaButton.onclick = getMedia; |
| 16 connectButton.onclick = createPeerConnection; |
| 17 hangupButton.onclick = hangup; |
| 18 |
| 19 var minWidthInput = document.querySelector('div#minWidth input'); |
| 20 var maxWidthInput = document.querySelector('div#maxWidth input'); |
| 21 var minHeightInput = document.querySelector('div#minHeight input'); |
| 22 var maxHeightInput = document.querySelector('div#maxHeight input'); |
| 23 var minFramerateInput = document.querySelector('div#minFramerate input'); |
| 24 var maxFramerateInput = document.querySelector('div#maxFramerate input'); |
| 25 |
| 26 minWidthInput.onchange = maxWidthInput.onchange = |
| 27 minHeightInput.onchange = maxHeightInput.onchange = |
| 28 minFramerateInput.onchange = maxFramerateInput.onchange = displayRangeValue; |
| 29 |
| 30 var getUserMediaConstraintsDiv = |
| 31 document.querySelector('div#getUserMediaConstraints'); |
| 32 var bitrateDiv = document.querySelector('div#bitrate'); |
| 33 var peerDiv = document.querySelector('div#peer'); |
| 34 var senderStatsDiv = document.querySelector('div#senderStats'); |
| 35 var receiverStatsDiv = document.querySelector('div#receiverStats'); |
| 36 |
| 37 var localVideo = document.querySelector('div#localVideo video'); |
| 38 var remoteVideo = document.querySelector('div#remoteVideo video'); |
| 39 var localVideoStatsDiv = document.querySelector('div#localVideo div'); |
| 40 var remoteVideoStatsDiv = document.querySelector('div#remoteVideo div'); |
| 41 |
| 42 var localPeerConnection; |
| 43 var remotePeerConnection; |
| 44 var localStream; |
| 45 var bytesPrev; |
| 46 var timestampPrev; |
| 47 |
| 48 main(); |
| 49 |
| 50 function main() { |
| 51 displayGetUserMediaConstraints(); |
| 52 } |
| 53 |
| 54 function hangup() { |
| 55 trace('Ending call'); |
| 56 localPeerConnection.close(); |
| 57 remotePeerConnection.close(); |
| 58 localPeerConnection = null; |
| 59 remotePeerConnection = null; |
| 60 |
| 61 localStream.getTracks().forEach(function(track) { |
| 62 track.stop(); |
| 63 }); |
| 64 localStream = null; |
| 65 |
| 66 hangupButton.disabled = true; |
| 67 getMediaButton.disabled = false; |
| 68 } |
| 69 |
| 70 function getMedia() { |
| 71 getMediaButton.disabled = true; |
| 72 if (localStream) { |
| 73 localStream.getTracks().forEach(function(track) { |
| 74 track.stop(); |
| 75 }); |
| 76 var videoTracks = localStream.getVideoTracks(); |
| 77 for (var i = 0; i !== videoTracks.length; ++i) { |
| 78 videoTracks[i].stop(); |
| 79 } |
| 80 } |
| 81 navigator.mediaDevices.getUserMedia(getUserMediaConstraints()) |
| 82 .then(gotStream) |
| 83 .catch(function(e) { |
| 84 var message = 'getUserMedia error: ' + e.name + '\n' + |
| 85 'PermissionDeniedError may mean invalid constraints.'; |
| 86 alert(message); |
| 87 console.log(message); |
| 88 getMediaButton.disabled = false; |
| 89 }); |
| 90 } |
| 91 |
| 92 function gotStream(stream) { |
| 93 connectButton.disabled = false; |
| 94 console.log('GetUserMedia succeeded'); |
| 95 localStream = stream; |
| 96 localVideo.srcObject = stream; |
| 97 } |
| 98 |
| 99 function getUserMediaConstraints() { |
| 100 var constraints = {}; |
| 101 constraints.audio = true; |
| 102 constraints.video = {}; |
| 103 if (minWidthInput.value !== '0') { |
| 104 constraints.video.width = {}; |
| 105 constraints.video.width.min = minWidthInput.value; |
| 106 } |
| 107 if (maxWidthInput.value !== '0') { |
| 108 constraints.video.width = constraints.video.width || {}; |
| 109 constraints.video.width.max = maxWidthInput.value; |
| 110 } |
| 111 if (minHeightInput.value !== '0') { |
| 112 constraints.video.height = {}; |
| 113 constraints.video.height.min = minHeightInput.value; |
| 114 } |
| 115 if (maxHeightInput.value !== '0') { |
| 116 constraints.video.height = constraints.video.height || {}; |
| 117 constraints.video.height.max = maxHeightInput.value; |
| 118 } |
| 119 if (minFramerateInput.value !== '0') { |
| 120 constraints.video.frameRate = {}; |
| 121 constraints.video.frameRate.min = minFramerateInput.value; |
| 122 } |
| 123 if (maxFramerateInput.value !== '0') { |
| 124 constraints.video.frameRate = constraints.video.frameRate || {}; |
| 125 constraints.video.frameRate.max = maxFramerateInput.value; |
| 126 } |
| 127 |
| 128 return constraints; |
| 129 } |
| 130 |
| 131 function displayGetUserMediaConstraints() { |
| 132 var constraints = getUserMediaConstraints(); |
| 133 console.log('getUserMedia constraints', constraints); |
| 134 getUserMediaConstraintsDiv.textContent = |
| 135 JSON.stringify(constraints, null, ' '); |
| 136 } |
| 137 |
| 138 function createPeerConnection() { |
| 139 connectButton.disabled = true; |
| 140 hangupButton.disabled = false; |
| 141 |
| 142 bytesPrev = 0; |
| 143 timestampPrev = 0; |
| 144 localPeerConnection = new RTCPeerConnection(null); |
| 145 remotePeerConnection = new RTCPeerConnection(null); |
| 146 localPeerConnection.addStream(localStream); |
| 147 console.log('localPeerConnection creating offer'); |
| 148 localPeerConnection.onnegotiationeeded = function() { |
| 149 console.log('Negotiation needed - localPeerConnection'); |
| 150 }; |
| 151 remotePeerConnection.onnegotiationeeded = function() { |
| 152 console.log('Negotiation needed - remotePeerConnection'); |
| 153 }; |
| 154 localPeerConnection.onicecandidate = function(e) { |
| 155 console.log('Candidate localPeerConnection'); |
| 156 if (e.candidate) { |
| 157 remotePeerConnection.addIceCandidate(e.candidate) |
| 158 .then( |
| 159 onAddIceCandidateSuccess, |
| 160 onAddIceCandidateError |
| 161 ); |
| 162 } |
| 163 }; |
| 164 remotePeerConnection.onicecandidate = function(e) { |
| 165 console.log('Candidate remotePeerConnection'); |
| 166 if (e.candidate) { |
| 167 localPeerConnection.addIceCandidate(e.candidate) |
| 168 .then( |
| 169 onAddIceCandidateSuccess, |
| 170 onAddIceCandidateError |
| 171 ); |
| 172 } |
| 173 }; |
| 174 remotePeerConnection.onaddstream = function(e) { |
| 175 console.log('remotePeerConnection got stream'); |
| 176 remoteVideo.srcObject = e.stream; |
| 177 }; |
| 178 localPeerConnection.createOffer().then( |
| 179 function(desc) { |
| 180 console.log('localPeerConnection offering'); |
| 181 localPeerConnection.setLocalDescription(desc); |
| 182 remotePeerConnection.setRemoteDescription(desc); |
| 183 remotePeerConnection.createAnswer().then( |
| 184 function(desc2) { |
| 185 console.log('remotePeerConnection answering'); |
| 186 remotePeerConnection.setLocalDescription(desc2); |
| 187 localPeerConnection.setRemoteDescription(desc2); |
| 188 }, |
| 189 function(err) { |
| 190 console.log(err); |
| 191 } |
| 192 ); |
| 193 }, |
| 194 function(err) { |
| 195 console.log(err); |
| 196 } |
| 197 ); |
| 198 } |
| 199 |
| 200 function onAddIceCandidateSuccess() { |
| 201 trace('AddIceCandidate success.'); |
| 202 } |
| 203 |
| 204 function onAddIceCandidateError(error) { |
| 205 trace('Failed to add Ice Candidate: ' + error.toString()); |
| 206 } |
| 207 |
| 208 // Display statistics |
| 209 setInterval(function() { |
| 210 if (remotePeerConnection && remotePeerConnection.getRemoteStreams()[0]) { |
| 211 remotePeerConnection.getStats(null) |
| 212 .then(function(results) { |
| 213 var statsString = dumpStats(results); |
| 214 receiverStatsDiv.innerHTML = '<h2>Receiver stats</h2>' + statsString; |
| 215 // calculate video bitrate |
| 216 results.forEach(function(report) { |
| 217 var now = report.timestamp; |
| 218 |
| 219 var bitrate; |
| 220 if (report.type === 'inboundrtp' && report.mediaType === 'video') { |
| 221 // firefox calculates the bitrate for us |
| 222 // https://bugzilla.mozilla.org/show_bug.cgi?id=951496 |
| 223 bitrate = Math.floor(report.bitrateMean / 1024); |
| 224 } else if (report.type === 'ssrc' && report.bytesReceived && |
| 225 report.googFrameHeightReceived) { |
| 226 // chrome does not so we need to do it ourselves |
| 227 var bytes = report.bytesReceived; |
| 228 if (timestampPrev) { |
| 229 bitrate = 8 * (bytes - bytesPrev) / (now - timestampPrev); |
| 230 bitrate = Math.floor(bitrate); |
| 231 } |
| 232 bytesPrev = bytes; |
| 233 timestampPrev = now; |
| 234 } |
| 235 if (bitrate) { |
| 236 bitrate += ' kbits/sec'; |
| 237 bitrateDiv.innerHTML = '<strong>Bitrate:</strong> ' + bitrate; |
| 238 } |
| 239 }); |
| 240 |
| 241 // figure out the peer's ip |
| 242 var activeCandidatePair = null; |
| 243 var remoteCandidate = null; |
| 244 |
| 245 // search for the candidate pair |
| 246 results.forEach(function(report) { |
| 247 if (report.type === 'candidatepair' && report.selected || |
| 248 report.type === 'googCandidatePair' && |
| 249 report.googActiveConnection === 'true') { |
| 250 activeCandidatePair = report; |
| 251 } |
| 252 }); |
| 253 if (activeCandidatePair && activeCandidatePair.remoteCandidateId) { |
| 254 remoteCandidate = results[activeCandidatePair.remoteCandidateId]; |
| 255 } |
| 256 if (remoteCandidate && remoteCandidate.ipAddress && |
| 257 remoteCandidate.portNumber) { |
| 258 peerDiv.innerHTML = '<strong>Connected to:</strong> ' + |
| 259 remoteCandidate.ipAddress + |
| 260 ':' + remoteCandidate.portNumber; |
| 261 } |
| 262 }, function(err) { |
| 263 console.log(err); |
| 264 }); |
| 265 localPeerConnection.getStats(null) |
| 266 .then(function(results) { |
| 267 var statsString = dumpStats(results); |
| 268 senderStatsDiv.innerHTML = '<h2>Sender stats</h2>' + statsString; |
| 269 }, function(err) { |
| 270 console.log(err); |
| 271 }); |
| 272 } else { |
| 273 console.log('Not connected yet'); |
| 274 } |
| 275 // Collect some stats from the video tags. |
| 276 if (localVideo.videoWidth) { |
| 277 localVideoStatsDiv.innerHTML = '<strong>Video dimensions:</strong> ' + |
| 278 localVideo.videoWidth + 'x' + localVideo.videoHeight + 'px'; |
| 279 } |
| 280 if (remoteVideo.videoWidth) { |
| 281 remoteVideoStatsDiv.innerHTML = '<strong>Video dimensions:</strong> ' + |
| 282 remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight + 'px'; |
| 283 } |
| 284 }, 1000); |
| 285 |
| 286 // Dumping a stats variable as a string. |
| 287 // might be named toString? |
| 288 function dumpStats(results) { |
| 289 var statsString = ''; |
| 290 results.forEach(function(res) { |
| 291 statsString += '<h3>Report type='; |
| 292 statsString += res.type; |
| 293 statsString += '</h3>\n'; |
| 294 statsString += 'id ' + res.id + '<br>\n'; |
| 295 statsString += 'time ' + res.timestamp + '<br>\n'; |
| 296 Object.keys(res).forEach(function(k) { |
| 297 if (k !== 'timestamp' && k !== 'type' && k !== 'id') { |
| 298 statsString += k + ': ' + res[k] + '<br>\n'; |
| 299 } |
| 300 }); |
| 301 }); |
| 302 return statsString; |
| 303 } |
| 304 |
| 305 // Utility to show the value of a range in a sibling span element |
| 306 function displayRangeValue(e) { |
| 307 var span = e.target.parentElement.querySelector('span'); |
| 308 span.textContent = e.target.value; |
| 309 displayGetUserMediaConstraints(); |
| 310 } |
OLD | NEW |