vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 1 | <html> |
| 2 | <head> |
| 3 | <title>Constraints and Statistics</title> |
hta@webrtc.org | db3f427 | 2013-03-05 15:23:40 +0000 | [diff] [blame] | 4 | <!-- Load the polyfill to switch-hit between Chrome and Firefox --> |
| 5 | <script src="../../base/adapter.js"></script> |
| 6 | |
hta@webrtc.org | 37bf584 | 2013-04-06 10:05:55 +0000 | [diff] [blame^] | 7 | <style type="text/css"> |
| 8 | td { vertical-align: top; } |
| 9 | </style> |
| 10 | |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 11 | <script> |
| 12 | var mystream; |
| 13 | var pc1; |
| 14 | var pc2; |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 15 | var bytesPrev = 0; |
| 16 | var timestampPrev = 0; |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 17 | |
| 18 | $ = function(id) { |
| 19 | return document.getElementById(id); |
| 20 | } |
| 21 | |
| 22 | function log(txt) { |
| 23 | console.log(txt); |
| 24 | } |
| 25 | |
| 26 | function openCamera() { |
| 27 | if (mystream) { |
| 28 | mystream.stop(); |
| 29 | } |
| 30 | navigator.webkitGetUserMedia(cameraConstraints(), gotStream, function() { |
| 31 | log("GetUserMedia failed"); |
| 32 | }); |
| 33 | } |
| 34 | |
| 35 | function gotStream(stream) { |
| 36 | log("GetUserMedia succeeded"); |
| 37 | mystream = stream; |
| 38 | $("local-video").src = webkitURL.createObjectURL(stream); |
| 39 | } |
| 40 | |
| 41 | function cameraConstraints() { |
| 42 | var constraints = {}; |
| 43 | constraints.audio = true; |
| 44 | constraints.video = { mandatory: {}, optional: [] }; |
| 45 | if ($("minwidth").value != "0") { |
| 46 | constraints.video.mandatory.minWidth = $("minwidth").value; |
| 47 | } |
| 48 | if ($("maxwidth").value != "0") { |
| 49 | constraints.video.mandatory.maxWidth = $("maxwidth").value; |
| 50 | } |
| 51 | if ($("minheight").value != "0") { |
| 52 | constraints.video.mandatory.minHeight = $("minheight").value; |
| 53 | } |
| 54 | if ($("maxheight").value != "0") { |
| 55 | constraints.video.mandatory.maxHeight = $("maxheight").value; |
| 56 | } |
| 57 | if ($("frameRate").value != "0") { |
| 58 | constraints.video.mandatory.minFrameRate = $("frameRate").value; |
| 59 | } |
| 60 | log('Camera constraints are ' + JSON.stringify(constraints)); |
| 61 | $("cameraConstraints").innerHTML = JSON.stringify(constraints, null, ' '); |
| 62 | return constraints; |
| 63 | } |
| 64 | |
| 65 | function streamConstraints() { |
| 66 | var constraints = { mandatory: {}, optional: [] }; |
| 67 | if ($("bandwidth").value != "0") { |
| 68 | constraints.optional[0] = { 'bandwidth' : $('bandwidth').value }; |
| 69 | } |
| 70 | log('Constraints are ' + JSON.stringify(constraints)); |
| 71 | $("addStreamConstraints").innerHTML = JSON.stringify(constraints, null, ' '); |
| 72 | return constraints; |
| 73 | } |
| 74 | |
| 75 | |
| 76 | |
| 77 | function connect() { |
| 78 | pc1 = new webkitRTCPeerConnection(null); |
| 79 | pc2 = new webkitRTCPeerConnection(null); |
| 80 | pc1.addStream(mystream, streamConstraints()); |
| 81 | log('PC1 creating offer'); |
| 82 | pc1.onnegotiationeeded = function() { |
| 83 | log('Negotiation needed - PC1'); |
| 84 | } |
| 85 | pc2.onnegotiationeeded = function() { |
| 86 | log('Negotiation needed - PC2'); |
| 87 | } |
| 88 | pc1.onicecandidate = function(e) { |
| 89 | log('Candidate PC1'); |
| 90 | if (e.candidate) { |
| 91 | pc2.addIceCandidate(new RTCIceCandidate(e.candidate)); |
| 92 | } |
| 93 | } |
| 94 | pc2.onicecandidate = function(e) { |
| 95 | log('Candidate PC2'); |
| 96 | if (e.candidate) { |
| 97 | pc1.addIceCandidate(new RTCIceCandidate(e.candidate)); |
| 98 | } |
| 99 | } |
| 100 | pc2.onaddstream = function(e) { |
| 101 | log('PC2 got stream'); |
| 102 | $('remote-video').src = webkitURL.createObjectURL(e.stream); |
| 103 | log('Remote video is ' + $('remote-video').src); |
| 104 | } |
| 105 | pc1.createOffer(function(desc) { |
| 106 | log('PC1 offering'); |
| 107 | pc1.setLocalDescription(desc); |
| 108 | pc2.setRemoteDescription(desc); |
| 109 | pc2.createAnswer(function(desc2) { |
| 110 | log('PC2 answering'); |
| 111 | pc2.setLocalDescription(desc2); |
| 112 | pc1.setRemoteDescription(desc2); |
| 113 | }); |
| 114 | }); |
| 115 | } |
| 116 | |
| 117 | // Display statistics |
| 118 | var statCollector = setInterval(function() { |
| 119 | var display = function(str) { |
| 120 | $('bitrate').innerHTML = str; |
| 121 | } |
| 122 | |
| 123 | display("No stream"); |
hta@webrtc.org | db3f427 | 2013-03-05 15:23:40 +0000 | [diff] [blame] | 124 | if (pc2 && pc2.getRemoteStreams()[0]) { |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 125 | if (pc2.getStats) { |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 126 | pc2.getStats(function(stats) { |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 127 | var statsString = ''; |
| 128 | var results = stats.result(); |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 129 | var bitrateText = 'No bitrate stats'; |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 130 | for (var i = 0; i < results.length; ++i) { |
| 131 | var res = results[i]; |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 132 | statsString += '<h3>Report '; |
| 133 | statsString += i; |
| 134 | statsString += '</h3>'; |
hta@webrtc.org | ecfd328 | 2013-03-19 08:45:47 +0000 | [diff] [blame] | 135 | if (!res.local || res.local === res) { |
| 136 | statsString += dumpStats(res); |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 137 | // The bandwidth info for video is in a type ssrc stats record |
| 138 | // with googFrameHeightReceived defined. |
| 139 | // Should check for mediatype = video, but this is not |
| 140 | // implemented yet. |
| 141 | if (res.type == 'ssrc' && res.stat('googFrameHeightReceived')) { |
| 142 | var bytesNow = res.stat('bytesReceived'); |
| 143 | if (timestampPrev > 0) { |
| 144 | var bitRate = Math.round((bytesNow - bytesPrev) * 8 / |
| 145 | (res.timestamp - timestampPrev)); |
| 146 | bitrateText = bitRate + ' kbits/sec'; |
| 147 | } |
| 148 | timestampPrev = res.timestamp; |
| 149 | bytesPrev = bytesNow; |
| 150 | } |
hta@webrtc.org | ecfd328 | 2013-03-19 08:45:47 +0000 | [diff] [blame] | 151 | } else { |
| 152 | // Pre-227.0.1445 (188719) browser |
| 153 | if (res.local) { |
| 154 | statsString += "<p>Local "; |
| 155 | statsString += dumpStats(res.local); |
| 156 | } |
| 157 | if (res.remote) { |
| 158 | statsString += "<p>Remote "; |
| 159 | statsString += dumpStats(res.remote); |
| 160 | } |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 161 | } |
| 162 | } |
hta@webrtc.org | 37bf584 | 2013-04-06 10:05:55 +0000 | [diff] [blame^] | 163 | $('receiverstats').innerHTML = statsString; |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 164 | display(bitrateText); |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 165 | }); |
hta@webrtc.org | 37bf584 | 2013-04-06 10:05:55 +0000 | [diff] [blame^] | 166 | pc1.getStats(function(stats) { |
| 167 | var statsString = ''; |
| 168 | var results = stats.result(); |
| 169 | for (var i = 0; i < results.length; ++i) { |
| 170 | var res = results[i]; |
| 171 | statsString += '<h3>Report '; |
| 172 | statsString += i; |
| 173 | statsString += '</h3>'; |
| 174 | if (!res.local || res.local === res) { |
| 175 | statsString += dumpStats(res); |
| 176 | } |
| 177 | } |
| 178 | $('senderstats').innerHTML = statsString; |
| 179 | }); |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 180 | } else { |
| 181 | display('No stats function. Use at least Chrome 24.0.1285'); |
| 182 | } |
| 183 | } else { |
| 184 | log('Not connected yet'); |
| 185 | } |
| 186 | // Collect some stats from the video tags. |
| 187 | local_video = $('local-video'); |
| 188 | if (local_video) { |
| 189 | $('local-video-stats').innerHTML = local_video.videoWidth + |
| 190 | 'x' + local_video.videoHeight; |
| 191 | } |
| 192 | remote_video = $('remote-video'); |
| 193 | if (remote_video) { |
| 194 | $('remote-video-stats').innerHTML = remote_video.videoWidth + |
| 195 | 'x' + remote_video.videoHeight; |
| 196 | } |
| 197 | }, 1000); |
| 198 | |
| 199 | // Dumping a stats variable as a string. |
| 200 | // might be named toString? |
| 201 | function dumpStats(obj) { |
| 202 | var statsString = 'Timestamp:'; |
| 203 | statsString += obj.timestamp; |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 204 | if (obj.id) { |
hta@webrtc.org | 37bf584 | 2013-04-06 10:05:55 +0000 | [diff] [blame^] | 205 | statsString += "<br>id "; |
hta@webrtc.org | 3ed599a | 2013-03-22 08:48:16 +0000 | [diff] [blame] | 206 | statsString += obj.id; |
| 207 | } |
| 208 | if (obj.type) { |
| 209 | statsString += " type "; |
| 210 | statsString += obj.type; |
| 211 | } |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 212 | if (obj.names) { |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 213 | names = obj.names(); |
| 214 | for (var i = 0; i < names.length; ++i) { |
| 215 | statsString += '<br>'; |
| 216 | statsString += names[i]; |
| 217 | statsString += ':'; |
| 218 | statsString += obj.stat(names[i]); |
| 219 | } |
| 220 | } else { |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 221 | if (obj.stat('audioOutputLevel')) { |
| 222 | statsString += "audioOutputLevel: "; |
| 223 | statsString += obj.stat('audioOutputLevel'); |
| 224 | statsString += "<br>"; |
| 225 | } |
| 226 | } |
| 227 | return statsString; |
| 228 | } |
| 229 | |
| 230 | |
| 231 | // Utility to show the value of a field in a span called name+Display |
| 232 | function showValue(name, value) { |
| 233 | $(name + 'Display').innerHTML = value; |
| 234 | } |
| 235 | </script> |
| 236 | </head> |
| 237 | <body> |
| 238 | <h1>Constraints and Statistics</h1> |
| 239 | This page is meant to give some hints on how one can use constraints and statistics in WebRTC applications. |
| 240 | <p> |
| 241 | The form to the left gives constraints you can set on the getUserMedia call. |
| 242 | When you hit "open", it will (re)open the camera with these constraints. |
| 243 | <p> |
| 244 | The left picture is the local preview. The right picture is the picture |
| 245 | after being passed through the PeerConnection (locally). |
| 246 | <p> |
| 247 | Underneath the picture you will see a running display of how many Kbits/sec |
| 248 | the video feed uses for transmission. |
| 249 | <hr> |
| 250 | <table> |
| 251 | <tr> |
| 252 | <td align="top"> |
| 253 | <h2>getUserMedia constraints</h2> |
| 254 | <table> |
| 255 | <tr><td><td>Min<td>Max |
| 256 | <tr><td>Horizontal |
| 257 | <td><input type="range" id="minwidth" min="0" max="1280" value="300" |
| 258 | onchange="showValue(this.id, this.value)"> |
| 259 | <td><input type="range" id="maxwidth" min="0" max="1280" value="640" |
| 260 | onchange="showValue(this.id, this.value)"> |
| 261 | <td><span id="minwidthDisplay">300</span>-<span id="maxwidthDisplay">640</span> |
| 262 | <tr><td>Vertical |
| 263 | <td><input type="range" id="minheight" min="0" max="1280" value="200" |
| 264 | onchange="showValue(this.id, this.value)"> |
| 265 | <td><input type="range" id="maxheight" min="0" max="1280" value="480" |
| 266 | onchange="showValue(this.id, this.value)"> |
| 267 | <td><span id="minheightDisplay">200</span>-<span id="maxheightDisplay">480</span> |
| 268 | <tr><td> |
| 269 | FrameRate |
| 270 | <td colspan=2><input type="range" id="frameRate" min="0" max="60" value="30" |
| 271 | onchange="showValue(this.id, this.value)"> |
| 272 | <td><span id="frameRateDisplay">30</span> |
| 273 | </table> |
| 274 | <input type="submit" name="capture" value="Capture!" onclick="openCamera()"> |
| 275 | </td> |
| 276 | <td align="top"> |
| 277 | <h2>addStream constraints</h2> |
| 278 | Maximum bitrate |
| 279 | <input type="range" id="bandwidth" min="0" max="2000" value="1000" |
| 280 | onchange="showValue(this.id, this.value)"> |
| 281 | <span id="bandwidthDisplay">1000</span> |
| 282 | <br> |
| 283 | <input type="submit" name="connect" value="Connect!" onclick="connect()"> |
| 284 | </td> |
| 285 | </tr> |
| 286 | <tr> |
| 287 | <td> |
vikasmarwaha@webrtc.org | da0f708 | 2013-03-11 16:58:07 +0000 | [diff] [blame] | 288 | <video id="local-video" autoplay width=400 muted="true"></video> |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 289 | </td> |
| 290 | <td> |
| 291 | <video id="remote-video" autoplay width=400></video> |
| 292 | </td> |
| 293 | <tr> |
| 294 | <td><span id="local-video-stats"></span> |
| 295 | <td><span id="remote-video-stats"></span> |
| 296 | <br> |
| 297 | <span id="bitrate">Bitrate unknown</span> |
| 298 | </td> |
| 299 | </tr> |
| 300 | <tr> |
| 301 | <td><pre><span id="cameraConstraints"></span></pre> |
| 302 | <td><pre><span id="addStreamConstraints"></span></pre> |
| 303 | </table> |
| 304 | <h2>Statistics report display</h2> |
hta@webrtc.org | 37bf584 | 2013-04-06 10:05:55 +0000 | [diff] [blame^] | 305 | <table> |
| 306 | <tr> |
| 307 | <th>Sender side<th>Receiver side |
| 308 | <tr> |
| 309 | <td align="top"><div id="senderstats">Stats will appear here.</div> |
| 310 | <td align="top"><div id="receiverstats">Stats will appear here.</div> |
| 311 | </table> |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 312 | </body> |
| 313 | </html> |