Bug 1380555 - refactor out isRemote; r?jib draft
authorNico Grunbaum
Wed, 12 Jul 2017 21:10:18 -0700
changeset 643445 d451d1c9c5cd6d9d057d92e7c2272778c8354d4a
parent 609865 e0b0865639cebc1b5afa0268a4b073fcdde0e69c
child 646184 0defc8dae91c0373a5221b6b9e8ee48da214d76f
child 649186 80839eb6f5856f7c8213c4df4a04cd15acd66455
push id73104
push userna-g@nostrum.com
push dateWed, 09 Aug 2017 19:23:27 +0000
reviewersjib
bugs1380555
milestone56.0a1
Bug 1380555 - refactor out isRemote; r?jib * Refactor inbound side in C++ MozReview-Commit-ID: 92maiTMhVjO
dom/media/PeerConnection.js
dom/media/RTCStatsReport.jsm
dom/media/tests/mochitest/pc.js
dom/media/tests/mochitest/templates.js
dom/media/tests/mochitest/test_peerConnection_stats.html
dom/media/webrtc/WebrtcGlobal.h
dom/webidl/RTCStatsReport.webidl
media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -307,25 +307,37 @@ class RTCStatsReport {
   // Since maplike is recent, we still also make the stats available as legacy
   // enumerable read-only properties directly on our content-facing object.
   // Must be called after our webidl sandwich is made.
 
   makeStatsPublic(warnNullable, isLegacy) {
     let legacyProps = {};
     for (let key in this._report) {
       let internal = Cu.cloneInto(this._report[key], this._win);
+      if (internal.packetsDiscarded !== undefined) {
+        internal.discardedPackets = internal.packetsDiscarded;
+      }
+      let isRemote = ["remote-inbound-rtp",
+                      "remote-outbound-rtp"].includes(internal.type);
       if (isLegacy) {
-        internal.type = this._specToLegacyFieldMapping[internal.type] || internal.type;
+        internal.type = this._specToLegacyFieldMapping[internal.type] ||
+                        internal.type;
       }
       this.setInternal(key, internal);
-      let value = Cu.cloneInto(this._report[key], this._win);
+      let value = Cu.cloneInto(internal, this._win);
       value.type = this._specToLegacyFieldMapping[value.type] || value.type;
       legacyProps[key] = {
         enumerable: true, configurable: false,
         get: Cu.exportFunction(function() {
+          if (isRemote) {
+            value.isRemote = true;
+          }
+          if (value.localId) {
+            value.remoteId = value.localId;
+          }
           if (warnNullable.warn) {
             warnNullable.warn();
             warnNullable.warn = null;
           }
           return value;
         }, this.__DOM_IMPL__.wrappedJSObject)
       };
     }
@@ -335,17 +347,19 @@ class RTCStatsReport {
   get mozPcid() { return this._pcid; }
 }
 setupPrototype(RTCStatsReport, {
   classID: PC_STATS_CID,
   contractID: PC_STATS_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
   _specToLegacyFieldMapping: {
         "inbound-rtp": "inboundrtp",
+        "remote-inbound-rtp": "inboundrtp",
         "outbound-rtp": "outboundrtp",
+        "remote-outbound-rtp": "outboundrtp",
         "candidate-pair": "candidatepair",
         "local-candidate": "localcandidate",
         "remote-candidate": "remotecandidate"
   }
 });
 
 class RTCPeerConnection {
   constructor() {
--- a/dom/media/RTCStatsReport.jsm
+++ b/dom/media/RTCStatsReport.jsm
@@ -9,17 +9,19 @@ this.EXPORTED_SYMBOLS = ["convertToRTCSt
 function convertToRTCStatsReport(dict) {
   function appendStats(stats, report) {
     stats.forEach(function(stat) {
         report[stat.id] = stat;
       });
   }
   let report = {};
   appendStats(dict.inboundRTPStreamStats, report);
+  appendStats(dict.remoteInboundRTPStreamStats, report);
   appendStats(dict.outboundRTPStreamStats, report);
+  appendStats(dict.remoteOutboundRTPStreamStats, report);
   appendStats(dict.rtpContributingSourceStats, report);
   appendStats(dict.mediaStreamTrackStats, report);
   appendStats(dict.mediaStreamStats, report);
   appendStats(dict.transportStats, report);
   appendStats(dict.iceComponentStats, report);
   appendStats(dict.iceCandidatePairStats, report);
   appendStats(dict.iceCandidateStats, report);
   appendStats(dict.codecStats, report);
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1445,24 +1445,24 @@ PeerConnectionWrapper.prototype = {
    *        Returns a promise which yields a StatsReport object with RTP stats.
    */
   async waitForRtpFlow(track) {
     info("waitForRtpFlow("+track.id+")");
     let hasFlow = (stats, retries) => {
       info("Checking for stats in " + JSON.stringify(stats) + " for " + track.kind
         + " track " + track.id + ", retry number " + retries);
       let rtp = stats.get([...Object.keys(stats)].find(key =>
-        !stats.get(key).isRemote && stats.get(key).type.endsWith("bound-rtp")));
+        ["inbound-rtp", "outbound-rtp"].includes(stats.get(key).type)));
       if (!rtp) {
         return false;
       }
       info("Should have RTP stats for track " + track.id);
       info("RTP stats: " + JSON.stringify(rtp));
       let nrPackets = rtp[rtp.type == "outbound-rtp" ? "packetsSent"
-                                                    : "packetsReceived"];
+                                                     : "packetsReceived"];
       info("Track " + track.id + " has " + nrPackets + " " +
            rtp.type + " RTP packets.");
       return nrPackets > 0;
     };
 
     // Time between stats checks
     const retryInterval = 500;
     // Timeout in ms
@@ -1472,18 +1472,19 @@ PeerConnectionWrapper.prototype = {
     for (let remaining = timeout; remaining >= 0; remaining -= retryInterval) {
       let stats = await this._pc.getStats(track);
       if (hasFlow(stats, retry++)) {
         ok(true, "RTP flowing for " + track.kind + " track " + track.id);
         return stats;
       }
       await wait(retryInterval);
     }
-    throw new Error("Timeout checking for stats for track " + track.id
-                    + " after at least" + timeout + "ms");
+    throw new Error("Timeout checking for stats for track "
+                    + JSON.stringify(track)
+                    + " after at least " + timeout + "ms");
   },
 
   /**
    * Wait for presence of video flow on all media elements and rtp flow on
    * all sending and receiving track involved in this test.
    *
    * @returns {Promise}
    *        A promise that resolves when media flows for all elements and tracks
@@ -1499,22 +1500,21 @@ PeerConnectionWrapper.prototype = {
       this._pc.getReceivers().map(receiver => this.waitForRtpFlow(receiver.track))));
   },
 
   async waitForSyncedRtcp() {
     // Ensures that RTCP is present
     let ensureSyncedRtcp = async () => {
       let report = await this._pc.getStats();
       for (let [k, v] of report) {
-        if (v.type.endsWith("bound-rtp") && !v.remoteId) {
+        if (["inbound-rtp", "outbound-rtp"].includes(v.type) && !v.remoteId) {
           info(v.id + " is missing remoteId: " + JSON.stringify(v));
           return null;
         }
-        if (v.type == "inbound-rtp" && v.isRemote == true
-            && v.roundTripTime === undefined) {
+        if (v.type == "remote-inbound-rtp" && v.roundTripTime === undefined) {
           info(v.id + " is missing roundTripTime: " + JSON.stringify(v));
           return null;
         }
       }
       return report;
     }
     let attempts = 0;
     // Time-units are MS
@@ -1640,36 +1640,33 @@ PeerConnectionWrapper.prototype = {
       // validate stats
       ok(res.id == key, "Coherent stats id");
       var nowish = Date.now() + 1000;        // TODO: clock drift observed
       var minimum = this.whenCreated - 1000; // on Windows XP (Bug 979649)
       if (isWinXP) {
         todo(false, "Can't reliably test rtcp timestamps on WinXP (Bug 979649)");
 
       } else if (false) { // Bug 1325430 - timestamps aren't working properly in update 49
-	// else if (!twoMachines) {
+        // else if (!twoMachines) {
         // Bug 1225729: On android, sometimes the first RTCP of the first
         // test run gets this value, likely because no RTP has been sent yet.
         if (res.timestamp != 2085978496000) {
           ok(res.timestamp >= minimum,
              "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
                  res.timestamp + " >= " + minimum + " (" +
                  (res.timestamp - minimum) + " ms)");
           ok(res.timestamp <= nowish,
              "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
                  res.timestamp + " <= " + nowish + " (" +
                  (res.timestamp - nowish) + " ms)");
         } else {
           info("Bug 1225729: Uninitialized timestamp (" + res.timestamp +
                 "), should be >=" + minimum + " and <= " + nowish);
         }
       }
-      if (res.isRemote) {
-        continue;
-      }
       counters[res.type] = (counters[res.type] || 0) + 1;
 
       switch (res.type) {
         case "inbound-rtp":
         case "outbound-rtp": {
           // ssrc is a 32 bit number returned as a string by spec
           ok(res.ssrc.length > 0, "Ssrc has length");
           ok(res.ssrc.length < 11, "Ssrc not lengthy");
@@ -1681,76 +1678,85 @@ PeerConnectionWrapper.prototype = {
             // We assume minimum payload to be 1 byte (guess from RFC 3550)
             ok(res.bytesSent >= res.packetsSent, "Rtp bytesSent");
           } else {
             ok(res.packetsReceived !== undefined, "Rtp packetsReceived");
             ok(res.bytesReceived >= res.packetsReceived, "Rtp bytesReceived");
           }
           if (res.remoteId) {
             var rem = stats.get(res.remoteId);
-            ok(rem.isRemote, "Remote is rtcp");
-            ok(rem.remoteId == res.id, "Remote backlink match");
+            ok(rem.localId == res.id, "Remote backlink match");
             if(res.type == "outbound-rtp") {
-              ok(rem.type == "inbound-rtp", "Rtcp is inbound");
+              ok(rem.type == "remote-inbound-rtp", "Rtcp is inbound");
               ok(rem.packetsReceived !== undefined, "Rtcp packetsReceived");
               ok(rem.packetsLost !== undefined, "Rtcp packetsLost");
               ok(rem.bytesReceived >= rem.packetsReceived, "Rtcp bytesReceived");
-	       if (false) { // Bug 1325430 if (!this.disableRtpCountChecking) {
-	       // no guarantee which one is newer!
-	       // Note: this must change when we add a timestamp field to remote RTCP reports
-	       // and make rem.timestamp be the reception time
-		if (res.timestamp >= rem.timestamp) {
-                 ok(rem.packetsReceived <= res.packetsSent, "No more than sent packets");
-		 } else {
-                  info("REVERSED timestamps: rec:" +
-		     rem.packetsReceived + " time:" + rem.timestamp + " sent:" + res.packetsSent + " time:" + res.timestamp);
-		 }
-		// Else we may have received more than outdated Rtcp packetsSent
-                ok(rem.bytesReceived <= res.bytesSent, "No more than sent bytes");
+              if (false) { // Bug 1325430 if (!this.disableRtpCountChecking) {
+                           // no guarantee which one is newer!
+                           // Note: this must change when we add a timestamp
+                           // field to remote RTCP reports
+                           // and make rem.timestamp be the reception time
+                if (res.timestamp >= rem.timestamp) {
+                  ok(rem.packetsReceived <= res.packetsSent,
+                    "No more than sent packets");
+                  } else {
+                    info("REVERSED timestamps: rec:" + rem.packetsReceived +
+                         " time:" + rem.timestamp +
+                         " sent:" + res.packetsSent +
+                         " time:" + res.timestamp);
+                  }
+                // Else we may have received more than outdated Rtcp packetsSent
+                ok(rem.bytesReceived <= res.bytesSent,
+                   "No more than sent bytes");
               }
               ok(rem.jitter !== undefined, "Rtcp jitter");
               if (rem.roundTripTime) {
                 ok(rem.roundTripTime > 0,
                    "Rtcp rtt " + rem.roundTripTime + " >= 0");
                 ok(rem.roundTripTime < 60000,
                    "Rtcp rtt " + rem.roundTripTime + " < 1 min");
               }
             } else {
-              ok(rem.type == "outbound-rtp", "Rtcp is outbound");
+              ok(rem.type == "remote-outbound-rtp", "Rtcp is outbound");
               ok(rem.packetsSent !== undefined, "Rtcp packetsSent");
               // We may have received more than outdated Rtcp packetsSent
               ok(rem.bytesSent >= rem.packetsSent, "Rtcp bytesSent");
             }
             ok(rem.ssrc == res.ssrc, "Remote ssrc match");
           } else {
             info("No rtcp info received yet");
           }
         }
         break;
       }
     }
 
-    var legacyToSpecMapping = {
-      'inboundrtp':'inbound-rtp',
-      'outboundrtp':'outbound-rtp',
-      'candidatepair':'candidate-pair',
-      'localcandidate':'local-candidate',
-      'remotecandidate':'remote-candidate'
+    var legacyToSpecMapping = stat => {
+      let mapping = {
+        'inboundrtp':'inbound-rtp',
+        'outboundrtp':'outbound-rtp',
+        'candidatepair':'candidate-pair',
+        'localcandidate':'local-candidate',
+        'remotecandidate':'remote-candidate',
+      };
+      let type = mapping[stat.type] || stat.type;
+      if (stat.isRemote) {
+        return "remote-" + type;
+      }
+      return type;
     };
     // Use legacy way of enumerating stats
     var counters2 = {};
     for (let key in stats) {
       if (!stats.hasOwnProperty(key)) {
         continue;
       }
       var res = stats[key];
-      var type = legacyToSpecMapping[res.type] || res.type;
-      if (!res.isRemote) {
-        counters2[type] = (counters2[type] || 0) + 1;
-      }
+      var type = legacyToSpecMapping(res);
+      counters2[type] = (counters2[type] || 0) + 1;
     }
     is(JSON.stringify(counters), JSON.stringify(counters2),
        "Spec and legacy variant of RTCStatsReport enumeration agree");
     var nin = Object.keys(this.expectedRemoteTrackInfoById).length;
     var nout = Object.keys(this.expectedLocalTrackInfoById).length;
     var ndata = this.dataChannels.length;
 
     // TODO(Bug 957145): Restore stronger inbound-rtp test once Bug 948249 is fixed
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -86,17 +86,16 @@ function waitForAnIceCandidate(pc) {
 function checkTrackStats(pc, rtpSenderOrReceiver, outbound) {
   var track = rtpSenderOrReceiver.track;
   var audio = (track.kind == "audio");
   var msg = pc + " stats " + (outbound ? "outbound " : "inbound ") +
       (audio ? "audio" : "video") + " rtp track id " + track.id;
   return pc.getStats(track).then(stats => {
     ok(pc.hasStat(stats, {
       type: outbound ? "outbound-rtp" : "inbound-rtp",
-      isRemote: false,
       mediaType: audio ? "audio" : "video"
     }), msg + " - found expected stats");
     ok(!pc.hasStat(stats, {
       type: outbound ? "inbound-rtp" : "outbound-rtp",
       isRemote: false
     }), msg + " - did not find extra stats with wrong direction");
     ok(!pc.hasStat(stats, {
       mediaType: audio ? "video" : "audio"
--- a/dom/media/tests/mochitest/test_peerConnection_stats.html
+++ b/dom/media/tests/mochitest/test_peerConnection_stats.html
@@ -5,64 +5,170 @@
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1337525",
     title: "webRtc Stats composition and sanity"
   });
+
+
+//
+// Creates type definitions by extending other type definitions
+//
+function extendTypeWith(superTypeDefinition, ownTypeDefintion) {
+  let newType = {
+    name: ownTypeDefintion.name,
+    types: [ownTypeDefintion.name],
+    hasSuperType: function(type) {
+      return this.types.includes(type);
+    },
+  };
+  newType.types = [...newType.types, ...superTypeDefinition.types || []];
+  for (let i of ["expected", "optional", "unimplemented", "localVideoOnly",
+                 "deprecated"]) {
+    newType[i] = superTypeDefinition[i] ? [...superTypeDefinition[i]] : [];
+    if (ownTypeDefintion[i]) {
+      newType[i] = [...newType[i], ...ownTypeDefintion[i]];
+    }
+  }
+  return newType;
+}
+
+//
+// Super types for tested types, these never appear as type in the reports
+// directly.
+//
+var rtcStats = extendTypeWith({}, {
+  name:            "RTCStats",
+  expected:        ["timestamp",
+                    "type",
+                    "id",],
+});
+
+var rtcRtpStreamStats = extendTypeWith(rtcStats, {
+  name:             "RTCRtpStreamStats",
+  expected:         ["ssrc",
+                     "mediaType",],
+  optional:         ["nackCount",],
+  unimplemented:    ["mediaTrackId",
+                     "transportId",
+                     "codecId",
+                     "sliCount",
+                     "qpSum",],
+  localVideoOnly:   ["firCount",
+                     "pliCount",],
+});
+
+var rtcReceivedRtpStreamStats = extendTypeWith(rtcRtpStreamStats, {
+  name:             "RTCReceivedRTPStreamStats",
+  expected:         ["packetsReceived",
+                     "bytesReceived",
+                     "packetsLost",
+                     "jitter",],
+  localVideoOnly:   ["packetsDiscarded",],
+  unimplemented:    ["fractionLost",
+                     "packetsRepaired",
+                     "burstPacketsLost",
+                     "burstPacketsDiscarded",
+                     "burstLossCount",
+                     "burstDiscardCount",
+                     "burstLossRate",
+                     "burstDiscardRate",
+                     "gapLossRate",
+                     "gapDiscardRate",],
+});
+
+var rtcSentRtpStreamStats = extendTypeWith(rtcRtpStreamStats, {
+  name:             "RTCSentRTPStreamStats",
+  expected:         ["packetsSent",
+                     "bytesSent",],
+  unimplemented:    ["packetsDiscardedOnSend",
+                     "bytesDiscardedOnSend",],
+});
+
 var statsExpectedByType = {
-  "inbound-rtp": {
-    expected: ["id", "timestamp", "type", "ssrc", "isRemote", "mediaType",
-      "packetsReceived", "packetsLost", "bytesReceived", "jitter",],
-    optional: ["roundTripTime", "remoteId", "nackCount",],
-    localVideoOnly: ["discardedPackets", "framerateStdDev", "framerateMean",
-      "bitrateMean", "bitrateStdDev", "firCount", "pliCount", "framesDecoded",],
-    unimplemented: ["mediaTrackId", "transportId", "codecId",
-      "packetsDiscarded", "associateStatsId",
-      "sliCount", "qpSum", "packetsRepaired", "fractionLost",
-      "burstPacketsLost", "burstLossCount", "burstDiscardCount",
-      "gapDiscardRate", "gapLossRate",],
-    deprecated: ["mozRtt"],
-  },
-  "outbound-rtp": {
-    expected: ["id", "timestamp", "type", "ssrc", "isRemote", "mediaType",
-      "packetsSent", "bytesSent", "remoteId",],
-    optional: ["remoteId", "nackCount",],
-    localVideoOnly: ["droppedFrames", "bitrateMean", "bitrateStdDev",
-      "framerateMean", "framerateStdDev", "framesEncoded", "firCount",
-      "pliCount",],
-    unimplemented: ["mediaTrackId", "transportId", "codecId",
-      "sliCount", "qpSum", "targetBitrate",],
-    deprecated: [],
-  },
-  "csrc": { skip: true },
-  "codec": { skip: true },
-  "peer-connection": { skip: true },
-  "data-channel": { skip: true },
-  "track": { skip: true },
-  "transport": { skip: true },
-  "candidate-pair": {
-    expected: ["id", "timestamp", "type",
-      "transportId", "localCandidateId", "remoteCandidateId", "state",
-      "priority", "nominated", "writable", "readable",
-      "bytesSent", "bytesReceived",
-      "lastPacketSentTimestamp", "lastPacketReceivedTimestamp",],
-    optional: ["selected",],
-    unimplemented: ["totalRoundTripTime", "currentRoundTripTime",
-      "availableOutgoingBitrate", "availableIncomingBitrate",
-      "requestsReceived", "requestsSent", "responsesReceived",
-      "responsesSent", "retransmissionsReceived", "retransmissionsSent",
-      "consentRequestsSent",],
-    deprecated: [],
-  },
-  "local-candidate": { skip: true },
+  "inbound-rtp": extendTypeWith(rtcReceivedRtpStreamStats, {
+    name:           "inbound-rtp",
+    optional:       ["remoteId",],
+    localVideoOnly: ["discardedPackets",
+                     "framerateStdDev",
+                     "framerateMean",
+                     "bitrateMean",
+                     "bitrateStdDev",
+                     "framesDecoded",],
+    unimplemented:  ["lastPacketReceivedTimestamp",],
+    deprecated:     ["mozRtt",
+                     "isRemote",],
+  }),
+  "remote-inbound-rtp": extendTypeWith(rtcReceivedRtpStreamStats, {
+    name:           "remote-inbound-rtp",
+    expected:       ["localId",
+                     "roundTripTime",],
+  }),
+  "outbound-rtp": extendTypeWith(rtcSentRtpStreamStats, {
+    name:           "outbound-rtp",
+    expected:       ["packetsSent",
+                     "bytesSent",],
+    optional:       ["remoteId",
+                     "nackCount",],
+    localVideoOnly: ["droppedFrames",
+                     "bitrateMean",
+                     "bitrateStdDev",
+                     "framerateMean",
+                     "framerateStdDev",
+                     "framesEncoded",],
+    unimplemented:  ["lastPacketSentTimeStamp",
+                     "targetBitrate",
+                     "totalEncodeTime",
+                     "averageRTCPInterval",],
+    deprecated:     ["isRemote",],
+  }),
+  "remote-outbound-rtp": extendTypeWith(rtcSentRtpStreamStats, {
+    name:           "remote-outbound-rtp",
+    expected:       ["localId",],
+    unimplemented:  ["remoteTimeStamp",],
+  }),
+  "candidate-pair": extendTypeWith(rtcStats, {
+    name:           "candidate-pair",
+    expected:       ["transportId",
+                     "localCandidateId",
+                     "remoteCandidateId",
+                     "state",
+                     "priority",
+                     "nominated",
+                     "writable",
+                     "readable",
+                     "bytesSent",
+                     "bytesReceived",
+                     "lastPacketSentTimestamp",
+                     "lastPacketReceivedTimestamp",],
+    optional:       ["selected",],
+    unimplemented:  ["totalRoundTripTime",
+                     "currentRoundTripTime",
+                     "availableOutgoingBitrate",
+                     "availableIncomingBitrate",
+                     "requestsReceived",
+                     "requestsSent",
+                     "responsesReceived",
+                     "responsesSent",
+                     "retransmissionsReceived",
+                     "retransmissionsSent",
+                     "consentRequestsSent",],
+  }),
+  "csrc":             { skip: true },
+  "codec":            { skip: true },
+  "peer-connection":  { skip: true },
+  "data-channel":     { skip: true },
+  "track":            { skip: true },
+  "transport":        { skip: true },
+  "local-candidate":  { skip: true },
   "remote-candidate": { skip: true },
-  "certificate": { skip: true },
+  "certificate":      { skip: true },
 };
 ["in", "out"].forEach(pre => {
   let s = statsExpectedByType[pre + "bound-rtp"];
   s.optional = [...s.optional, ...s.localVideoOnly];
 });
 
 //
 //  Checks that the fields in a report conform to the expectations in
@@ -109,84 +215,95 @@ var checkExpectedFields = report => repo
 
 var pedanticChecks = report => {
   report.forEach((statObj, mapKey) => {
     let tested = {};
     // Record what fields get tested.
     // To access a field foo without marking it as tested use stat.inner.foo
     let stat = new Proxy(statObj, {
       get(stat, key) {
+        if (key == "isRemote") return stat.type.startsWith("remote-");
         if (key == "inner") return stat;
         tested[key] = true;
         return stat[key];
       }
     });
 
     let expectations = statsExpectedByType[stat.type];
 
     if (expectations.skip) {
       return;
     }
 
-    // All stats share the following attributes inherited from RTCStats
-    is(stat.id, mapKey, stat.type + ".id is the same as the report key.");
+    if (expectations.hasSuperType("RTCStats")) {
+      // All stats share the following attributes inherited from RTCStats
+      is(stat.id, mapKey, stat.type + ".id is the same as the report key.");
 
-    // timestamp
-    ok(stat.timestamp >= 0, stat.type + ".timestamp is not less than 0");
+      // timestamp
+      ok(stat.timestamp >= 0, stat.type + ".timestamp is not less than 0");
+    }
 
     //
     // RTCStreamStats attributes with common behavior
     //
-    // inbound-rtp and outbound-rtp inherit from RTCStreamStats
-    if (["inbound-rtp", "outbound-rtp"].includes(stat.type)) {
+    if (expectations.hasSuperType("RTCRtpStreamStats")) {
       //
       // Common RTCStreamStats fields
       //
 
       // SSRC
       ok(stat.ssrc, stat.type + ".ssrc has a value");
 
-      // isRemote
-      ok(stat.isRemote !== undefined, stat.type + ".isRemote exists.");
-
       // mediaType
       ok(["audio", "video"].includes(stat.mediaType),
         stat.type + ".mediaType is 'audio' or 'video'");
 
       // remote id
-      if (stat.remoteId) {
+      if (stat.inner.remoteId) {
         ok(report.has(stat.remoteId), "remoteId exists in report.");
         is(report.get(stat.remoteId).ssrc, stat.ssrc,
           "remote ssrc and local ssrc match.");
-        is(report.get(stat.remoteId).remoteId, stat.id,
-          "remote object has local object as it's own remote object.");
+        is(report.get(stat.remoteId).localId, stat.id,
+          "remote object has localId matching the id of the local object.");
+      }
+
+      // local id
+      if (stat.inner.localId) {
+        ok(report.has(stat.localId), "localId exists in report.");
+        is(report.get(stat.localId).ssrc, stat.ssrc,
+          "remote ssrc and local ssrc match.");
+        is(report.get(stat.localId).remoteId, stat.id,
+          "local object has remoteId matching the id of the remote object.");
       }
 
       // nackCount
-      if (!stat.inner.isRemote) {
+      if (!stat.isRemote) {
         ok(stat.nackCount >= 0, stat.type + ".nackCount is sane.");
       } else {
         is(stat.nackCount, undefined, stat.type
           + ".nackCount is only set when isRemote is false");
       }
 
-      if (!stat.inner.isRemote && stat.inner.mediaType == "video") {
+      if (!stat.isRemote && stat.inner.mediaType == "video") {
         // firCount
         ok(stat.firCount >= 0 && stat.firCount < 100,
           stat.type + ".firCount is a sane number for a short test. value="
           + stat.firCount);
 
         // pliCount
         ok(stat.pliCount >= 0 && stat.pliCount < 100,
           stat.type + ".pliCount is a sane number for a short test. value="
           + stat.pliCount);
       }
     }
 
-    if (stat.type == "inbound-rtp") {
+    //
+    // Common RTCReceivedRTPStreamStats fields
+    //
+    if (expectations.hasSuperType("RTCReceivedRTPStreamStats")) {
       //
       // Required fields
       //
 
       // packetsReceived
       ok(stat.packetsReceived >= 0
         && stat.packetsReceived < 10 ** 5,
         stat.type + ".packetsReceived is a sane number for a short test. value="
@@ -205,174 +322,131 @@ var pedanticChecks = report => {
 
       // This should be much lower for audio, TODO: Bug 1330575
       let expectedJitter = stat.mediaType == "video" ? 0.5 : 1;
       // jitter
       ok(stat.jitter < expectedJitter,
         stat.type + ".jitter is sane number for a local only test. value="
         + stat.jitter);
 
-      // packetsDiscarded
-      // special exception for, TODO: Bug 1335967
-      // if (!stat.inner.isRemote && stat.discardedPackets !== undefined) {
-      //   ok(stat.packetsDiscarded < 100, stat.type
-      //     + ".packetsDiscarded is a sane number for a short test. value="
-      //     + stat.packetsDiscarded);
-      // }
-      // if (stat.packetsDiscarded !== undefined) {
-      //   ok(!stat.inner.isRemote,
-      //     stat.type + ".packetsDiscarded is only set when isRemote is "
-      //     + "false");
-      // }
-
-      //
-      // Optional fields
-      //
-
-      // roundTripTime
-      if (stat.inner.isRemote) {
-        ok(stat.roundTripTime >= 0, stat.type + ".roundTripTime is sane with" +
-          "value of:" + stat.roundTripTime);
-      } else {
-        is(stat.roundTripTime, undefined, stat.type
-          + ".roundTripTime is only set when isRemote is true");
-      }
-
-      //
-      // Local video only stats
-      //
-      if (stat.inner.isRemote || stat.inner.mediaType != "video") {
-        expectations.localVideoOnly.forEach(field => {
-          if (stat.inner.isRemote) {
-            ok(stat[field] === undefined, stat.type + " does not have field "
-              + field + " when isRemote is true");
-          } else { // mediaType != video
-            ok(stat[field] === undefined, stat.type + " does not have field "
-              + field + " when mediaType is not 'video'");
-          }
-        });
+      // Bug 1386010, only available on local video for the time being
+      if (stat.mediaType == "video" && stat.type == "inbound-rtp") {
+        // packetsDiscarded
+        ok(stat.packetsDiscarded < 100 && stat.packetsDiscarded >= 0,
+          stat.type
+          + ".packetsDiscared is a sane number for a short test. value="
+          + stat.packetsDiscared);
+          // discardedPackets
+          is(stat.discardedPackets, stat.packetsDiscarded, stat.type
+            + ".discardedPackets is the same as packetsDiscarded. value="
+            + stat.discardedPackets);
       } else {
-        expectations.localVideoOnly.forEach(field => {
-          ok(stat.inner[field] !== undefined, stat.type + " has field " + field
-            + " when mediaType is video");
-        });
-        // discardedPackets
-        ok(stat.discardedPackets < 100, stat.type
-          + ".discardedPackets is a sane number for a short test. value="
-          + stat.discardedPackets);
-        // framesDecoded
-        ok(stat.framesDecoded > 0 && stat.framesDecoded < 1000000, stat.type
-          + ".framesDecoded is a sane number for a short test. value="
-          + stat.framesDecoded);
-        // bitrateMean
-        // special exception, TODO: Bug 1341533
-        if (stat.bitrateMean !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.bitrateMean >= 0 && stat.bitrateMean < 2 ** 25,
-          //   stat.type + ".bitrateMean is sane. value="
-          //   + stat.bitrateMean);
-        }
+        is(stat.packetsDiscarded, undefined);
+        is(stat.discardedPackets, undefined);
+      }
+    }
 
-        // bitrateStdDev
-        // special exception, TODO Bug 1341533
-        if (stat.bitrateStdDev !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.bitrateStdDev >= 0 && stat.bitrateStdDev < 2 ** 25,
-          //   stat.type + ".bitrateStdDev is sane. value="
-          //   + stat.bitrateStdDev);
-        }
-
-        // framerateMean
-        // special exception, TODO: Bug 1341533
-        if (stat.framerateMean !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.framerateMean >= 0 && stat.framerateMean < 120,
-          //   stat.type + ".framerateMean is sane. value="
-          //   + stat.framerateMean);
-        }
-
-        // framerateStdDev
-        // special exception, TODO: Bug 1341533
-        if (stat.framerateStdDev !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.framerateStdDev >= 0 && stat.framerateStdDev < 120,
-          //   stat.type + ".framerateStdDev is sane. value="
-          //   + stat.framerateStdDev);
-        }
-      }
-    } else if (stat.type == "outbound-rtp") {
-      //
-      // Required fields
-      //
-
+    //
+    // Common RTCSentRTPStreamStats
+    //
+    if (expectations.hasSuperType("RTCSentRTPStreamStats")) {
       // packetsSent
       ok(stat.packetsSent > 0 && stat.packetsSent < 10000,
         stat.type + ".packetsSent is a sane number for a short test. value="
         + stat.packetsSent);
 
       // bytesSent
       ok(stat.bytesSent, stat.type + ".bytesSent has a value."
         + " Value not expected to be sane, bug 1339104. value="
         + stat.bytesSent);
+    }
+
+    // Common fields between inbound and outbound-rtp that are not common
+    // to remote-*-rtp. Note these are all non-spec and will be removed
+    if (stat.type == "inbound-rtp" || stat.type == "outbound-rtp" &&
+        stat.mediaType == "video") {
+      // bitrateMean
+      // special exception, TODO: Bug 1341533
+      if (stat.bitrateMean !== undefined) {
+        // TODO: uncomment when Bug 1341533 lands
+        // ok(stat.bitrateMean >= 0 && stat.bitrateMean < 2 ** 25,
+        //   stat.type + ".bitrateMean is sane. value="
+        //   + stat.bitrateMean);
+      }
+
+      // bitrateStdDev
+      // special exception, TODO Bug 1341533
+      if (stat.bitrateStdDev !== undefined) {
+        // TODO: uncomment when Bug 1341533 lands
+        // ok(stat.bitrateStdDev >= 0 && stat.bitrateStdDev < 2 ** 25,
+        //   stat.type + ".bitrateStdDev is sane. value="
+        //   + stat.bitrateStdDev);
+      }
+
+      // framerateMean
+      // special exception, TODO: Bug 1341533
+      if (stat.framerateMean !== undefined) {
+        // TODO: uncomment when Bug 1341533 lands
+        // ok(stat.framerateMean >= 0 && stat.framerateMean < 120,
+        //   stat.type + ".framerateMean is sane. value="
+        //   + stat.framerateMean);
+      }
+
+      // framerateStdDev
+      // special exception, TODO: Bug 1341533
+      if (stat.framerateStdDev !== undefined) {
+        // TODO: uncomment when Bug 1341533 lands
+        // ok(stat.framerateStdDev >= 0 && stat.framerateStdDev < 120,
+        //   stat.type + ".framerateStdDev is sane. value="
+        //   + stat.framerateStdDev);
+      }
+    }
+
+    if (stat.type == "inbound-rtp") {
+      //
+      // Local video only stats
+      //
+      if (stat.inner.mediaType != "video") {
+        expectations.localVideoOnly.forEach(field => {
+            ok(stat[field] === undefined, stat.type + " does not have field "
+              + field + " when mediaType is not 'video'");
+          });
+      } else {
+        expectations.localVideoOnly.forEach(field => {
+          ok(stat.inner[field] !== undefined, stat.type + " has field " + field
+            + " when mediaType is video");
+        });
+        // framesDecoded
+        ok(stat.framesDecoded > 0 && stat.framesDecoded < 1000000, stat.type
+          + ".framesDecoded is a sane number for a short test. value="
+          + stat.framesDecoded);
+      }
+    } else if (stat.type == "remote-inbound-rtp") {
+      ok(stat.roundTripTime >= 0, stat.type + ".roundTripTime is sane with" +
+        "value of:" + stat.roundTripTime);
+    } else if (stat.type == "outbound-rtp") {
 
       //
       // Optional fields
       //
 
       //
       // Local video only stats
       //
-      if (stat.inner.isRemote || stat.inner.mediaType != "video") {
+      if (stat.inner.mediaType != "video") {
         expectations.localVideoOnly.forEach(field => {
-          if (stat.inner.isRemote) {
-            ok(stat[field] === undefined, stat.type + " does not have field "
-              + field + " when isRemote is true");
-          } else { // mediaType != video
-            ok(stat[field] === undefined, stat.type + " does not have field "
-              + field + " when mediaType is not 'video'");
-          }
+          ok(stat[field] === undefined, stat.type + " does not have field "
+            + field + " when mediaType is not 'video'");
         });
       } else {
         expectations.localVideoOnly.forEach(field => {
           ok(stat.inner[field] !== undefined, stat.type + " has field " + field
             + " when mediaType is video and isRemote is false");
         });
 
-        // bitrateMean
-        if (stat.bitrateMean !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.bitrateMean >= 0 && stat.bitrateMean < 2 ** 25,
-          //   stat.type + ".bitrateMean is sane. value="
-          //   + stat.bitrateMean);
-        }
-
-        // bitrateStdDev
-        if (stat.bitrateStdDev !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.bitrateStdDev >= 0 && stat.bitrateStdDev < 2 ** 25,
-          //   stat.type + ".bitrateStdDev is sane. value="
-          //   + stat.bitrateStdDev);
-        }
-
-        // framerateMean
-        if (stat.framerateMean !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.framerateMean >= 0 && stat.framerateMean < 120,
-          //   stat.type + ".framerateMean is sane. value="
-          //   + stat.framerateMean);
-        }
-
-        // framerateStdDev
-        if (stat.framerateStdDev !== undefined) {
-          // TODO: uncomment when Bug 1341533 lands
-          // ok(stat.framerateStdDev >= 0 && stat.framerateStdDev < 120,
-          //   stat.type + ".framerateStdDev is sane. value="
-          //   + stat.framerateStdDev);
-        }
-
         // droppedFrames
         ok(stat.droppedFrames >= 0,
           stat.type + ".droppedFrames is not negative. value="
           + stat.droppedFrames);
 
         // framesEncoded
         ok(stat.framesEncoded >= 0 && stat.framesEncoded < 100000, stat.type
           + ".framesEncoded is a sane number for a short test. value="
@@ -490,21 +564,30 @@ var pedanticChecks = report => {
 
 // This MUST be run after PC_*_WAIT_FOR_MEDIA_FLOW to ensure that we have RTP
 // before checking for RTCP.
 var waitForSyncedRtcp = async pc => {
   // Ensures that RTCP is present
   let ensureSyncedRtcp = async () => {
     let stats = await pc.getStats();
     for (let [k, v] of stats) {
-      if (v.type.endsWith("bound-rtp") && !v.remoteId) {
-        throw new Error(v.id + " is missing remoteId: "
-          + JSON.stringify(v));
+      if (v.type.endsWith("bound-rtp")) {
+        if (v.type.startsWith("remote-")) {
+          if (!v.localId) {
+            throw new Error(v.id + "remote stat is missing localId: "
+              + JSON.stringify(v));
+          }
+        } else {
+          if (!v.remoteId) {
+            throw new Error(v.id + "local stat is missing remoteId: "
+              + JSON.stringify(v));
+          }
+        }
       }
-      if (v.type == "inbound-rtp" && v.isRemote == true
+      if (v.type == "remote-inbound-rtp"
           && v.roundTripTime === undefined) {
         throw new Error(v.id + " is missing roundTripTime: "
           + JSON.stringify(v));
       }
     }
     return stats;
   }
   const waitPeriod = 500;
--- a/dom/media/webrtc/WebrtcGlobal.h
+++ b/dom/media/webrtc/WebrtcGlobal.h
@@ -27,49 +27,49 @@ struct ParamTraits<mozilla::dom::Optiona
       WriteParam(aMsg, true);
       WriteParam(aMsg, aParam.Value());
       return;
     }
 
     WriteParam(aMsg, false);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
     bool was_passed = false;
 
-    if (!ReadParam(aMsg, aIter, &was_passed)) {
+    if (!ReadParam(aMsg, aItr, &was_passed)) {
       return false;
     }
 
-    aResult->Reset(); //XXX Optional_base seems to reach this point with isSome true.
+    //XXX Optional_base seems to reach this point with isSome true.
+    aResult->Reset();
 
     if (was_passed) {
-      if (!ReadParam(aMsg, aIter, &(aResult->Construct()))) {
-        return false;
-      }
+      return ReadParam(aMsg, aItr, &(aResult->Construct()));
     }
 
     return true;
   }
 };
 
 template<typename T>
 struct ParamTraits<mozilla::dom::Sequence<T>>
 {
   typedef mozilla::dom::Sequence<T> paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, static_cast<const FallibleTArray<T>&>(aParam));
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    return ReadParam(aMsg, aIter, dynamic_cast<FallibleTArray<T>*>(aResult));
+    return ReadParam(aMsg, aItr, dynamic_cast<FallibleTArray<T>*>(aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCStatsType> :
   public ContiguousEnumSerializer<
     mozilla::dom::RTCStatsType,
     mozilla::dom::RTCStatsType::Inbound_rtp,
@@ -100,105 +100,102 @@ struct ParamTraits<mozilla::dom::RTCStat
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mClosed);
     WriteParam(aMsg, aParam.mCodecStats);
     WriteParam(aMsg, aParam.mIceCandidatePairStats);
     WriteParam(aMsg, aParam.mIceCandidateStats);
     WriteParam(aMsg, aParam.mIceComponentStats);
     WriteParam(aMsg, aParam.mInboundRTPStreamStats);
+    WriteParam(aMsg, aParam.mRemoteInboundRTPStreamStats);
     WriteParam(aMsg, aParam.mLocalSdp);
     WriteParam(aMsg, aParam.mMediaStreamStats);
     WriteParam(aMsg, aParam.mMediaStreamTrackStats);
     WriteParam(aMsg, aParam.mOutboundRTPStreamStats);
+    WriteParam(aMsg, aParam.mRemoteOutboundRTPStreamStats);
     WriteParam(aMsg, aParam.mPcid);
     WriteParam(aMsg, aParam.mRemoteSdp);
     WriteParam(aMsg, aParam.mTimestamp);
     WriteParam(aMsg, aParam.mIceRestarts);
     WriteParam(aMsg, aParam.mIceRollbacks);
     WriteParam(aMsg, aParam.mTransportStats);
     WriteParam(aMsg, aParam.mRtpContributingSourceStats);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mClosed)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mCodecStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIceCandidatePairStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIceCandidateStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIceComponentStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mInboundRTPStreamStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mLocalSdp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mMediaStreamStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mMediaStreamTrackStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mOutboundRTPStreamStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPcid)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mRemoteSdp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTimestamp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIceRestarts)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIceRollbacks)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTransportStats)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mRtpContributingSourceStats))) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mClosed)) &&
+           ReadParam(aMsg, aItr, &(aResult->mCodecStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIceCandidatePairStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIceCandidateStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIceComponentStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mInboundRTPStreamStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteInboundRTPStreamStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mLocalSdp)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMediaStreamStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMediaStreamTrackStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mOutboundRTPStreamStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteOutboundRTPStreamStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPcid)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteSdp)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTimestamp)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIceRestarts)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIceRollbacks)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTransportStats)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRtpContributingSourceStats));
   }
 };
 
 typedef mozilla::dom::RTCStats RTCStats;
 
-static void WriteRTCStats(Message* aMsg, const RTCStats& aParam)
-{
-  // RTCStats base class
-  WriteParam(aMsg, aParam.mId);
-  WriteParam(aMsg, aParam.mTimestamp);
-  WriteParam(aMsg, aParam.mType);
-}
-
-static bool ReadRTCStats(const Message* aMsg, PickleIterator* aIter, RTCStats* aResult)
-{
-  // RTCStats base class
-  if (!ReadParam(aMsg, aIter, &(aResult->mId)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mTimestamp)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mType))) {
-    return false;
+template<>
+struct ParamTraits<mozilla::dom::RTCStats> {
+  static void Write(Message* aMsg, const RTCStats& aParam)
+  {
+    // RTCStats base class
+    WriteParam(aMsg, aParam.mId);
+    WriteParam(aMsg, aParam.mTimestamp);
+    WriteParam(aMsg, aParam.mType);
   }
 
-  return true;
-}
+  static bool Read(const Message* aMsg, PickleIterator* aItr, RTCStats* aResult)
+  {
+    // RTCStats base class
+    return ReadParam(aMsg, aItr, &(aResult->mId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTimestamp)) &&
+           ReadParam(aMsg, aItr, &(aResult->mType));
+  }
+};
 
 template<>
 struct ParamTraits<mozilla::dom::RTCCodecStats>
 {
   typedef mozilla::dom::RTCCodecStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mChannels);
     WriteParam(aMsg, aParam.mClockRate);
     WriteParam(aMsg, aParam.mCodec);
     WriteParam(aMsg, aParam.mParameters);
     WriteParam(aMsg, aParam.mPayloadType);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mChannels)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mClockRate)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mCodec)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mParameters)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPayloadType)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
- }
+    return ReadParam(aMsg, aItr, &(aResult->mChannels)) &&
+           ReadParam(aMsg, aItr, &(aResult->mClockRate)) &&
+           ReadParam(aMsg, aItr, &(aResult->mCodec)) &&
+           ReadParam(aMsg, aItr, &(aResult->mParameters)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPayloadType)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
+  }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCIceCandidatePairStats>
 {
   typedef mozilla::dom::RTCIceCandidatePairStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
@@ -211,39 +208,36 @@ struct ParamTraits<mozilla::dom::RTCIceC
     WriteParam(aMsg, aParam.mReadable);
     WriteParam(aMsg, aParam.mRemoteCandidateId);
     WriteParam(aMsg, aParam.mSelected);
     WriteParam(aMsg, aParam.mState);
     WriteParam(aMsg, aParam.mBytesSent);
     WriteParam(aMsg, aParam.mBytesReceived);
     WriteParam(aMsg, aParam.mLastPacketSentTimestamp);
     WriteParam(aMsg, aParam.mLastPacketReceivedTimestamp);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mTransportId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mLocalCandidateId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPriority)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNominated)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mWritable)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mReadable)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mRemoteCandidateId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mSelected)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mState)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mBytesSent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mBytesReceived)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mLastPacketSentTimestamp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mLastPacketReceivedTimestamp)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mTransportId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mLocalCandidateId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPriority)) &&
+           ReadParam(aMsg, aItr, &(aResult->mNominated)) &&
+           ReadParam(aMsg, aItr, &(aResult->mWritable)) &&
+           ReadParam(aMsg, aItr, &(aResult->mReadable)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteCandidateId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mSelected)) &&
+           ReadParam(aMsg, aItr, &(aResult->mState)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesSent)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mLastPacketSentTimestamp)) &&
+           ReadParam(aMsg, aItr, &(aResult->mLastPacketReceivedTimestamp)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
  }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCIceCandidateStats>
 {
   typedef mozilla::dom::RTCIceCandidateStats paramType;
 
@@ -251,227 +245,294 @@ struct ParamTraits<mozilla::dom::RTCIceC
   {
     WriteParam(aMsg, aParam.mCandidateId);
     WriteParam(aMsg, aParam.mCandidateType);
     WriteParam(aMsg, aParam.mComponentId);
     WriteParam(aMsg, aParam.mIpAddress);
     WriteParam(aMsg, aParam.mMozLocalTransport);
     WriteParam(aMsg, aParam.mPortNumber);
     WriteParam(aMsg, aParam.mTransport);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mCandidateId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mCandidateType)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mComponentId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIpAddress)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mMozLocalTransport)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPortNumber)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTransport)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
- }
+    return ReadParam(aMsg, aItr, &(aResult->mCandidateId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mCandidateType)) &&
+           ReadParam(aMsg, aItr, &(aResult->mComponentId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mIpAddress)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMozLocalTransport)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPortNumber)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTransport)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
+  }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCIceComponentStats>
 {
   typedef mozilla::dom::RTCIceComponentStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mActiveConnection);
     WriteParam(aMsg, aParam.mBytesReceived);
     WriteParam(aMsg, aParam.mBytesSent);
     WriteParam(aMsg, aParam.mComponent);
     WriteParam(aMsg, aParam.mTransportId);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mActiveConnection)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mBytesReceived)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mBytesSent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mComponent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTransportId)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mActiveConnection)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesSent)) &&
+           ReadParam(aMsg, aItr, &(aResult->mComponent)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTransportId)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
   }
 };
 
-static void WriteRTCRTPStreamStats(
-              Message* aMsg,
-              const mozilla::dom::RTCRTPStreamStats& aParam)
+template<>
+struct ParamTraits<mozilla::dom::RTCRTPStreamStats>
 {
-    WriteParam(aMsg, aParam.mBitrateMean);
-    WriteParam(aMsg, aParam.mBitrateStdDev);
-    WriteParam(aMsg, aParam.mCodecId);
-    WriteParam(aMsg, aParam.mFramerateMean);
-    WriteParam(aMsg, aParam.mFramerateStdDev);
-    WriteParam(aMsg, aParam.mIsRemote);
-    WriteParam(aMsg, aParam.mMediaTrackId);
-    WriteParam(aMsg, aParam.mMediaType);
-    WriteParam(aMsg, aParam.mRemoteId);
-    WriteParam(aMsg, aParam.mSsrc);
-    WriteParam(aMsg, aParam.mTransportId);
-}
+  typedef mozilla::dom::RTCRTPStreamStats paramType;
+  typedef mozilla::dom::RTCStats superType;
 
-static bool ReadRTCRTPStreamStats(
-              const Message* aMsg, PickleIterator* aIter,
-              mozilla::dom::RTCRTPStreamStats* aResult)
-{
-  if (!ReadParam(aMsg, aIter, &(aResult->mBitrateMean)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mBitrateStdDev)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mCodecId)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mFramerateMean)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mFramerateStdDev)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mIsRemote)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mMediaTrackId)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mMediaType)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mRemoteId)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mSsrc)) ||
-      !ReadParam(aMsg, aIter, &(aResult->mTransportId))) {
-    return false;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mSsrc);
+    WriteParam(aMsg, aParam.mMediaType);
+    WriteParam(aMsg, aParam.mMediaTrackId);
+    WriteParam(aMsg, aParam.mTransportId);
+    WriteParam(aMsg, aParam.mCodecId);
+    WriteParam(aMsg, aParam.mFirCount);
+    WriteParam(aMsg, aParam.mNackCount);
+    WriteParam(aMsg, aParam.mPliCount);
+    ParamTraits<superType>::Write(aMsg, aParam);
   }
 
-  return true;
-}
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mSsrc)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMediaType)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMediaTrackId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTransportId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mCodecId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFirCount)) &&
+           ReadParam(aMsg, aItr, &(aResult->mNackCount)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPliCount)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::dom::RTCReceivedRTPStreamStats>
+{
+  typedef mozilla::dom::RTCReceivedRTPStreamStats paramType;
+  typedef mozilla::dom::RTCRTPStreamStats superType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mPacketsReceived);
+    WriteParam(aMsg, aParam.mBytesReceived);
+    WriteParam(aMsg, aParam.mPacketsLost);
+    WriteParam(aMsg, aParam.mJitter);
+    WriteParam(aMsg, aParam.mFractionLost);
+    WriteParam(aMsg, aParam.mPacketsDiscarded);
+    ParamTraits<superType>::Write(aMsg, aParam);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mPacketsReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPacketsLost)) &&
+           ReadParam(aMsg, aItr, &(aResult->mJitter)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFractionLost)) &&
+           ReadParam(aMsg, aItr, &(aResult->mPacketsDiscarded)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
+  }
+};
 
 template<>
 struct ParamTraits<mozilla::dom::RTCInboundRTPStreamStats>
 {
   typedef mozilla::dom::RTCInboundRTPStreamStats paramType;
+  typedef mozilla::dom::RTCReceivedRTPStreamStats superType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.mBytesReceived);
-    WriteParam(aMsg, aParam.mDiscardedPackets);
     WriteParam(aMsg, aParam.mFramesDecoded);
-    WriteParam(aMsg, aParam.mJitter);
     WriteParam(aMsg, aParam.mMozAvSyncDelay);
     WriteParam(aMsg, aParam.mMozJitterBufferDelay);
-    WriteParam(aMsg, aParam.mRoundTripTime);
-    WriteParam(aMsg, aParam.mPacketsLost);
-    WriteParam(aMsg, aParam.mPacketsReceived);
-    WriteRTCRTPStreamStats(aMsg, aParam);
-    WriteRTCStats(aMsg, aParam);
+    WriteParam(aMsg, aParam.mBitrateMean);
+    WriteParam(aMsg, aParam.mBitrateStdDev);
+    WriteParam(aMsg, aParam.mFramerateMean);
+    WriteParam(aMsg, aParam.mFramerateStdDev);
+    ParamTraits<superType>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mFramesDecoded)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMozAvSyncDelay)) &&
+           ReadParam(aMsg, aItr, &(aResult->mMozJitterBufferDelay)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBitrateMean)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBitrateStdDev)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramerateMean)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramerateStdDev)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::dom::RTCRemoteInboundRTPStreamStats>
+{
+  typedef mozilla::dom::RTCRemoteInboundRTPStreamStats paramType;
+  typedef mozilla::dom::RTCReceivedRTPStreamStats superType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mBytesReceived)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mDiscardedPackets)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesDecoded)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mJitter)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mMozAvSyncDelay)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mMozJitterBufferDelay)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mRoundTripTime)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPacketsLost)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPacketsReceived)) ||
-        !ReadRTCRTPStreamStats(aMsg, aIter, aResult) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
+    WriteParam(aMsg, aParam.mLocalId);
+    WriteParam(aMsg, aParam.mRoundTripTime);
+    ParamTraits<superType>::Write(aMsg, aParam);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mLocalId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRoundTripTime)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
+  }
+};
 
-    return true;
+template<>
+struct ParamTraits<mozilla::dom::RTCSentRTPStreamStats>
+{
+  typedef mozilla::dom::RTCSentRTPStreamStats paramType;
+  typedef mozilla::dom::RTCRTPStreamStats superType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mPacketsSent);
+    WriteParam(aMsg, aParam.mBytesSent);
+    ParamTraits<superType>::Write(aMsg, aParam);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mPacketsSent)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesSent)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCOutboundRTPStreamStats>
 {
   typedef mozilla::dom::RTCOutboundRTPStreamStats paramType;
+  typedef mozilla::dom::RTCSentRTPStreamStats superType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.mBytesSent);
+    WriteParam(aMsg, aParam.mTargetBitrate);
     WriteParam(aMsg, aParam.mDroppedFrames);
-    WriteParam(aMsg, aParam.mPacketsSent);
-    WriteParam(aMsg, aParam.mTargetBitrate);
     WriteParam(aMsg, aParam.mFramesEncoded);
-    WriteParam(aMsg, aParam.mFirCount);
-    WriteParam(aMsg, aParam.mNackCount);
-    WriteParam(aMsg, aParam.mPliCount);
-    WriteRTCRTPStreamStats(aMsg, aParam);
-    WriteRTCStats(aMsg, aParam);
+    WriteParam(aMsg, aParam.mBitrateMean);
+    WriteParam(aMsg, aParam.mBitrateStdDev);
+    WriteParam(aMsg, aParam.mFramerateMean);
+    WriteParam(aMsg, aParam.mFramerateStdDev);
+    ParamTraits<superType>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mBytesSent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mDroppedFrames)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPacketsSent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTargetBitrate)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesEncoded)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFirCount)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNackCount)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPliCount)) ||
-        !ReadRTCRTPStreamStats(aMsg, aIter, aResult) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
+    return ReadParam(aMsg, aItr, &(aResult->mTargetBitrate)) &&
+           ReadParam(aMsg, aItr, &(aResult->mDroppedFrames)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesEncoded)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBitrateMean)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBitrateStdDev)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramerateMean)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramerateStdDev)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
+  }
+};
+
 
-    return true;
+template<>
+struct ParamTraits<mozilla::dom::RTCRemoteOutboundRTPStreamStats>
+{
+  typedef mozilla::dom::RTCRemoteOutboundRTPStreamStats paramType;
+  typedef mozilla::dom::RTCSentRTPStreamStats superType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mLocalId);
+    WriteParam(aMsg, aParam.mRemoteTimestamp);
+    ParamTraits<superType>::Write(aMsg, aParam);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
+  {
+    return ReadParam(aMsg, aItr, &(aResult->mLocalId)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteTimestamp)) &&
+           ParamTraits<superType>::Read(aMsg, aItr, aResult);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCMediaStreamStats>
 {
   typedef mozilla::dom::RTCMediaStreamStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mStreamIdentifier);
     WriteParam(aMsg, aParam.mTrackIds);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mStreamIdentifier)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTrackIds)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mStreamIdentifier)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTrackIds)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCTransportStats>
 {
   typedef mozilla::dom::RTCTransportStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mBytesReceived);
     WriteParam(aMsg, aParam.mBytesSent);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mBytesReceived)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mBytesSent)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mBytesReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mBytesSent)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCMediaStreamTrackStats>
 {
   typedef mozilla::dom::RTCMediaStreamTrackStats paramType;
 
@@ -486,61 +547,56 @@ struct ParamTraits<mozilla::dom::RTCMedi
     WriteParam(aMsg, aParam.mFramesDecoded);
     WriteParam(aMsg, aParam.mFramesDropped);
     WriteParam(aMsg, aParam.mFramesPerSecond);
     WriteParam(aMsg, aParam.mFramesReceived);
     WriteParam(aMsg, aParam.mFramesSent);
     WriteParam(aMsg, aParam.mRemoteSource);
     WriteParam(aMsg, aParam.mSsrcIds);
     WriteParam(aMsg, aParam.mTrackIdentifier);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mAudioLevel)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mEchoReturnLoss)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mEchoReturnLossEnhancement)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFrameHeight)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFrameWidth)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesCorrupted)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesDecoded)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesDropped)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesPerSecond)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesReceived)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFramesSent)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mRemoteSource)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mSsrcIds)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mTrackIdentifier)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mAudioLevel)) &&
+           ReadParam(aMsg, aItr, &(aResult->mEchoReturnLoss)) &&
+           ReadParam(aMsg, aItr, &(aResult->mEchoReturnLossEnhancement)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFrameHeight)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFrameWidth)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesCorrupted)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesDecoded)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesDropped)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesPerSecond)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesReceived)) &&
+           ReadParam(aMsg, aItr, &(aResult->mFramesSent)) &&
+           ReadParam(aMsg, aItr, &(aResult->mRemoteSource)) &&
+           ReadParam(aMsg, aItr, &(aResult->mSsrcIds)) &&
+           ReadParam(aMsg, aItr, &(aResult->mTrackIdentifier)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::dom::RTCRTPContributingSourceStats>
 {
   typedef mozilla::dom::RTCRTPContributingSourceStats paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mContributorSsrc);
     WriteParam(aMsg, aParam.mInboundRtpStreamId);
-    WriteRTCStats(aMsg, aParam);
+    ParamTraits<mozilla::dom::RTCStats>::Write(aMsg, aParam);
   }
 
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  static bool
+  Read(const Message* aMsg, PickleIterator* aItr, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->mContributorSsrc)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mInboundRtpStreamId)) ||
-        !ReadRTCStats(aMsg, aIter, aResult)) {
-      return false;
-    }
-    return true;
+    return ReadParam(aMsg, aItr, &(aResult->mContributorSsrc)) &&
+           ReadParam(aMsg, aItr, &(aResult->mInboundRtpStreamId)) &&
+           ParamTraits<mozilla::dom::RTCStats>::Read(aMsg, aItr, aResult);
   }
 };
 
 } // namespace ipc
 
 #endif  // _WEBRTC_GLOBAL_H_
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -6,16 +6,18 @@
  * The origin of this IDL file is
  * http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcstatsreport-object
  * http://www.w3.org/2011/04/webrtc/wiki/Stats
  */
 
 enum RTCStatsType {
   "inbound-rtp",
   "outbound-rtp",
+  "remote-inbound-rtp",
+  "remote-outbound-rtp",
   "csrc",
   "session",
   "track",
   "transport",
   "candidate-pair",
   "local-candidate",
   "remote-candidate"
 };
@@ -24,57 +26,69 @@ dictionary RTCStats {
   DOMHighResTimeStamp timestamp;
   RTCStatsType type;
   DOMString id;
 };
 
 dictionary RTCRTPStreamStats : RTCStats {
   DOMString ssrc;
   DOMString mediaType;
-  DOMString remoteId;
-  boolean isRemote = false;
   DOMString mediaTrackId;
   DOMString transportId;
   DOMString codecId;
-
-  // Video encoder/decoder measurements, not present in RTCP case
-  double bitrateMean;
-  double bitrateStdDev;
-  double framerateMean;
-  double framerateStdDev;
-
-  // Local only measurements, RTCP related but not communicated via RTCP. Not
-  // present in RTCP case.
   unsigned long firCount;
   unsigned long pliCount;
   unsigned long nackCount;
 };
 
-dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
-  unsigned long packetsReceived;
+dictionary RTCReceivedRTPStreamStats : RTCRTPStreamStats {
+  unsigned long      packetsReceived;
   unsigned long long bytesReceived;
-  double jitter;
-  unsigned long packetsLost;
-  long mozAvSyncDelay;
-  long mozJitterBufferDelay;
-  long roundTripTime;
+  unsigned long      packetsLost;
+  double             jitter;
+  double             fractionLost;
+  // Bug 1386010 - packetsDiscarded not yet supported for audio
+  unsigned long      packetsDiscarded;
+};
 
-  // Video decoder measurement, not present in RTCP case
-  unsigned long discardedPackets;
+dictionary RTCInboundRTPStreamStats : RTCReceivedRTPStreamStats {
+  DOMString     remoteId;
+  long          mozAvSyncDelay;       // Bug 1386861 - remove
+  long          mozJitterBufferDelay; // Bug 1386861 - remove
   unsigned long framesDecoded;
+  double bitrateMean;       // Bug 1331134 - remove
+  double bitrateStdDev;     // Bug 1331134 - remove
+  double framerateMean;     // Bug 1331134 - remove
+  double framerateStdDev;   // Bug 1331134 - remove
 };
 
-dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
-  unsigned long packetsSent;
+dictionary RTCRemoteInboundRTPStreamStats : RTCReceivedRTPStreamStats {
+  DOMString localId;
+  double    roundTripTime;
+};
+
+dictionary RTCSentRTPStreamStats : RTCRTPStreamStats {
+  unsigned long      packetsSent;
   unsigned long long bytesSent;
-  double targetBitrate;  // config encoder bitrate target of this SSRC in bits/s
+};
 
-  // Video encoder measurements, not present in RTCP case
-  unsigned long droppedFrames;
-  unsigned long framesEncoded;
+dictionary RTCOutboundRTPStreamStats : RTCSentRTPStreamStats {
+  DOMString         remoteId;
+  double            targetBitrate;
+  unsigned long     droppedFrames;
+  unsigned long     framesEncoded;
+  double bitrateMean;       // Bug 1331134 - remove
+  double bitrateStdDev;     // Bug 1331134 - remove
+  double framerateMean;     // Bug 1331134 - remove
+  double framerateStdDev;   // Bug 1331134 - remove
+};
+
+dictionary RTCRemoteOutboundRTPStreamStats : RTCSentRTPStreamStats {
+  DOMString           localId;
+  DOMHighResTimeStamp remoteTimestamp;
 };
 
 dictionary RTCMediaStreamTrackStats : RTCStats {
   DOMString trackIdentifier;      // track.id property
   boolean remoteSource;
   sequence<DOMString> ssrcIds;
   // Stuff that makes sense for video
   unsigned long frameWidth;
@@ -164,33 +178,35 @@ dictionary RTCCodecStats : RTCStats {
   unsigned long channels;          // 2=stereo, missing for most other cases.
   DOMString parameters;            // From SDP description line
 };
 
 // This is the internal representation of the report in this implementation
 // to be received from c++
 
 dictionary RTCStatsReportInternal {
-  DOMString                               pcid = "";
-  sequence<RTCInboundRTPStreamStats>      inboundRTPStreamStats;
-  sequence<RTCOutboundRTPStreamStats>     outboundRTPStreamStats;
-  sequence<RTCRTPContributingSourceStats> rtpContributingSourceStats;
-  sequence<RTCMediaStreamTrackStats>      mediaStreamTrackStats;
-  sequence<RTCMediaStreamStats>           mediaStreamStats;
-  sequence<RTCTransportStats>             transportStats;
-  sequence<RTCIceComponentStats>          iceComponentStats;
-  sequence<RTCIceCandidatePairStats>      iceCandidatePairStats;
-  sequence<RTCIceCandidateStats>          iceCandidateStats;
-  sequence<RTCCodecStats>                 codecStats;
-  DOMString                               localSdp;
-  DOMString                               remoteSdp;
-  DOMHighResTimeStamp                     timestamp;
-  unsigned long                           iceRestarts;
-  unsigned long                           iceRollbacks;
-  boolean                                 closed; // Is the PC now closed
+  DOMString                                 pcid = "";
+  sequence<RTCInboundRTPStreamStats>        inboundRTPStreamStats;
+  sequence<RTCRemoteInboundRTPStreamStats>  remoteInboundRTPStreamStats;
+  sequence<RTCOutboundRTPStreamStats>       outboundRTPStreamStats;
+  sequence<RTCRemoteOutboundRTPStreamStats> remoteOutboundRTPStreamStats;
+  sequence<RTCRTPContributingSourceStats>   rtpContributingSourceStats;
+  sequence<RTCMediaStreamTrackStats>        mediaStreamTrackStats;
+  sequence<RTCMediaStreamStats>             mediaStreamStats;
+  sequence<RTCTransportStats>               transportStats;
+  sequence<RTCIceComponentStats>            iceComponentStats;
+  sequence<RTCIceCandidatePairStats>        iceCandidatePairStats;
+  sequence<RTCIceCandidateStats>            iceCandidateStats;
+  sequence<RTCCodecStats>                   codecStats;
+  DOMString                                 localSdp;
+  DOMString                                 remoteSdp;
+  DOMHighResTimeStamp                       timestamp;
+  unsigned long                             iceRestarts;
+  unsigned long                             iceRollbacks;
+  boolean                                   closed; // Is the PC now closed
 };
 
 [Pref="media.peerconnection.enabled",
 // TODO: Use MapClass here once it's available (Bug 928114)
 // MapClass(DOMString, object)
  JSImplementation="@mozilla.org/dom/rtcstatsreport;1"]
 interface RTCStatsReport {
   readonly maplike<DOMString, object>;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -231,72 +231,57 @@ EverySecondTelemetryCallback_s(nsAutoPtr
       // First, get reports from a second ago, if any, for calculations below
       const Sequence<RTCInboundRTPStreamStats> *lastInboundStats = nullptr;
       {
         auto i = FindId(ctx->mLastReports, r.mPcid);
         if (i != ctx->mLastReports.NoIndex) {
           lastInboundStats = &ctx->mLastReports[i]->mInboundRTPStreamStats.Value();
         }
       }
+
       // Then, look for the things we want telemetry on
       auto& array = r.mInboundRTPStreamStats.Value();
       for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
         auto& s = array[i];
         bool isAudio = (s.mId.Value().Find("audio") != -1);
         if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed() &&
             (s.mPacketsLost.Value() + s.mPacketsReceived.Value()) != 0) {
           HistogramID id;
-          if (s.mIsRemote) {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_PACKETLOSS_RATE :
-                           WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE;
-          } else {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
-                           WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
-          }
+          // TODO @@NG TELEMETRY
+          id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
+                         WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
           // *1000 so we can read in 10's of a percent (permille)
           Accumulate(id,
                      (s.mPacketsLost.Value() * 1000) /
                      (s.mPacketsLost.Value() + s.mPacketsReceived.Value()));
         }
         if (s.mJitter.WasPassed()) {
           HistogramID id;
-          if (s.mIsRemote) {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
-                           WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER;
-          } else {
+          // TODO @@NG TELEMETRY
             id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
                            WEBRTC_VIDEO_QUALITY_INBOUND_JITTER;
-          }
+          //}
           Accumulate(id, s.mJitter.Value());
         }
-        if (s.mRoundTripTime.WasPassed()) {
-          MOZ_ASSERT(s.mIsRemote);
-          HistogramID id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
-                                     WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
-          Accumulate(id, s.mRoundTripTime.Value());
-        }
+        // TODO @@NG TELEMETRY
         if (lastInboundStats && s.mBytesReceived.WasPassed()) {
           auto& laststats = *lastInboundStats;
           auto i = FindId(laststats, s.mId.Value());
           if (i != laststats.NoIndex) {
             auto& lasts = laststats[i];
             if (lasts.mBytesReceived.WasPassed()) {
               auto delta_ms = int32_t(s.mTimestamp.Value() -
                                       lasts.mTimestamp.Value());
               // In theory we're called every second, so delta *should* be in that range.
               // Small deltas could cause errors due to division
               if (delta_ms > 500 && delta_ms < 60000) {
                 HistogramID id;
-                if (s.mIsRemote) {
-                  id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
-                                 WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
-                } else {
-                  id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
-                                 WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
-                }
+                // TODO @NG TELEMETRY
+                id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
+                               WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
                 Accumulate(id, ((s.mBytesReceived.Value() -
                                  lasts.mBytesReceived.Value()) * 8) / delta_ms);
               }
               // We could accumulate values until enough time has passed
               // and then Accumulate() but this isn't that important.
             }
           }
         }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2107,17 +2107,19 @@ PeerConnectionImpl::GetTimeSinceEpoch(DO
 }
 
 class RTCStatsReportInternalConstruct : public RTCStatsReportInternal {
 public:
   RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) {
     mPcid = pcid;
     mRtpContributingSourceStats.Construct();
     mInboundRTPStreamStats.Construct();
+    mRemoteInboundRTPStreamStats.Construct();
     mOutboundRTPStreamStats.Construct();
+    mRemoteOutboundRTPStreamStats.Construct();
     mMediaStreamTrackStats.Construct();
     mMediaStreamStats.Construct();
     mTransportStats.Construct();
     mIceComponentStats.Construct();
     mIceCandidatePairStats.Construct();
     mIceCandidateStats.Construct();
     mCodecStats.Construct();
     mTimestamp.Construct(now);
@@ -3718,50 +3720,48 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
           uint64_t bytesReceived;
           uint32_t packetsLost;
           int32_t rtt;
           if (mp.Conduit()->GetRTCPReceiverReport(&timestamp, &jitterMs,
                                                   &packetsReceived,
                                                   &bytesReceived,
                                                   &packetsLost,
                                                   &rtt)) {
-            remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr;
-            RTCInboundRTPStreamStats s;
+            remoteId = NS_LITERAL_STRING("remote_inbound_rtcp_") + idstr;
+            RTCRemoteInboundRTPStreamStats s;
             s.mTimestamp.Construct(timestamp);
             s.mId.Construct(remoteId);
-            s.mType.Construct(RTCStatsType::Inbound_rtp);
+            s.mType.Construct(RTCStatsType::Remote_inbound_rtp);
             if (ssrc.Length()) {
               s.mSsrc.Construct(ssrc);
             }
             s.mMediaType.Construct(mediaType);
             s.mJitter.Construct(double(jitterMs)/1000);
-            s.mRemoteId.Construct(localId);
-            s.mIsRemote = true;
+            s.mLocalId.Construct(localId);
             s.mPacketsReceived.Construct(packetsReceived);
             s.mBytesReceived.Construct(bytesReceived);
             s.mPacketsLost.Construct(packetsLost);
             if (rtt > 0) {
               s.mRoundTripTime.Construct(rtt);
             }
-            query->report->mInboundRTPStreamStats.Value().AppendElement(s,
+            query->report->mRemoteInboundRTPStreamStats.Value().AppendElement(s,
                                                                         fallible);
           }
         }
         // Then, fill in local side (with cross-link to remote only if present)
         {
           RTCOutboundRTPStreamStats s;
           s.mTimestamp.Construct(query->now);
           s.mId.Construct(localId);
           s.mType.Construct(RTCStatsType::Outbound_rtp);
           if (ssrc.Length()) {
             s.mSsrc.Construct(ssrc);
           }
           s.mMediaType.Construct(mediaType);
           s.mRemoteId.Construct(remoteId);
-          s.mIsRemote = false;
           s.mPacketsSent.Construct(mp.rtp_packets_sent());
           s.mBytesSent.Construct(mp.rtp_bytes_sent());
 
           // Fill in packet type statistics
           webrtc::RtcpPacketTypeCounter counters;
           if (mp.Conduit()->GetSendPacketTypeStats(&counters)) {
             s.mNackCount.Construct(counters.nack_packets);
             // Fill in video only packet type stats
@@ -3808,31 +3808,30 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
         }
         {
           // First, fill in remote stat with rtcp sender data, if present.
           DOMHighResTimeStamp timestamp;
           uint32_t packetsSent;
           uint64_t bytesSent;
           if (mp.Conduit()->GetRTCPSenderReport(&timestamp,
                                                 &packetsSent, &bytesSent)) {
-            remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr;
-            RTCOutboundRTPStreamStats s;
+            remoteId = NS_LITERAL_STRING("remote_outbound_rtcp_") + idstr;
+            RTCRemoteOutboundRTPStreamStats s;
             s.mTimestamp.Construct(timestamp);
             s.mId.Construct(remoteId);
-            s.mType.Construct(RTCStatsType::Outbound_rtp);
+            s.mType.Construct(RTCStatsType::Remote_outbound_rtp);
             if (ssrc.Length()) {
               s.mSsrc.Construct(ssrc);
             }
             s.mMediaType.Construct(mediaType);
-            s.mRemoteId.Construct(localId);
-            s.mIsRemote = true;
+            s.mLocalId.Construct(localId);
             s.mPacketsSent.Construct(packetsSent);
             s.mBytesSent.Construct(bytesSent);
-            query->report->mOutboundRTPStreamStats.Value().AppendElement(s,
-                                                                         fallible);
+            query->report->mRemoteOutboundRTPStreamStats.Value().AppendElement(
+                s, fallible);
           }
         }
         // Then, fill in local side (with cross-link to remote only if present)
         RTCInboundRTPStreamStats s;
         s.mTimestamp.Construct(query->now);
         s.mId.Construct(localId);
         s.mType.Construct(RTCStatsType::Inbound_rtp);
         if (ssrc.Length()) {
@@ -3842,17 +3841,16 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
         unsigned int jitterMs, packetsLost;
         if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
           s.mJitter.Construct(double(jitterMs)/1000);
           s.mPacketsLost.Construct(packetsLost);
         }
         if (remoteId.Length()) {
           s.mRemoteId.Construct(remoteId);
         }
-        s.mIsRemote = false;
         s.mPacketsReceived.Construct(mp.rtp_packets_received());
         s.mBytesReceived.Construct(mp.rtp_bytes_received());
 
         if (query->internalStats && isAudio) {
           int32_t jitterBufferDelay;
           int32_t playoutBufferDelay;
           int32_t avSyncDelta;
           if (mp.Conduit()->GetAVStats(&jitterBufferDelay,
@@ -3873,29 +3871,29 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
           }
         }
         // Lastly, fill in video decoder stats if this is video
         if (!isAudio) {
           double framerateMean;
           double framerateStdDev;
           double bitrateMean;
           double bitrateStdDev;
-          uint32_t discardedPackets;
+          uint32_t packetsDiscarded;
           uint32_t framesDecoded;
           if (mp.Conduit()->GetVideoDecoderStats(&framerateMean,
                                                  &framerateStdDev,
                                                  &bitrateMean,
                                                  &bitrateStdDev,
-                                                 &discardedPackets,
+                                                 &packetsDiscarded,
                                                  &framesDecoded)) {
             s.mFramerateMean.Construct(framerateMean);
             s.mFramerateStdDev.Construct(framerateStdDev);
             s.mBitrateMean.Construct(bitrateMean);
             s.mBitrateStdDev.Construct(bitrateStdDev);
-            s.mDiscardedPackets.Construct(discardedPackets);
+            s.mPacketsDiscarded.Construct(packetsDiscarded);
             s.mFramesDecoded.Construct(framesDecoded);
           }
         }
         query->report->mInboundRTPStreamStats.Value().AppendElement(s,
                                                                     fallible);
         // Fill in Contributing Source statistics
         mp.GetContributingSourceStats(localId,
             query->report->mRtpContributingSourceStats.Value());
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -1009,42 +1009,32 @@ static void StoreLongTermICEStatisticsIm
   // components within a stream didn't have the same types of relayed
   // candidates? I have a feeling that late trickle could cause this, but right
   // now we don't have enough information to detect it (we would need to know
   // the ICE component id for each candidate pair and candidate)
 
   std::map<std::string, StreamResult> streamResults;
 
   // Build list of streams, and whether or not they failed.
-  for (size_t i = 0;
-       i < query->report->mIceCandidatePairStats.Value().Length();
-       ++i) {
-    const RTCIceCandidatePairStats &pair =
-      query->report->mIceCandidatePairStats.Value()[i];
-
+  for (const auto& pair : query->report->mIceCandidatePairStats.Value()) {
     if (!pair.mState.WasPassed() || !pair.mTransportId.WasPassed()) {
       MOZ_CRASH();
       continue;
     }
 
     // Note: we use NrIceMediaStream's name for the
     // RTCIceCandidatePairStats tranportId
     std::string streamId(
       NS_ConvertUTF16toUTF8(pair.mTransportId.Value()).get());
 
     streamResults[streamId].streamSucceeded |=
       pair.mState.Value() == RTCStatsIceCandidatePairState::Succeeded;
   }
 
-  for (size_t i = 0;
-       i < query->report->mIceCandidateStats.Value().Length();
-       ++i) {
-    const RTCIceCandidateStats &cand =
-      query->report->mIceCandidateStats.Value()[i];
-
+  for (const auto& cand: query->report->mIceCandidateStats.Value()) {
     if (!cand.mType.WasPassed() ||
         !cand.mCandidateType.WasPassed() ||
         !cand.mTransport.WasPassed() ||
         !cand.mIpAddress.WasPassed() ||
         !cand.mComponentId.WasPassed()) {
       // Crash on debug, ignore this candidate otherwise.
       MOZ_CRASH();
       continue;
@@ -1102,29 +1092,27 @@ static void StoreLongTermICEStatisticsIm
     // stream ID. This is just the way the stats API is standardized right now.
     // Very confusing.
     std::string streamId(
       NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get());
 
     streamResults[streamId].candidateTypeBitpattern |= candBitmask;
   }
 
-  for (auto& streamResult : streamResults) {
+  for (const auto& streamResult : streamResults) {
     Telemetry::RecordWebrtcIceCandidates(streamResult.second.candidateTypeBitpattern,
                                          streamResult.second.streamSucceeded);
   }
 
   // Beyond ICE, accumulate telemetry for various PER_CALL settings here.
 
   if (query->report->mOutboundRTPStreamStats.WasPassed()) {
-    auto& array = query->report->mOutboundRTPStreamStats.Value();
-    for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
-      auto& s = array[i];
+    for (const auto& s: query->report->mOutboundRTPStreamStats.Value()) {
       bool isVideo = (s.mId.Value().Find("video") != -1);
-      if (!isVideo || s.mIsRemote) {
+      if (!isVideo) {
         continue;
       }
       if (s.mBitrateMean.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
                    uint32_t(s.mBitrateMean.Value() / 1000));
       }
       if (s.mBitrateStdDev.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
@@ -1144,21 +1132,19 @@ static void StoreLongTermICEStatisticsIm
           Accumulate(WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
                      uint32_t(double(s.mDroppedFrames.Value()) / mins));
         }
       }
     }
   }
 
   if (query->report->mInboundRTPStreamStats.WasPassed()) {
-    auto& array = query->report->mInboundRTPStreamStats.Value();
-    for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
-      auto& s = array[i];
+    for (const auto& s: query->report->mInboundRTPStreamStats.Value()) {
       bool isVideo = (s.mId.Value().Find("video") != -1);
-      if (!isVideo || s.mIsRemote) {
+      if (!isVideo) {
         continue;
       }
       if (s.mBitrateMean.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
                    uint32_t(s.mBitrateMean.Value() / 1000));
       }
       if (s.mBitrateStdDev.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
@@ -1167,21 +1153,21 @@ static void StoreLongTermICEStatisticsIm
       if (s.mFramerateMean.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
                    uint32_t(s.mFramerateMean.Value()));
       }
       if (s.mFramerateStdDev.WasPassed()) {
         Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
                    uint32_t(s.mFramerateStdDev.Value() * 10));
       }
-      if (s.mDiscardedPackets.WasPassed() && !query->iceStartTime.IsNull()) {
+      if (s.mPacketsDiscarded.WasPassed() && !query->iceStartTime.IsNull()) {
         double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60;
         if (mins > 0) {
           Accumulate(WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
-                     uint32_t(double(s.mDiscardedPackets.Value()) / mins));
+                     uint32_t(double(s.mPacketsDiscarded.Value()) / mins));
         }
       }
     }
   }
 
   // Finally, store the stats
 
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();