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