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