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