toolkit/content/aboutWebrtc.xhtml
author Ben Tian <btian@mozilla.com>
Mon, 16 Jun 2014 16:47:11 +0800
changeset 211107 097125a96d846a085d7cbf6b017c133eb4a88a19
parent 210418 33aad7e2e1c37c0f3fe72ae148835f88339fee7c
permissions -rw-r--r--
Bug 1020736 - Wrap adapter's new properties for BluetoothManager::GetAdapters call in BluetoothServiceBluedroid, r=echou

<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->


<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Webrtc Internals</title>
  </head>
  <script>


function displayLogs(logs) {
  var logsDiv = document.getElementById('logs');
  while (logsDiv.lastChild) {
    logsDiv.removeChild(logsDiv.lastChild);
  }
  logsDiv.appendChild(document.createElement('h3'))
         .appendChild(document.createTextNode('Logging:'));
  logs.forEach(function(logLine){
    logsDiv.appendChild(document.createElement('div'))
           .appendChild(document.createTextNode(logLine));
  });
}

function candidateTypeString(cand) {
  if (cand.type == "localcandidate") {
    if (cand.candidateType == "relayed") {
      return cand.candidateType + '-' + cand.mozLocalTransport;
    }
  }
  return cand.candidateType;
}

function candidateAddrString(cand) {
  return cand.ipAddress + ':' +
         cand.portNumber + '/' +
         cand.transport + '(' +
         candidateTypeString(cand) + ')';
}

function buildCandPairTableRow(candPair, localCand, remoteCand) {
  var row = document.createElement('tr');
  row.onclick = function() {
    WebrtcGlobalInformation.getLogging("CAND-PAIR(" + row.id, displayLogs);
  }

  if (localCand) {
    row.appendChild(document.createElement('td'))
       .appendChild(document.createTextNode(candidateAddrString(localCand)));
  } else {
    row.appendChild(document.createElement('td'))
       .appendChild(document.createTextNode(candPair.localCandidateId));
  }

  if (remoteCand) {
    row.appendChild(document.createElement('td'))
       .appendChild(document.createTextNode(candidateAddrString(remoteCand)));
  } else {
    row.appendChild(document.createElement('td'))
       .appendChild(document.createTextNode(candPair.remoteCandidateId));
  }

  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(candPair.state));
  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(candPair.mozPriority));

  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(candPair.nominated ? '*' : ''));
  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(candPair.selected ? '*' : ''));
  return row;
}

function buildCandTableRow(cand) {
  var row = document.createElement('tr');

  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(cand.ipAddress + ':' +
                                          cand.portNumber + '/' +
                                          cand.transport));

  row.appendChild(document.createElement('td'))
     .appendChild(document.createTextNode(candidateTypeString(cand)));
  return row;
}

function buildCandPairTableHeader() {
  var headerRow = document.createElement('tr');
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Local candidate'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Remote candidate'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('ICE State'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Priority'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Nominated'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Selected'));
  return headerRow;
}

function buildCandTableHeader(isLocal) {
  var headerRow = document.createElement('tr');
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode(isLocal ?
                                                'Local candidate addr' :
                                                'Remote candidate addr'));
  headerRow.appendChild(document.createElement('th'))
           .appendChild(document.createTextNode('Type'));
  return headerRow;
}

function buildEmptyCandPairTable() {
  var candPairTable = document.createElement('table');
  candPairTable.appendChild(buildCandPairTableHeader());
  return candPairTable;
}

function buildEmptyCandTable(local) {
  var candTable = document.createElement('table');
  candTable.appendChild(buildCandTableHeader(local));
  return candTable;
}

function round00(num) {
  return Math.round(num * 100) / 100;
}

function dumpAvStat(stat) {
  var div = document.createElement('div');
  var statsString = "";
  if (stat.mozAvSyncDelay !== undefined) {
    statsString += "A/V sync: " + stat.mozAvSyncDelay + " ms ";
  }
  if (stat.mozJitterBufferDelay !== undefined) {
    statsString += "Jitter-buffer delay: " + stat.mozJitterBufferDelay + " ms";
  }
  div.appendChild(document.createTextNode(statsString));
  return div;
}

function dumpRtpStat(stat, label) {
  var div = document.createElement('div');
  var statsString = " " + label + new Date(stat.timestamp).toTimeString() +
                    " " + stat.type + " SSRC: " + stat.ssrc;
  if (stat.packetsReceived !== undefined) {
    statsString += " Received: " + stat.packetsReceived + " packets";
    if (stat.bytesReceived !== undefined) {
      statsString += " (" + round00(stat.bytesReceived/1024) + " Kb)";
    }
    statsString += " Lost: " + stat.packetsLost + " Jitter: " + stat.jitter;
    if (stat.mozRtt !== undefined) {
      statsString += " RTT: " + stat.mozRtt + " ms";
    }
  } else if (stat.packetsSent !== undefined) {
    statsString += " Sent: " + stat.packetsSent + " packets";
    if (stat.bytesSent !== undefined) {
      statsString += " (" + round00(stat.bytesSent/1024) + " Kb)";
    }
  }
  div.appendChild(document.createTextNode(statsString));
  return div;
}

function dumpCoderStat(stat) {
  var div = document.createElement('div');
  if (stat.bitrateMean !== undefined ||
      stat.framerateMean !== undefined ||
      stat.droppedFrames !== undefined ||
      stat.discardedPackets !== undefined) {
    var statsString = (stat.packetsReceived !== undefined)? " Decoder:" : " Encoder:";
    if (stat.bitrateMean !== undefined) {
      statsString += " Avg. bitrate: " + (stat.bitrateMean/1000000).toFixed(2) + " Mbps";
      if (stat.bitrateStdDev !== undefined) {
        statsString += " (" + (stat.bitrateStdDev/1000000).toFixed(2) + " SD)";
      }
    }
    if (stat.framerateMean !== undefined) {
      statsString += " Avg. framerate: " + (stat.framerateMean).toFixed(2) + " fps";
      if (stat.framerateStdDev !== undefined) {
        statsString += " (" + stat.framerateStdDev.toFixed(2) + " SD)";
      }
    }
    if (stat.droppedFrames !== undefined) {
      statsString += " Dropped frames: " + stat.droppedFrames;
    }
    if (stat.discardedPackets !== undefined) {
      statsString += " Discarded packets: " + stat.discardedPackets;
    }
    div.appendChild(document.createTextNode(statsString));
  }
  return div;
}

function buildPcDiv(stats, pcDivHeading) {
  var newPcDiv = document.createElement('div');

  var heading = document.createElement('h3');

  if (stats.closed) {
    heading.appendChild(document.createTextNode("Closed "));
  }

  heading.appendChild(document.createTextNode(pcDivHeading));

  heading.appendChild(document.createTextNode(" " +
    new Date(stats.timestamp).toTimeString()));

  newPcDiv.appendChild(heading);

  // First, ICE stats
  var iceHeading = document.createElement('h4');
  iceHeading.appendChild(document.createTextNode("ICE statistics"));
  newPcDiv.appendChild(iceHeading);

  var iceTablesByComponent = {};

  function getIceTables(componentId) {
    if (!iceTablesByComponent[componentId]) {
      iceTablesByComponent[componentId] = {
        candidatePairTable: buildEmptyCandPairTable(),
        localCandidateTable: buildEmptyCandTable(true),
        remoteCandidateTable: buildEmptyCandTable(false)
      };
    }
    return iceTablesByComponent[componentId];
  }

  // Candidates
  var candidateMap = {}; // Used later to speed up recording of candidate pairs

  if (stats.iceCandidateStats) {
    stats.iceCandidateStats.forEach(function(cand) {
        var tables = getIceTables(cand.componentId);

        candidateMap[cand.id] = cand;

        if (cand.type == "localcandidate") {
          tables.localCandidateTable.appendChild(buildCandTableRow(cand));
        } else {
          tables.remoteCandidateTable.appendChild(buildCandTableRow(cand));
        }
    });
  }

  // Candidate pairs
  if (stats.iceCandidatePairStats) {
    stats.iceCandidatePairStats.forEach(function(candPair) {
      var candPairTable =
        getIceTables(candPair.componentId).candidatePairTable;
      candPairTable.appendChild(
          buildCandPairTableRow(candPair,
                                candidateMap[candPair.localCandidateId],
                                candidateMap[candPair.remoteCandidateId]));
    });
  }

  // Now that tables are completely built, put them on the page.
  for (var cid in iceTablesByComponent) {
    if (iceTablesByComponent.hasOwnProperty(cid)) {
      var tables = iceTablesByComponent[cid];
      newPcDiv.appendChild(document.createElement('h4'))
              .appendChild(document.createTextNode(cid));
      newPcDiv.appendChild(tables.candidatePairTable);
      newPcDiv.appendChild(tables.localCandidateTable);
      newPcDiv.appendChild(tables.remoteCandidateTable);
    }
  }

  // end of ICE stats

  // Now, SDP
  var localSdpHeading = document.createElement('h4');
  localSdpHeading.appendChild(document.createTextNode("Local SDP"));
  newPcDiv.appendChild(localSdpHeading);

  var localSdpDiv = document.createElement('pre');
  localSdpDiv.appendChild(document.createTextNode(stats.localSdp));

  newPcDiv.appendChild(localSdpDiv);

  var remoteSdpHeading = document.createElement('h4');
  remoteSdpHeading.appendChild(document.createTextNode("Remote SDP"));
  newPcDiv.appendChild(remoteSdpHeading);

  var remoteSdpDiv = document.createElement('pre');
  remoteSdpDiv.appendChild(document.createTextNode(stats.remoteSdp));

  newPcDiv.appendChild(remoteSdpDiv);
  // End of SDP

  // Now, RTP stats
  var rtpHeading = document.createElement('h4');
  rtpHeading.appendChild(document.createTextNode("RTP statistics"));
  newPcDiv.appendChild(rtpHeading);

  // Build map from id -> remote RTP stats (ie; stats obtained from RTCP
  // from the other end). This allows us to pair up local/remote stats for
  // the same stream more easily.
  var remoteRtpStatsMap = {};

  var addRemoteStatToMap = function (rtpStat) {
    if (rtpStat.isRemote) {
      remoteRtpStatsMap[rtpStat.id] = rtpStat;
    }
  }

  if (stats.inboundRTPStreamStats) {
    stats.inboundRTPStreamStats.forEach(addRemoteStatToMap);
  }

  if (stats.outboundRTPStreamStats) {
    stats.outboundRTPStreamStats.forEach(addRemoteStatToMap);
  }

  var addRtpStatPairToDocument = function (rtpStat) {
    if (!rtpStat.isRemote) {
      newPcDiv.appendChild(document.createElement('h5'))
              .appendChild(document.createTextNode(rtpStat.id));
      if (rtpStat.mozAvSyncDelay !== undefined ||
          rtpStat.mozJitterBufferDelay !== undefined) {
        newPcDiv.appendChild(dumpAvStat(rtpStat));
      }
      newPcDiv.appendChild(dumpCoderStat(rtpStat));
      newPcDiv.appendChild(dumpRtpStat(rtpStat, "Local: "));

      // Might not be receiving RTCP, so we have no idea what the
      // statistics look like from the perspective of the other end.
      if (rtpStat.remoteId) {
        var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId];
        newPcDiv.appendChild(dumpRtpStat(remoteRtpStat, "Remote: "));
      }
    }
  }

  if (stats.outboundRTPStreamStats) {
    stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument);
  }

  if (stats.inboundRTPStreamStats) {
    stats.inboundRTPStreamStats.forEach(addRtpStatPairToDocument);
  }

  return newPcDiv;
}

function displayStats(globalReport) {
  console.log("Got stats callback.");
  globalReport.reports.forEach(function (report) {
    var pcDivHeading = 'PeerConnection:' + report.pcid;

    var pcDiv = document.getElementById(pcDivHeading);
    var newPcDiv = buildPcDiv(report, pcDivHeading);
    newPcDiv.id = pcDivHeading;

    if (!pcDiv) {
      document.getElementById('stats').appendChild(newPcDiv);
    } else {
      document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
    }
  });
}

function onLoad() {
  WebrtcGlobalInformation.getAllStats(displayStats);
  if (WebrtcGlobalInformation.debugLevel) {
    setDebugButton(true);
  } else {
    setDebugButton(false);
  }
  if (WebrtcGlobalInformation.aecDebug) {
    setAECDebugButton(true);
  } else {
    setAECDebugButton(false);
  }
}

function startDebugMode() {
  WebrtcGlobalInformation.debugLevel = 65535;
  setDebugButton(true);
}

function stopDebugMode() {
  WebrtcGlobalInformation.debugLevel = 0;
  setDebugButton(false);
}

function setDebugButton(on) {
  var button = document.getElementById("debug-toggle-button");
  button.innerHTML = on ? "Stop debug mode" : "Start debug mode";
  button.onclick = on ? stopDebugMode : startDebugMode;
}

function startAECDebugMode() {
  WebrtcGlobalInformation.aecDebug = true;
  setAECDebugButton(true);
}

function stopAECDebugMode() {
  WebrtcGlobalInformation.aecDebug = false;
  setAECDebugButton(false);
}

function setAECDebugButton(on) {
  var button = document.getElementById("aec-debug-toggle-button");
  button.innerHTML = on ? "Stop AEC logging" : "Start AEC logging";
  button.onclick = on ? stopAECDebugMode : startAECDebugMode;
}



  </script>

  <body id="body" onload="onLoad()">
    <div id="stats">
    </div>
    <button onclick="WebrtcGlobalInformation.getLogging('', displayLogs)">
      Connection log
    </button>
    <button id="debug-toggle-button" onclick="startDebugMode()">
      Start debug mode
    </button>
    <button id="aec-debug-toggle-button" onclick="startAECDebugMode()">
      Start AEC logging
    </button>
    <div id="logs">
    </div>
  </body>
</html>
<!-- vim: softtabstop=2:shiftwidth=2:expandtab
-->