Bumping manifests a=b2g-bump
[gecko.git] / toolkit / content / aboutWebrtc.xhtml
blobe60f75ef9faff53a06b130e58aab21da719e5899
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
3 - License, v. 2.0. If a copy of the MPL was not distributed with this
4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
7 <!DOCTYPE html [
8 <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
9 ]>
11 <html xmlns="http://www.w3.org/1999/xhtml">
12 <head>
13 <title>Webrtc Internals</title>
14 </head>
15 <script>
18 function displayLogs(logs) {
19 var logsDiv = document.getElementById('logs');
20 while (logsDiv.lastChild) {
21 logsDiv.removeChild(logsDiv.lastChild);
23 logsDiv.appendChild(document.createElement('h3'))
24 .appendChild(document.createTextNode('Logging:'));
25 logs.forEach(function(logLine){
26 logsDiv.appendChild(document.createElement('div'))
27 .appendChild(document.createTextNode(logLine));
28 });
31 function candidateTypeString(cand) {
32 if (cand.type == "localcandidate") {
33 if (cand.candidateType == "relayed") {
34 return cand.candidateType + '-' + cand.mozLocalTransport;
37 return cand.candidateType;
40 function candidateAddrString(cand) {
41 return cand.ipAddress + ':' +
42 cand.portNumber + '/' +
43 cand.transport + '(' +
44 candidateTypeString(cand) + ')';
47 function buildCandPairTableRow(candPair, localCand, remoteCand) {
48 var row = document.createElement('tr');
49 row.onclick = function() {
50 WebrtcGlobalInformation.getLogging("CAND-PAIR(" + row.id, displayLogs);
53 if (localCand) {
54 row.appendChild(document.createElement('td'))
55 .appendChild(document.createTextNode(candidateAddrString(localCand)));
56 } else {
57 row.appendChild(document.createElement('td'))
58 .appendChild(document.createTextNode(candPair.localCandidateId));
61 if (remoteCand) {
62 row.appendChild(document.createElement('td'))
63 .appendChild(document.createTextNode(candidateAddrString(remoteCand)));
64 } else {
65 row.appendChild(document.createElement('td'))
66 .appendChild(document.createTextNode(candPair.remoteCandidateId));
69 row.appendChild(document.createElement('td'))
70 .appendChild(document.createTextNode(candPair.state));
71 row.appendChild(document.createElement('td'))
72 .appendChild(document.createTextNode(candPair.mozPriority));
74 row.appendChild(document.createElement('td'))
75 .appendChild(document.createTextNode(candPair.nominated ? '*' : ''));
76 row.appendChild(document.createElement('td'))
77 .appendChild(document.createTextNode(candPair.selected ? '*' : ''));
78 return row;
81 function buildCandTableRow(cand) {
82 var row = document.createElement('tr');
84 row.appendChild(document.createElement('td'))
85 .appendChild(document.createTextNode(cand.ipAddress + ':' +
86 cand.portNumber + '/' +
87 cand.transport));
89 row.appendChild(document.createElement('td'))
90 .appendChild(document.createTextNode(candidateTypeString(cand)));
91 return row;
94 function buildCandPairTableHeader() {
95 var headerRow = document.createElement('tr');
96 headerRow.appendChild(document.createElement('th'))
97 .appendChild(document.createTextNode('Local candidate'));
98 headerRow.appendChild(document.createElement('th'))
99 .appendChild(document.createTextNode('Remote candidate'));
100 headerRow.appendChild(document.createElement('th'))
101 .appendChild(document.createTextNode('ICE State'));
102 headerRow.appendChild(document.createElement('th'))
103 .appendChild(document.createTextNode('Priority'));
104 headerRow.appendChild(document.createElement('th'))
105 .appendChild(document.createTextNode('Nominated'));
106 headerRow.appendChild(document.createElement('th'))
107 .appendChild(document.createTextNode('Selected'));
108 return headerRow;
111 function buildCandTableHeader(isLocal) {
112 var headerRow = document.createElement('tr');
113 headerRow.appendChild(document.createElement('th'))
114 .appendChild(document.createTextNode(isLocal ?
115 'Local candidate addr' :
116 'Remote candidate addr'));
117 headerRow.appendChild(document.createElement('th'))
118 .appendChild(document.createTextNode('Type'));
119 return headerRow;
122 function buildEmptyCandPairTable() {
123 var candPairTable = document.createElement('table');
124 candPairTable.appendChild(buildCandPairTableHeader());
125 return candPairTable;
128 function buildEmptyCandTable(local) {
129 var candTable = document.createElement('table');
130 candTable.appendChild(buildCandTableHeader(local));
131 return candTable;
134 function round00(num) {
135 return Math.round(num * 100) / 100;
138 function dumpAvStat(stat) {
139 var div = document.createElement('div');
140 var statsString = "";
141 if (stat.mozAvSyncDelay !== undefined) {
142 statsString += "A/V sync: " + stat.mozAvSyncDelay + " ms ";
144 if (stat.mozJitterBufferDelay !== undefined) {
145 statsString += "Jitter-buffer delay: " + stat.mozJitterBufferDelay + " ms";
147 div.appendChild(document.createTextNode(statsString));
148 return div;
151 function dumpRtpStat(stat, label) {
152 var div = document.createElement('div');
153 var statsString = " " + label + new Date(stat.timestamp).toTimeString() +
154 " " + stat.type + " SSRC: " + stat.ssrc;
155 if (stat.packetsReceived !== undefined) {
156 statsString += " Received: " + stat.packetsReceived + " packets";
157 if (stat.bytesReceived !== undefined) {
158 statsString += " (" + round00(stat.bytesReceived/1024) + " Kb)";
160 statsString += " Lost: " + stat.packetsLost + " Jitter: " + stat.jitter;
161 if (stat.mozRtt !== undefined) {
162 statsString += " RTT: " + stat.mozRtt + " ms";
164 } else if (stat.packetsSent !== undefined) {
165 statsString += " Sent: " + stat.packetsSent + " packets";
166 if (stat.bytesSent !== undefined) {
167 statsString += " (" + round00(stat.bytesSent/1024) + " Kb)";
170 div.appendChild(document.createTextNode(statsString));
171 return div;
174 function dumpCoderStat(stat) {
175 var div = document.createElement('div');
176 if (stat.bitrateMean !== undefined ||
177 stat.framerateMean !== undefined ||
178 stat.droppedFrames !== undefined ||
179 stat.discardedPackets !== undefined) {
180 var statsString = (stat.packetsReceived !== undefined)? " Decoder:" : " Encoder:";
181 if (stat.bitrateMean !== undefined) {
182 statsString += " Avg. bitrate: " + (stat.bitrateMean/1000000).toFixed(2) + " Mbps";
183 if (stat.bitrateStdDev !== undefined) {
184 statsString += " (" + (stat.bitrateStdDev/1000000).toFixed(2) + " SD)";
187 if (stat.framerateMean !== undefined) {
188 statsString += " Avg. framerate: " + (stat.framerateMean).toFixed(2) + " fps";
189 if (stat.framerateStdDev !== undefined) {
190 statsString += " (" + stat.framerateStdDev.toFixed(2) + " SD)";
193 if (stat.droppedFrames !== undefined) {
194 statsString += " Dropped frames: " + stat.droppedFrames;
196 if (stat.discardedPackets !== undefined) {
197 statsString += " Discarded packets: " + stat.discardedPackets;
199 div.appendChild(document.createTextNode(statsString));
201 return div;
204 function buildPcDiv(stats, pcDivHeading) {
205 var newPcDiv = document.createElement('div');
207 var heading = document.createElement('h3');
209 if (stats.closed) {
210 heading.appendChild(document.createTextNode("Closed "));
213 heading.appendChild(document.createTextNode(pcDivHeading));
215 heading.appendChild(document.createTextNode(" " +
216 new Date(stats.timestamp).toTimeString()));
218 newPcDiv.appendChild(heading);
220 // First, ICE stats
221 var iceHeading = document.createElement('h4');
222 iceHeading.appendChild(document.createTextNode("ICE statistics"));
223 newPcDiv.appendChild(iceHeading);
225 var iceTablesByComponent = {};
227 function getIceTables(componentId) {
228 if (!iceTablesByComponent[componentId]) {
229 iceTablesByComponent[componentId] = {
230 candidatePairTable: buildEmptyCandPairTable(),
231 localCandidateTable: buildEmptyCandTable(true),
232 remoteCandidateTable: buildEmptyCandTable(false)
235 return iceTablesByComponent[componentId];
238 // Candidates
239 var candidateMap = {}; // Used later to speed up recording of candidate pairs
241 if (stats.iceCandidateStats) {
242 stats.iceCandidateStats.forEach(function(cand) {
243 var tables = getIceTables(cand.componentId);
245 candidateMap[cand.id] = cand;
247 if (cand.type == "localcandidate") {
248 tables.localCandidateTable.appendChild(buildCandTableRow(cand));
249 } else {
250 tables.remoteCandidateTable.appendChild(buildCandTableRow(cand));
255 // Candidate pairs
256 if (stats.iceCandidatePairStats) {
257 stats.iceCandidatePairStats.forEach(function(candPair) {
258 var candPairTable =
259 getIceTables(candPair.componentId).candidatePairTable;
260 candPairTable.appendChild(
261 buildCandPairTableRow(candPair,
262 candidateMap[candPair.localCandidateId],
263 candidateMap[candPair.remoteCandidateId]));
267 // Now that tables are completely built, put them on the page.
268 for (var cid in iceTablesByComponent) {
269 if (iceTablesByComponent.hasOwnProperty(cid)) {
270 var tables = iceTablesByComponent[cid];
271 newPcDiv.appendChild(document.createElement('h4'))
272 .appendChild(document.createTextNode(cid));
273 newPcDiv.appendChild(tables.candidatePairTable);
274 newPcDiv.appendChild(tables.localCandidateTable);
275 newPcDiv.appendChild(tables.remoteCandidateTable);
279 // end of ICE stats
281 // Now, SDP
282 var localSdpHeading = document.createElement('h4');
283 localSdpHeading.appendChild(document.createTextNode("Local SDP"));
284 newPcDiv.appendChild(localSdpHeading);
286 var localSdpDiv = document.createElement('pre');
287 localSdpDiv.appendChild(document.createTextNode(stats.localSdp));
289 newPcDiv.appendChild(localSdpDiv);
291 var remoteSdpHeading = document.createElement('h4');
292 remoteSdpHeading.appendChild(document.createTextNode("Remote SDP"));
293 newPcDiv.appendChild(remoteSdpHeading);
295 var remoteSdpDiv = document.createElement('pre');
296 remoteSdpDiv.appendChild(document.createTextNode(stats.remoteSdp));
298 newPcDiv.appendChild(remoteSdpDiv);
299 // End of SDP
301 // Now, RTP stats
302 var rtpHeading = document.createElement('h4');
303 rtpHeading.appendChild(document.createTextNode("RTP statistics"));
304 newPcDiv.appendChild(rtpHeading);
306 // Build map from id -> remote RTP stats (ie; stats obtained from RTCP
307 // from the other end). This allows us to pair up local/remote stats for
308 // the same stream more easily.
309 var remoteRtpStatsMap = {};
311 var addRemoteStatToMap = function (rtpStat) {
312 if (rtpStat.isRemote) {
313 remoteRtpStatsMap[rtpStat.id] = rtpStat;
317 if (stats.inboundRTPStreamStats) {
318 stats.inboundRTPStreamStats.forEach(addRemoteStatToMap);
321 if (stats.outboundRTPStreamStats) {
322 stats.outboundRTPStreamStats.forEach(addRemoteStatToMap);
325 var addRtpStatPairToDocument = function (rtpStat) {
326 if (!rtpStat.isRemote) {
327 newPcDiv.appendChild(document.createElement('h5'))
328 .appendChild(document.createTextNode(rtpStat.id));
329 if (rtpStat.mozAvSyncDelay !== undefined ||
330 rtpStat.mozJitterBufferDelay !== undefined) {
331 newPcDiv.appendChild(dumpAvStat(rtpStat));
333 newPcDiv.appendChild(dumpCoderStat(rtpStat));
334 newPcDiv.appendChild(dumpRtpStat(rtpStat, "Local: "));
336 // Might not be receiving RTCP, so we have no idea what the
337 // statistics look like from the perspective of the other end.
338 if (rtpStat.remoteId) {
339 var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId];
340 newPcDiv.appendChild(dumpRtpStat(remoteRtpStat, "Remote: "));
345 if (stats.outboundRTPStreamStats) {
346 stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument);
349 if (stats.inboundRTPStreamStats) {
350 stats.inboundRTPStreamStats.forEach(addRtpStatPairToDocument);
353 return newPcDiv;
356 function displayStats(globalReport) {
357 console.log("Got stats callback.");
358 globalReport.reports.forEach(function (report) {
359 var pcDivHeading = 'PeerConnection:' + report.pcid;
361 var pcDiv = document.getElementById(pcDivHeading);
362 var newPcDiv = buildPcDiv(report, pcDivHeading);
363 newPcDiv.id = pcDivHeading;
365 if (!pcDiv) {
366 document.getElementById('stats').appendChild(newPcDiv);
367 } else {
368 document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
373 function onLoad() {
374 WebrtcGlobalInformation.getAllStats(displayStats);
375 if (WebrtcGlobalInformation.debugLevel) {
376 setDebugButton(true);
377 } else {
378 setDebugButton(false);
380 if (WebrtcGlobalInformation.aecDebug) {
381 setAECDebugButton(true);
382 } else {
383 setAECDebugButton(false);
387 function startDebugMode() {
388 WebrtcGlobalInformation.debugLevel = 65535;
389 setDebugButton(true);
392 function stopDebugMode() {
393 WebrtcGlobalInformation.debugLevel = 0;
394 setDebugButton(false);
397 function setDebugButton(on) {
398 var button = document.getElementById("debug-toggle-button");
399 button.innerHTML = on ? "Stop debug mode" : "Start debug mode";
400 button.onclick = on ? stopDebugMode : startDebugMode;
403 function startAECDebugMode() {
404 WebrtcGlobalInformation.aecDebug = true;
405 setAECDebugButton(true);
408 function stopAECDebugMode() {
409 WebrtcGlobalInformation.aecDebug = false;
410 setAECDebugButton(false);
413 function setAECDebugButton(on) {
414 var button = document.getElementById("aec-debug-toggle-button");
415 button.innerHTML = on ? "Stop AEC logging" : "Start AEC logging";
416 button.onclick = on ? stopAECDebugMode : startAECDebugMode;
421 </script>
423 <body id="body" onload="onLoad()">
424 <div id="stats">
425 </div>
426 <button onclick="WebrtcGlobalInformation.getLogging('', displayLogs)">
427 Connection log
428 </button>
429 <button id="debug-toggle-button" onclick="startDebugMode()">
430 Start debug mode
431 </button>
432 <button id="aec-debug-toggle-button" onclick="startAECDebugMode()">
433 Start AEC logging
434 </button>
435 <div id="logs">
436 </div>
437 </body>
438 </html>
439 <!-- vim: softtabstop=2:shiftwidth=2:expandtab