Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
michael@0 | 2 | <!-- This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | - License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
michael@0 | 5 | |
michael@0 | 6 | |
michael@0 | 7 | <!DOCTYPE html [ |
michael@0 | 8 | <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD; |
michael@0 | 9 | ]> |
michael@0 | 10 | |
michael@0 | 11 | <html xmlns="http://www.w3.org/1999/xhtml"> |
michael@0 | 12 | <head> |
michael@0 | 13 | <title>Webrtc Internals</title> |
michael@0 | 14 | </head> |
michael@0 | 15 | <script> |
michael@0 | 16 | |
michael@0 | 17 | |
michael@0 | 18 | function displayLogs(logs) { |
michael@0 | 19 | var logsDiv = document.getElementById('logs'); |
michael@0 | 20 | while (logsDiv.lastChild) { |
michael@0 | 21 | logsDiv.removeChild(logsDiv.lastChild); |
michael@0 | 22 | } |
michael@0 | 23 | logsDiv.appendChild(document.createElement('h3')) |
michael@0 | 24 | .appendChild(document.createTextNode('Logging:')); |
michael@0 | 25 | logs.forEach(function(logLine){ |
michael@0 | 26 | logsDiv.appendChild(document.createElement('div')) |
michael@0 | 27 | .appendChild(document.createTextNode(logLine)); |
michael@0 | 28 | }); |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | function candidateTypeString(cand) { |
michael@0 | 32 | if (cand.type == "localcandidate") { |
michael@0 | 33 | if (cand.candidateType == "relayed") { |
michael@0 | 34 | return cand.candidateType + '-' + cand.mozLocalTransport; |
michael@0 | 35 | } |
michael@0 | 36 | } |
michael@0 | 37 | return cand.candidateType; |
michael@0 | 38 | } |
michael@0 | 39 | |
michael@0 | 40 | function candidateAddrString(cand) { |
michael@0 | 41 | return cand.ipAddress + ':' + |
michael@0 | 42 | cand.portNumber + '/' + |
michael@0 | 43 | cand.transport + '(' + |
michael@0 | 44 | candidateTypeString(cand) + ')'; |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | function buildCandPairTableRow(candPair, localCand, remoteCand) { |
michael@0 | 48 | var row = document.createElement('tr'); |
michael@0 | 49 | row.onclick = function() { |
michael@0 | 50 | WebrtcGlobalInformation.getLogging("CAND-PAIR(" + row.id, displayLogs); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | if (localCand) { |
michael@0 | 54 | row.appendChild(document.createElement('td')) |
michael@0 | 55 | .appendChild(document.createTextNode(candidateAddrString(localCand))); |
michael@0 | 56 | } else { |
michael@0 | 57 | row.appendChild(document.createElement('td')) |
michael@0 | 58 | .appendChild(document.createTextNode(candPair.localCandidateId)); |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | if (remoteCand) { |
michael@0 | 62 | row.appendChild(document.createElement('td')) |
michael@0 | 63 | .appendChild(document.createTextNode(candidateAddrString(remoteCand))); |
michael@0 | 64 | } else { |
michael@0 | 65 | row.appendChild(document.createElement('td')) |
michael@0 | 66 | .appendChild(document.createTextNode(candPair.remoteCandidateId)); |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | row.appendChild(document.createElement('td')) |
michael@0 | 70 | .appendChild(document.createTextNode(candPair.state)); |
michael@0 | 71 | row.appendChild(document.createElement('td')) |
michael@0 | 72 | .appendChild(document.createTextNode(candPair.mozPriority)); |
michael@0 | 73 | |
michael@0 | 74 | row.appendChild(document.createElement('td')) |
michael@0 | 75 | .appendChild(document.createTextNode(candPair.nominated ? '*' : '')); |
michael@0 | 76 | row.appendChild(document.createElement('td')) |
michael@0 | 77 | .appendChild(document.createTextNode(candPair.selected ? '*' : '')); |
michael@0 | 78 | return row; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | function buildCandTableRow(cand) { |
michael@0 | 82 | var row = document.createElement('tr'); |
michael@0 | 83 | |
michael@0 | 84 | row.appendChild(document.createElement('td')) |
michael@0 | 85 | .appendChild(document.createTextNode(cand.ipAddress + ':' + |
michael@0 | 86 | cand.portNumber + '/' + |
michael@0 | 87 | cand.transport)); |
michael@0 | 88 | |
michael@0 | 89 | row.appendChild(document.createElement('td')) |
michael@0 | 90 | .appendChild(document.createTextNode(candidateTypeString(cand))); |
michael@0 | 91 | return row; |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | function buildCandPairTableHeader() { |
michael@0 | 95 | var headerRow = document.createElement('tr'); |
michael@0 | 96 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 97 | .appendChild(document.createTextNode('Local candidate')); |
michael@0 | 98 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 99 | .appendChild(document.createTextNode('Remote candidate')); |
michael@0 | 100 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 101 | .appendChild(document.createTextNode('ICE State')); |
michael@0 | 102 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 103 | .appendChild(document.createTextNode('Priority')); |
michael@0 | 104 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 105 | .appendChild(document.createTextNode('Nominated')); |
michael@0 | 106 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 107 | .appendChild(document.createTextNode('Selected')); |
michael@0 | 108 | return headerRow; |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | function buildCandTableHeader(isLocal) { |
michael@0 | 112 | var headerRow = document.createElement('tr'); |
michael@0 | 113 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 114 | .appendChild(document.createTextNode(isLocal ? |
michael@0 | 115 | 'Local candidate addr' : |
michael@0 | 116 | 'Remote candidate addr')); |
michael@0 | 117 | headerRow.appendChild(document.createElement('th')) |
michael@0 | 118 | .appendChild(document.createTextNode('Type')); |
michael@0 | 119 | return headerRow; |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | function buildEmptyCandPairTable() { |
michael@0 | 123 | var candPairTable = document.createElement('table'); |
michael@0 | 124 | candPairTable.appendChild(buildCandPairTableHeader()); |
michael@0 | 125 | return candPairTable; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | function buildEmptyCandTable(local) { |
michael@0 | 129 | var candTable = document.createElement('table'); |
michael@0 | 130 | candTable.appendChild(buildCandTableHeader(local)); |
michael@0 | 131 | return candTable; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | function round00(num) { |
michael@0 | 135 | return Math.round(num * 100) / 100; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | function dumpAvStat(stat) { |
michael@0 | 139 | var div = document.createElement('div'); |
michael@0 | 140 | var statsString = ""; |
michael@0 | 141 | if (stat.mozAvSyncDelay !== undefined) { |
michael@0 | 142 | statsString += "A/V sync: " + stat.mozAvSyncDelay + " ms "; |
michael@0 | 143 | } |
michael@0 | 144 | if (stat.mozJitterBufferDelay !== undefined) { |
michael@0 | 145 | statsString += "Jitter-buffer delay: " + stat.mozJitterBufferDelay + " ms"; |
michael@0 | 146 | } |
michael@0 | 147 | div.appendChild(document.createTextNode(statsString)); |
michael@0 | 148 | return div; |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | function dumpRtpStat(stat, label) { |
michael@0 | 152 | var div = document.createElement('div'); |
michael@0 | 153 | var statsString = " " + label + new Date(stat.timestamp).toTimeString() + |
michael@0 | 154 | " " + stat.type + " SSRC: " + stat.ssrc; |
michael@0 | 155 | if (stat.packetsReceived !== undefined) { |
michael@0 | 156 | statsString += " Received: " + stat.packetsReceived + " packets"; |
michael@0 | 157 | if (stat.bytesReceived !== undefined) { |
michael@0 | 158 | statsString += " (" + round00(stat.bytesReceived/1024) + " Kb)"; |
michael@0 | 159 | } |
michael@0 | 160 | statsString += " Lost: " + stat.packetsLost + " Jitter: " + stat.jitter; |
michael@0 | 161 | if (stat.mozRtt !== undefined) { |
michael@0 | 162 | statsString += " RTT: " + stat.mozRtt + " ms"; |
michael@0 | 163 | } |
michael@0 | 164 | } else if (stat.packetsSent !== undefined) { |
michael@0 | 165 | statsString += " Sent: " + stat.packetsSent + " packets"; |
michael@0 | 166 | if (stat.bytesSent !== undefined) { |
michael@0 | 167 | statsString += " (" + round00(stat.bytesSent/1024) + " Kb)"; |
michael@0 | 168 | } |
michael@0 | 169 | } |
michael@0 | 170 | div.appendChild(document.createTextNode(statsString)); |
michael@0 | 171 | return div; |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | function buildPcDiv(stats, pcDivHeading) { |
michael@0 | 175 | var newPcDiv = document.createElement('div'); |
michael@0 | 176 | |
michael@0 | 177 | var heading = document.createElement('h3'); |
michael@0 | 178 | heading.appendChild(document.createTextNode(pcDivHeading)); |
michael@0 | 179 | newPcDiv.appendChild(heading); |
michael@0 | 180 | |
michael@0 | 181 | // First, ICE stats |
michael@0 | 182 | var iceHeading = document.createElement('h4'); |
michael@0 | 183 | iceHeading.appendChild(document.createTextNode("ICE statistics")); |
michael@0 | 184 | newPcDiv.appendChild(iceHeading); |
michael@0 | 185 | |
michael@0 | 186 | var iceTablesByComponent = {}; |
michael@0 | 187 | |
michael@0 | 188 | function getIceTables(componentId) { |
michael@0 | 189 | if (!iceTablesByComponent[componentId]) { |
michael@0 | 190 | iceTablesByComponent[componentId] = { |
michael@0 | 191 | candidatePairTable: buildEmptyCandPairTable(), |
michael@0 | 192 | localCandidateTable: buildEmptyCandTable(true), |
michael@0 | 193 | remoteCandidateTable: buildEmptyCandTable(false) |
michael@0 | 194 | }; |
michael@0 | 195 | } |
michael@0 | 196 | return iceTablesByComponent[componentId]; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | // Candidates |
michael@0 | 200 | var candidateMap = {}; // Used later to speed up recording of candidate pairs |
michael@0 | 201 | |
michael@0 | 202 | if (stats.iceCandidateStats) { |
michael@0 | 203 | stats.iceCandidateStats.forEach(function(cand) { |
michael@0 | 204 | var tables = getIceTables(cand.componentId); |
michael@0 | 205 | |
michael@0 | 206 | candidateMap[cand.id] = cand; |
michael@0 | 207 | |
michael@0 | 208 | if (cand.type == "localcandidate") { |
michael@0 | 209 | tables.localCandidateTable.appendChild(buildCandTableRow(cand)); |
michael@0 | 210 | } else { |
michael@0 | 211 | tables.remoteCandidateTable.appendChild(buildCandTableRow(cand)); |
michael@0 | 212 | } |
michael@0 | 213 | }); |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | // Candidate pairs |
michael@0 | 217 | if (stats.iceCandidatePairStats) { |
michael@0 | 218 | stats.iceCandidatePairStats.forEach(function(candPair) { |
michael@0 | 219 | var candPairTable = |
michael@0 | 220 | getIceTables(candPair.componentId).candidatePairTable; |
michael@0 | 221 | candPairTable.appendChild( |
michael@0 | 222 | buildCandPairTableRow(candPair, |
michael@0 | 223 | candidateMap[candPair.localCandidateId], |
michael@0 | 224 | candidateMap[candPair.remoteCandidateId])); |
michael@0 | 225 | }); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | // Now that tables are completely built, put them on the page. |
michael@0 | 229 | for (var cid in iceTablesByComponent) { |
michael@0 | 230 | if (iceTablesByComponent.hasOwnProperty(cid)) { |
michael@0 | 231 | var tables = iceTablesByComponent[cid]; |
michael@0 | 232 | newPcDiv.appendChild(document.createElement('h4')) |
michael@0 | 233 | .appendChild(document.createTextNode(cid)); |
michael@0 | 234 | newPcDiv.appendChild(tables.candidatePairTable); |
michael@0 | 235 | newPcDiv.appendChild(tables.localCandidateTable); |
michael@0 | 236 | newPcDiv.appendChild(tables.remoteCandidateTable); |
michael@0 | 237 | } |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | // end of ICE stats |
michael@0 | 241 | |
michael@0 | 242 | // Now, RTP stats |
michael@0 | 243 | var rtpHeading = document.createElement('h4'); |
michael@0 | 244 | rtpHeading.appendChild(document.createTextNode("RTP statistics")); |
michael@0 | 245 | newPcDiv.appendChild(rtpHeading); |
michael@0 | 246 | |
michael@0 | 247 | // Build map from id -> remote RTP stats (ie; stats obtained from RTCP |
michael@0 | 248 | // from the other end). This allows us to pair up local/remote stats for |
michael@0 | 249 | // the same stream more easily. |
michael@0 | 250 | var remoteRtpStatsMap = {}; |
michael@0 | 251 | |
michael@0 | 252 | var addRemoteStatToMap = function (rtpStat) { |
michael@0 | 253 | if (rtpStat.isRemote) { |
michael@0 | 254 | remoteRtpStatsMap[rtpStat.id] = rtpStat; |
michael@0 | 255 | } |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | if (stats.inboundRTPStreamStats) { |
michael@0 | 259 | stats.inboundRTPStreamStats.forEach(addRemoteStatToMap); |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | if (stats.outboundRTPStreamStats) { |
michael@0 | 263 | stats.outboundRTPStreamStats.forEach(addRemoteStatToMap); |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | var addRtpStatPairToDocument = function (rtpStat) { |
michael@0 | 267 | if (!rtpStat.isRemote) { |
michael@0 | 268 | newPcDiv.appendChild(document.createElement('h5')) |
michael@0 | 269 | .appendChild(document.createTextNode(rtpStat.id)); |
michael@0 | 270 | if (rtpStat.mozAvSyncDelay !== undefined || |
michael@0 | 271 | rtpStat.mozJitterBufferDelay !== undefined) { |
michael@0 | 272 | newPcDiv.appendChild(dumpAvStat(rtpStat)); |
michael@0 | 273 | } |
michael@0 | 274 | newPcDiv.appendChild(dumpRtpStat(rtpStat, "Local: ")); |
michael@0 | 275 | |
michael@0 | 276 | // Might not be receiving RTCP, so we have no idea what the |
michael@0 | 277 | // statistics look like from the perspective of the other end. |
michael@0 | 278 | if (rtpStat.remoteId) { |
michael@0 | 279 | var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId]; |
michael@0 | 280 | newPcDiv.appendChild(dumpRtpStat(remoteRtpStat, "Remote: ")); |
michael@0 | 281 | } |
michael@0 | 282 | } |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | if (stats.outboundRTPStreamStats) { |
michael@0 | 286 | stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | if (stats.inboundRTPStreamStats) { |
michael@0 | 290 | stats.inboundRTPStreamStats.forEach(addRtpStatPairToDocument); |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | return newPcDiv; |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | function displayStats(globalReport) { |
michael@0 | 297 | console.log("Got stats callback."); |
michael@0 | 298 | globalReport.reports.forEach(function (report) { |
michael@0 | 299 | var pcDivHeading = 'PeerConnection:' + report.pcid; |
michael@0 | 300 | |
michael@0 | 301 | var pcDiv = document.getElementById(pcDivHeading); |
michael@0 | 302 | var newPcDiv = buildPcDiv(report, pcDivHeading); |
michael@0 | 303 | newPcDiv.id = pcDivHeading; |
michael@0 | 304 | |
michael@0 | 305 | if (!pcDiv) { |
michael@0 | 306 | document.getElementById('stats').appendChild(newPcDiv); |
michael@0 | 307 | } else { |
michael@0 | 308 | document.getElementById('stats').replaceChild(newPcDiv, pcDiv); |
michael@0 | 309 | } |
michael@0 | 310 | }); |
michael@0 | 311 | |
michael@0 | 312 | globalReport.errors.forEach(function (error) { |
michael@0 | 313 | var pcDivHeading = 'PeerConnection:' + error.pcid; |
michael@0 | 314 | |
michael@0 | 315 | var pcDiv = document.getElementById(pcDivHeading); |
michael@0 | 316 | var newPcDiv = buildPcDiv(error, pcDivHeading); |
michael@0 | 317 | newPcDiv.id = pcDivHeading; |
michael@0 | 318 | |
michael@0 | 319 | if (pcDiv) { |
michael@0 | 320 | document.getElementById('stats').replaceChild(newPcDiv, pcDiv); |
michael@0 | 321 | } else { |
michael@0 | 322 | document.getElementById('stats').appendChild(newPcDiv); |
michael@0 | 323 | } |
michael@0 | 324 | }); |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | </script> |
michael@0 | 328 | |
michael@0 | 329 | <body id="body" onload="WebrtcGlobalInformation.getAllStats(displayStats)"> |
michael@0 | 330 | <div id="stats"> |
michael@0 | 331 | </div> |
michael@0 | 332 | <button onclick="WebrtcGlobalInformation.getLogging('', displayLogs)"> |
michael@0 | 333 | Show/refresh logging |
michael@0 | 334 | </button> |
michael@0 | 335 | <div id="logs"> |
michael@0 | 336 | </div> |
michael@0 | 337 | </body> |
michael@0 | 338 | </html> |
michael@0 | 339 | <!-- vim: softtabstop=2:shiftwidth=2:expandtab |
michael@0 | 340 | --> |