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/. -->
8 <!ENTITY % htmlDTD PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
11 <html xmlns=
"http://www.w3.org/1999/xhtml">
13 <title>Webrtc Internals
</title>
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));
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);
54 row.appendChild(document.createElement('td'))
55 .appendChild(document.createTextNode(candidateAddrString(localCand)));
57 row.appendChild(document.createElement('td'))
58 .appendChild(document.createTextNode(candPair.localCandidateId));
62 row.appendChild(document.createElement('td'))
63 .appendChild(document.createTextNode(candidateAddrString(remoteCand)));
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 ? '*' : ''));
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 + '/' +
89 row.appendChild(document.createElement('td'))
90 .appendChild(document.createTextNode(candidateTypeString(cand)));
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'));
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'));
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));
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));
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));
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));
204 function buildPcDiv(stats, pcDivHeading) {
205 var newPcDiv = document.createElement('div');
207 var heading = document.createElement('h3');
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);
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];
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));
250 tables.remoteCandidateTable.appendChild(buildCandTableRow(cand));
256 if (stats.iceCandidatePairStats) {
257 stats.iceCandidatePairStats.forEach(function(candPair) {
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);
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);
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);
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;
366 document.getElementById('stats').appendChild(newPcDiv);
368 document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
374 WebrtcGlobalInformation.getAllStats(displayStats);
375 if (WebrtcGlobalInformation.debugLevel) {
376 setDebugButton(true);
378 setDebugButton(false);
380 if (WebrtcGlobalInformation.aecDebug) {
381 setAECDebugButton(true);
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;
423 <body id=
"body" onload=
"onLoad()">
426 <button onclick=
"WebrtcGlobalInformation.getLogging('', displayLogs)">
429 <button id=
"debug-toggle-button" onclick=
"startDebugMode()">
432 <button id=
"aec-debug-toggle-button" onclick=
"startAECDebugMode()">
439 <!-- vim: softtabstop=2:shiftwidth=2:expandtab