Bug 906990: Part 12. Report statistics from all components when the MediaStreamTrack is not specified. r=ekr, r=jib
authorByron Campen [:bwc] <docfaraday@gmail.com>
Tue, 14 Jan 2014 08:48:27 -0800
changeset 163608 26bd374d35135aee46c08ae0d9a0a20fa6a06e26
parent 163607 03d92a15f8e49c7c8bdfd924ceebd34dad11f0c2
child 163609 61da9e9b3c28045f06bcdffc8c34aafc96e3f375
push idunknown
push userunknown
push dateunknown
reviewersekr, jib
bugs906990
milestone29.0a1
Bug 906990: Part 12. Report statistics from all components when the MediaStreamTrack is not specified. r=ekr, r=jib
dom/media/PeerConnection.js
dom/media/tests/mochitest/pc.js
dom/webidl/RTCStatsReport.webidl
media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/test/mediapipeline_unittest.cpp
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -114,23 +114,27 @@ GlobalPCList.prototype = {
         this._networkdown = true;
       } else if (data == "online") {
         this._networkdown = false;
       }
     }
   },
 
   getStatsForEachPC: function(callback, errorCallback) {
+    function getStatsFromPC(pcref) {
+      if (pcref.get()) {
+        pcref.get().getStatsInternal(null, callback, errorCallback);
+      }
+    }
+
     for (let winId in this._list) {
       if (this._list.hasOwnProperty(winId)) {
         this.removeNullRefs(winId);
         if (this._list[winId]) {
-          this._list[winId].forEach(function(pcref) {
-            pcref.get().getStatsInternal(null, callback, errorCallback);
-          });
+          this._list[winId].forEach(getStatsFromPC);
         }
       }
     }
   },
 
   getLoggingFromFirstPC: function(pattern, callback, errorCallback) {
     for (let winId in this._list) {
       this.removeNullRefs(winId);
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1483,18 +1483,27 @@ PeerConnectionWrapper.prototype = {
     var nin = numTracks(this._pc.getRemoteStreams());
     var nout = numTracks(this._pc.getLocalStreams());
 
     // TODO(Bug 957145): Restore stronger inboundrtp test once Bug 948249 is fixed
     //is(toNum(counters["inboundrtp"]), nin, "Have " + nin + " inboundrtp stat(s)");
     ok(toNum(counters["inboundrtp"]) >= nin, "Have at least " + nin + " inboundrtp stat(s) *");
 
     is(toNum(counters["outboundrtp"]), nout, "Have " + nout + " outboundrtp stat(s)");
-    ok(toNum(counters["localcandidate"]), "Have localcandidate stat(s)");
-    ok(toNum(counters["remotecandidate"]), "Have remotecandidate stat(s)");
+
+    var numLocalCandidates  = toNum(counters["localcandidate"]);
+    var numRemoteCandidates = toNum(counters["remotecandidate"]);
+    // If there are no tracks, there will be no stats either.
+    if (nin + nout > 0) {
+      ok(numLocalCandidates, "Have localcandidate stat(s)");
+      ok(numRemoteCandidates, "Have remotecandidate stat(s)");
+    } else {
+      is(numLocalCandidates, 0, "Have no localcandidate stats");
+      is(numRemoteCandidates, 0, "Have no remotecandidate stats");
+    }
   },
 
   /**
    * Closes the connection
    */
   close : function PCW_close() {
     // It might be that a test has already closed the pc. In those cases
     // we should not fail.
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -99,16 +99,17 @@ dictionary RTCIceCandidatePairStats : RT
 enum RTCStatsIceCandidateType {
   "host",
   "serverreflexive",
   "peerreflexive",
   "relayed"
 };
 
 dictionary RTCIceCandidateStats : RTCStats {
+  DOMString componentId;
   DOMString candidateId;
   DOMString ipAddress;
   long portNumber;
   RTCStatsIceCandidateType candidateType;
 };
 
 dictionary RTCCodecStats : RTCStats {
   unsigned long payloadType;       // As used in RTP encoding.
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -1584,16 +1584,17 @@ static int vcmRxStartICE_m(cc_mcapid_t m
     // Now we have all the pieces, create the pipeline
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
       new mozilla::MediaPipelineReceiveAudio(
         pc.impl()->GetHandle(),
         pc.impl()->GetMainThread().get(),
         pc.impl()->GetSTSThread(),
         stream->GetMediaStream()->GetStream(),
         pc_track_id,
+        level,
         conduit, rtp_flow, rtcp_flow);
 
     nsresult res = pipeline->Init();
     if (NS_FAILED(res)) {
       CSFLogError(logTag, "Failure initializing audio pipeline");
       return VCM_ERROR;
     }
 
@@ -1635,16 +1636,17 @@ static int vcmRxStartICE_m(cc_mcapid_t m
     // Now we have all the pieces, create the pipeline
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
         new mozilla::MediaPipelineReceiveVideo(
             pc.impl()->GetHandle(),
             pc.impl()->GetMainThread().get(),
             pc.impl()->GetSTSThread(),
             stream->GetMediaStream()->GetStream(),
             pc_track_id,
+            level,
             conduit, rtp_flow, rtcp_flow);
 
     nsresult res = pipeline->Init();
     if (NS_FAILED(res)) {
       CSFLogError(logTag, "Failure initializing video pipeline");
       return VCM_ERROR;
     }
 
@@ -2236,16 +2238,17 @@ static int vcmTxStartICE_m(cc_mcapid_t m
 
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
         new mozilla::MediaPipelineTransmit(
             pc.impl()->GetHandle(),
             pc.impl()->GetMainThread().get(),
             pc.impl()->GetSTSThread(),
             stream->GetMediaStream(),
             pc_track_id,
+            level,
             conduit, rtp_flow, rtcp_flow);
 
     nsresult res = pipeline->Init();
     if (NS_FAILED(res)) {
       CSFLogError(logTag, "Failure initializing audio pipeline");
       return VCM_ERROR;
     }
     CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d",
@@ -2286,16 +2289,17 @@ static int vcmTxStartICE_m(cc_mcapid_t m
     // Now we have all the pieces, create the pipeline
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
         new mozilla::MediaPipelineTransmit(
             pc.impl()->GetHandle(),
             pc.impl()->GetMainThread().get(),
             pc.impl()->GetSTSThread(),
             stream->GetMediaStream(),
             pc_track_id,
+            level,
             conduit, rtp_flow, rtcp_flow);
 
     nsresult res = pipeline->Init();
     if (NS_FAILED(res)) {
       CSFLogError(logTag, "Failure initializing video pipeline");
       return VCM_ERROR;
     }
 
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -66,22 +66,24 @@ class MediaPipeline : public sigslot::ha
   enum Direction { TRANSMIT, RECEIVE };
   enum State { MP_CONNECTING, MP_OPEN, MP_CLOSED };
   MediaPipeline(const std::string& pc,
                 Direction direction,
                 nsCOMPtr<nsIEventTarget> main_thread,
                 nsCOMPtr<nsIEventTarget> sts_thread,
                 MediaStream *stream,
                 TrackID track_id,
+                int level,
                 RefPtr<MediaSessionConduit> conduit,
                 RefPtr<TransportFlow> rtp_transport,
                 RefPtr<TransportFlow> rtcp_transport)
       : direction_(direction),
         stream_(stream),
         track_id_(track_id),
+        level_(level),
         conduit_(conduit),
         rtp_transport_(rtp_transport),
         rtp_state_(MP_CONNECTING),
         rtcp_transport_(rtcp_transport),
         rtcp_state_(MP_CONNECTING),
         main_thread_(main_thread),
         sts_thread_(sts_thread),
         rtp_send_srtp_(),
@@ -121,16 +123,17 @@ class MediaPipeline : public sigslot::ha
       DetachMediaStream();
     }
   }
 
   virtual nsresult Init();
 
   virtual Direction direction() const { return direction_; }
   virtual TrackID trackid() const { return track_id_; }
+  virtual int level() const { return level_; }
 
   bool IsDoingRtcpMux() const {
     return (rtp_transport_ == rtcp_transport_);
   }
 
   int32_t rtp_packets_sent() const { return rtp_packets_sent_; }
   int64_t rtp_bytes_sent() const { return rtp_bytes_sent_; }
   int32_t rtcp_packets_sent() const { return rtcp_packets_sent_; }
@@ -189,16 +192,17 @@ class MediaPipeline : public sigslot::ha
                       size_t len);
 
   Direction direction_;
   RefPtr<MediaStream> stream_;  // A pointer to the stream we are servicing.
                                 // Written on the main thread.
                                 // Used on STS and MediaStreamGraph threads.
   TrackID track_id_;            // The track on the stream.
                                 // Written and used as the stream_;
+  int level_; // The m-line index (starting at 1, to match convention)
   RefPtr<MediaSessionConduit> conduit_;  // Our conduit. Written on the main
                                          // thread. Read on STS thread.
 
   // The transport objects. Read/written on STS thread.
   RefPtr<TransportFlow> rtp_transport_;
   State rtp_state_;
   RefPtr<TransportFlow> rtcp_transport_;
   State rtcp_state_;
@@ -310,22 +314,23 @@ private:
 class MediaPipelineTransmit : public MediaPipeline {
  public:
   // Set rtcp_transport to nullptr to use rtcp-mux
   MediaPipelineTransmit(const std::string& pc,
                         nsCOMPtr<nsIEventTarget> main_thread,
                         nsCOMPtr<nsIEventTarget> sts_thread,
                         DOMMediaStream *domstream,
                         TrackID track_id,
+                        int level,
                         RefPtr<MediaSessionConduit> conduit,
                         RefPtr<TransportFlow> rtp_transport,
                         RefPtr<TransportFlow> rtcp_transport) :
       MediaPipeline(pc, TRANSMIT, main_thread, sts_thread,
-                    domstream->GetStream(), track_id, conduit, rtp_transport,
-                    rtcp_transport),
+                    domstream->GetStream(), track_id, level,
+                    conduit, rtp_transport, rtcp_transport),
       listener_(new PipelineListener(conduit)),
       domstream_(domstream)
   {}
 
   // Initialize (stuff here may fail)
   virtual nsresult Init();
 
   // Called on the main thread.
@@ -432,21 +437,22 @@ class MediaPipelineTransmit : public Med
 class MediaPipelineReceive : public MediaPipeline {
  public:
   // Set rtcp_transport to nullptr to use rtcp-mux
   MediaPipelineReceive(const std::string& pc,
                        nsCOMPtr<nsIEventTarget> main_thread,
                        nsCOMPtr<nsIEventTarget> sts_thread,
                        MediaStream *stream,
                        TrackID track_id,
+                       int level,
                        RefPtr<MediaSessionConduit> conduit,
                        RefPtr<TransportFlow> rtp_transport,
                        RefPtr<TransportFlow> rtcp_transport) :
       MediaPipeline(pc, RECEIVE, main_thread, sts_thread,
-                    stream, track_id, conduit, rtp_transport,
+                    stream, track_id, level, conduit, rtp_transport,
                     rtcp_transport),
       segments_added_(0) {
   }
 
   int segments_added() const { return segments_added_; }
 
  protected:
   int segments_added_;
@@ -459,21 +465,22 @@ class MediaPipelineReceive : public Medi
 // rendering audio.
 class MediaPipelineReceiveAudio : public MediaPipelineReceive {
  public:
   MediaPipelineReceiveAudio(const std::string& pc,
                             nsCOMPtr<nsIEventTarget> main_thread,
                             nsCOMPtr<nsIEventTarget> sts_thread,
                             MediaStream *stream,
                             TrackID track_id,
+                            int level,
                             RefPtr<AudioSessionConduit> conduit,
                             RefPtr<TransportFlow> rtp_transport,
                             RefPtr<TransportFlow> rtcp_transport) :
       MediaPipelineReceive(pc, main_thread, sts_thread,
-                           stream, track_id, conduit, rtp_transport,
+                           stream, track_id, level, conduit, rtp_transport,
                            rtcp_transport),
       listener_(new PipelineListener(stream->AsSourceStream(),
                                      track_id, conduit)) {
   }
 
   virtual void DetachMediaStream() {
     ASSERT_ON_THREAD(main_thread_);
     listener_->EndTrack();
@@ -521,21 +528,22 @@ class MediaPipelineReceiveAudio : public
 // rendering video.
 class MediaPipelineReceiveVideo : public MediaPipelineReceive {
  public:
   MediaPipelineReceiveVideo(const std::string& pc,
                             nsCOMPtr<nsIEventTarget> main_thread,
                             nsCOMPtr<nsIEventTarget> sts_thread,
                             MediaStream *stream,
                             TrackID track_id,
+                            int level,
                             RefPtr<VideoSessionConduit> conduit,
                             RefPtr<TransportFlow> rtp_transport,
                             RefPtr<TransportFlow> rtcp_transport) :
       MediaPipelineReceive(pc, main_thread, sts_thread,
-                           stream, track_id, conduit, rtp_transport,
+                           stream, track_id, level, conduit, rtp_transport,
                            rtcp_transport),
       renderer_(new PipelineRenderer(MOZ_THIS_IN_INITIALIZER_LIST())),
       listener_(new PipelineListener(stream->AsSourceStream(), track_id)) {
   }
 
   // Called on the main thread.
   virtual void DetachMediaStream() {
     ASSERT_ON_THREAD(main_thread_);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <cstdlib>
 #include <cerrno>
 #include <deque>
+#include <sstream>
 
 #include "base/histogram.h"
 #include "vcm.h"
 #include "CSFLog.h"
 #include "timecard.h"
 #include "ccapi_call_info.h"
 #include "CC_SIPCCCallInfo.h"
 #include "ccapi_device_info.h"
@@ -27,16 +28,17 @@
 #include "nsIPropertyBag2.h"
 #include "nsIServiceManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsISocketTransportService.h"
 #include "nsIConsoleService.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
+#include "prtime.h"
 
 #include "runnable_utils.h"
 #include "PeerConnectionCtx.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionMedia.h"
 #include "nsDOMDataChannelDeclarations.h"
 #include "dtlsidentity.h"
 
@@ -51,16 +53,17 @@
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsIDocument.h"
 #include "nsIScriptError.h"
 #include "nsPrintfCString.h"
 #include "nsURLHelper.h"
 #include "nsNetUtil.h"
 #include "nsIDOMDataChannel.h"
+#include "nsIDOMLocation.h"
 #include "mozilla/dom/RTCConfigurationBinding.h"
 #include "mozilla/dom/RTCStatsReportBinding.h"
 #include "mozilla/dom/RTCPeerConnectionBinding.h"
 #include "mozilla/dom/PeerConnectionImplBinding.h"
 #include "mozilla/dom/DataChannelBinding.h"
 #include "MediaStreamList.h"
 #include "MediaStreamTrack.h"
 #include "AudioStreamTrack.h"
@@ -710,18 +713,46 @@ PeerConnectionImpl::Initialize(PeerConne
     NS_ENSURE_SUCCESS(res = InitNSSInContent(), res);
   }
 
   // Currently no standalone unit tests for DataChannel,
   // which is the user of mWindow
   MOZ_ASSERT(aWindow);
   mWindow = aWindow;
   NS_ENSURE_STATE(mWindow);
+
 #endif // MOZILLA_INTERNAL_API
 
+  PRTime timestamp = PR_Now();
+  // Ok if we truncate this.
+  char temp[128];
+
+#ifdef MOZILLA_INTERNAL_API
+  nsIDOMLocation* location = nullptr;
+  mWindow->GetLocation(&location);
+  MOZ_ASSERT(location);
+  nsString locationAStr;
+  location->ToString(locationAStr);
+  location->Release();
+
+  nsCString locationCStr;
+  CopyUTF16toUTF8(locationAStr, locationCStr);
+  MOZ_ASSERT(mWindow);
+  PR_snprintf(temp,
+              sizeof(temp),
+              "%llu (id=%u url=%s)",
+              (unsigned long long)timestamp,
+              (unsigned)mWindow->WindowID(),
+              locationCStr.get() ? locationCStr.get() : "NULL");
+#else
+  PR_snprintf(temp, sizeof(temp), "%llu", (unsigned long long)timestamp);
+#endif // MOZILLA_INTERNAL_API
+
+  mName = temp;
+
   // Generate a random handle
   unsigned char handle_bin[8];
   SECStatus rv;
   rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin));
   if (rv != SECSuccess) {
     MOZ_CRASH();
     return NS_ERROR_UNEXPECTED;
   }
@@ -1260,55 +1291,75 @@ NS_IMETHODIMP
 PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector, bool internalStats) {
   PC_AUTO_ENTER_API_CALL(true);
 
 #ifdef MOZILLA_INTERNAL_API
   MOZ_ASSERT(mMedia);
 
   // Gather up pipelines from mMedia and dispatch them to STS for inspection
 
-  nsAutoPtr<std::vector<RefPtr<MediaPipeline>>> pipelines(
-      new std::vector<RefPtr<MediaPipeline>>());
+  std::vector<RefPtr<MediaPipeline>> pipelines;
   TrackID trackId = aSelector ? aSelector->GetTrackID() : 0;
 
   for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
-    PushBackSelect(*pipelines, mMedia->GetLocalStream(i)->GetPipelines(), trackId);
+    PushBackSelect(pipelines, mMedia->GetLocalStream(i)->GetPipelines(), trackId);
   }
   for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
-    PushBackSelect(*pipelines, mMedia->GetRemoteStream(i)->GetPipelines(), trackId);
+    PushBackSelect(pipelines, mMedia->GetRemoteStream(i)->GetPipelines(), trackId);
+  }
+
+  // From the list of MediaPipelines, determine the set of NrIceMediaStreams
+  // we are interested in.
+  std::vector<RefPtr<NrIceMediaStream> > streams;
+  RefPtr<NrIceCtx> iceCtx(mMedia->ice_ctx());
+  for (auto p = pipelines.begin(); p != pipelines.end(); ++p) {
+    size_t level = p->get()->level();
+    // TODO(bcampen@mozilla.com): I may need to revisit this for bundle.
+    // (Bug 786234)
+    RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(level-1));
+    if (temp.get()) {
+      streams.push_back(temp);
+    } else {
+       CSFLogError(logTag, "Failed to get NrIceMediaStream for level %u "
+                           "in %s:  %s",
+                           level, __FUNCTION__, mHandle.c_str());
+       MOZ_CRASH();
+    }
   }
 
   DOMHighResTimeStamp now;
   nsresult rv = GetTimeSinceEpoch(&now);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mSTSThread,
-                WrapRunnable(pc,
-                             &PeerConnectionImpl::GetStats_s,
-                             trackId,
-                             internalStats,
-                             pipelines,
-                             now),
+                WrapRunnableNM(&PeerConnectionImpl::GetStats_s,
+                               mHandle,
+                               mName,
+                               mThread,
+                               internalStats,
+                               pipelines,
+                               iceCtx,
+                               streams,
+                               now),
                 NS_DISPATCH_NORMAL);
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::GetLogging(const nsAString& aPattern) {
   PC_AUTO_ENTER_API_CALL(true);
 
 #ifdef MOZILLA_INTERNAL_API
   std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get());
-  nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mSTSThread,
-                WrapRunnable(pc,
-                             &PeerConnectionImpl::GetLogging_s,
-                             pattern),
+                WrapRunnableNM(&PeerConnectionImpl::GetLogging_s,
+                               mHandle,
+                               mThread,
+                               pattern),
                 NS_DISPATCH_NORMAL);
 
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
@@ -1744,16 +1795,23 @@ PeerConnectionWrapper::PeerConnectionWra
 
 const std::string&
 PeerConnectionImpl::GetHandle()
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   return mHandle;
 }
 
+const std::string&
+PeerConnectionImpl::GetName()
+{
+  PC_AUTO_ENTER_API_CALL_NO_CHECK();
+  return mName;
+}
+
 static mozilla::dom::PCImplIceConnectionState
 toDomIceConnectionState(NrIceCtx::ConnectionState state) {
   switch (state) {
     case NrIceCtx::ICE_CTX_INIT:
       return PCImplIceConnectionState::New;
     case NrIceCtx::ICE_CTX_CHECKING:
       return PCImplIceConnectionState::Checking;
     case NrIceCtx::ICE_CTX_OPEN:
@@ -1891,25 +1949,26 @@ PeerConnectionImpl::IceGatheringStateCha
                              rv, static_cast<JSCompartment*>(nullptr)),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 #ifdef MOZILLA_INTERNAL_API
 nsresult
 PeerConnectionImpl::GetStatsImpl_s(
-    TrackID trackId,
     bool internalStats,
-    nsAutoPtr<std::vector<RefPtr<MediaPipeline>>> pipelines,
+    const std::vector<RefPtr<MediaPipeline>>& pipelines,
+    const RefPtr<NrIceCtx>& iceCtx,
+    const std::vector<RefPtr<NrIceMediaStream>>& streams,
     DOMHighResTimeStamp now,
-    RTCStatsReportInternal *report) {
+    RTCStatsReportInternal* report) {
 
   // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
 
-  for (auto it = pipelines->begin(); it != pipelines->end(); ++it) {
+  for (auto it = pipelines.begin(); it != pipelines.end(); ++it) {
     const MediaPipeline& mp = **it;
     nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ?
         NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
     idstr.AppendInt(mp.trackid());
 
     switch (mp.direction()) {
       case MediaPipeline::TRANSMIT: {
         RTCOutboundRTPStreamStats s;
@@ -1933,162 +1992,194 @@ PeerConnectionImpl::GetStatsImpl_s(
         s.mPacketsReceived.Construct(mp.rtp_packets_received());
         s.mBytesReceived.Construct(mp.rtp_bytes_received());
         report->mInboundRTPStreamStats.Value().AppendElement(s);
         break;
       }
     }
   }
 
-  if (mMedia) {
-
-    // Gather stats from ICE
-
-    RefPtr<NrIceMediaStream> mediaStream(mMedia->ice_media_stream(trackId));
-    if (mediaStream) {
-      std::vector<NrIceCandidatePair> candPairs;
-      mediaStream->GetCandidatePairs(&candPairs);
-      NS_ConvertASCIItoUTF16 componentId(mediaStream->name().c_str());
-      for (auto p = candPairs.begin(); p != candPairs.end(); ++p) {
-        NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str());
-        NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str());
-        NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str());
-        // Only expose candidate-pair statistics to chrome, until we've thought
-        // through the implications of exposing it to content.
-
-        if (internalStats) {
-          RTCIceCandidatePairStats s;
-          s.mId.Construct(codeword);
-          s.mComponentId.Construct(componentId);
-          s.mTimestamp.Construct(now);
-          s.mType.Construct(RTCStatsType::Candidatepair);
+  // Gather stats from ICE
+  for (auto s = streams.begin(); s != streams.end(); ++s) {
+    FillStatsReport_s(**s, internalStats, now, report);
+  }
 
-          // Not quite right; we end up with duplicate candidates. Will fix.
-          s.mLocalCandidateId.Construct(localCodeword);
-          s.mRemoteCandidateId.Construct(remoteCodeword);
-          s.mNominated.Construct(p->nominated);
-          s.mMozPriority.Construct(p->priority);
-          s.mSelected.Construct(p->selected);
-          s.mState.Construct(RTCStatsIceCandidatePairState(p->state));
-          report->mIceCandidatePairStats.Value().AppendElement(s);
-        }
-
-        {
-          RTCIceCandidateStats local;
-          local.mId.Construct(localCodeword);
-          local.mTimestamp.Construct(now);
-          local.mType.Construct(RTCStatsType::Localcandidate);
-          local.mCandidateType.Construct(
-              RTCStatsIceCandidateType(p->local.type));
-          local.mIpAddress.Construct(
-              NS_ConvertASCIItoUTF16(p->local.cand_addr.host.c_str()));
-          local.mPortNumber.Construct(p->local.cand_addr.port);
-          report->mIceCandidateStats.Value().AppendElement(local);
-        }
-
-        {
-          RTCIceCandidateStats remote;
-          remote.mId.Construct(remoteCodeword);
-          remote.mTimestamp.Construct(now);
-          remote.mType.Construct(RTCStatsType::Remotecandidate);
-          remote.mCandidateType.Construct(
-              RTCStatsIceCandidateType(p->remote.type));
-          remote.mIpAddress.Construct(
-              NS_ConvertASCIItoUTF16(p->remote.cand_addr.host.c_str()));
-          remote.mPortNumber.Construct(p->remote.cand_addr.port);
-          report->mIceCandidateStats.Value().AppendElement(remote);
-        }
-      }
-    }
-  }
   return NS_OK;
 }
 
+void PeerConnectionImpl::FillStatsReport_s(
+    NrIceMediaStream& mediaStream,
+    bool internalStats,
+    DOMHighResTimeStamp now,
+    RTCStatsReportInternal* report) {
+  std::vector<NrIceCandidatePair> candPairs;
+  nsresult res = mediaStream.GetCandidatePairs(&candPairs);
+
+  if (NS_FAILED(res)) {
+    CSFLogError(logTag, "%s: Error getting candidate pairs", __FUNCTION__);
+    return;
+  }
+
+  NS_ConvertASCIItoUTF16 componentId(mediaStream.name().c_str());
+  for (auto p = candPairs.begin(); p != candPairs.end(); ++p) {
+    NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str());
+    NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str());
+    NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str());
+    // Only expose candidate-pair statistics to chrome, until we've thought
+    // through the implications of exposing it to content.
+
+    if (internalStats) {
+      RTCIceCandidatePairStats s;
+      s.mId.Construct(codeword);
+      s.mComponentId.Construct(componentId);
+      s.mTimestamp.Construct(now);
+      s.mType.Construct(RTCStatsType::Candidatepair);
+
+      s.mLocalCandidateId.Construct(localCodeword);
+      s.mRemoteCandidateId.Construct(remoteCodeword);
+      s.mNominated.Construct(p->nominated);
+      s.mMozPriority.Construct(p->priority);
+      s.mSelected.Construct(p->selected);
+      s.mState.Construct(RTCStatsIceCandidatePairState(p->state));
+      report->mIceCandidatePairStats.Value().AppendElement(s);
+    }
+
+    {
+      RTCIceCandidateStats local;
+      local.mComponentId.Construct(componentId);
+      local.mId.Construct(localCodeword);
+      local.mTimestamp.Construct(now);
+      local.mType.Construct(RTCStatsType::Localcandidate);
+      local.mCandidateType.Construct(
+          RTCStatsIceCandidateType(p->local.type));
+      local.mIpAddress.Construct(
+          NS_ConvertASCIItoUTF16(p->local.cand_addr.host.c_str()));
+      local.mPortNumber.Construct(p->local.cand_addr.port);
+      report->mIceCandidateStats.Value().AppendElement(local);
+    }
+
+    {
+      RTCIceCandidateStats remote;
+      remote.mComponentId.Construct(componentId);
+      remote.mId.Construct(remoteCodeword);
+      remote.mTimestamp.Construct(now);
+      remote.mType.Construct(RTCStatsType::Remotecandidate);
+      remote.mCandidateType.Construct(
+          RTCStatsIceCandidateType(p->remote.type));
+      remote.mIpAddress.Construct(
+          NS_ConvertASCIItoUTF16(p->remote.cand_addr.host.c_str()));
+      remote.mPortNumber.Construct(p->remote.cand_addr.port);
+      report->mIceCandidateStats.Value().AppendElement(remote);
+    }
+  }
+}
+
 void PeerConnectionImpl::GetStats_s(
-    TrackID trackId,
+    const std::string& pcHandle, // The Runnable holds the memory
+    const std::string& pcName, // The Runnable holds the memory
+    nsCOMPtr<nsIThread> callbackThread,
     bool internalStats,
-    nsAutoPtr<std::vector<RefPtr<MediaPipeline>>> pipelines,
+    const std::vector<RefPtr<MediaPipeline>>& pipelines,
+    const RefPtr<NrIceCtx>& iceCtx,
+    const std::vector<RefPtr<NrIceMediaStream>>& streams,
     DOMHighResTimeStamp now) {
 
-  nsAutoPtr<RTCStatsReportInternal> report(new RTCStatsReportInternalConstruct(
-      NS_ConvertASCIItoUTF16(mHandle.c_str()), now));
-
-  nsresult rv = report ? GetStatsImpl_s(trackId, internalStats, pipelines, now,
-                                        report)
-                       : NS_ERROR_UNEXPECTED;
+  // We do not use the pcHandle here, since that's risky to expose to content.
+  nsAutoPtr<RTCStatsReportInternal> report(
+      new RTCStatsReportInternalConstruct(
+          NS_ConvertASCIItoUTF16(pcName.c_str()),
+          now));
 
-  nsRefPtr<PeerConnectionImpl> pc(this);
-  RUN_ON_THREAD(mThread,
-                WrapRunnable(pc,
-                             &PeerConnectionImpl::OnStatsReport_m,
-                             rv,
-                             pipelines, // return for release on main thread
-                             report),
+  nsresult rv = GetStatsImpl_s(internalStats,
+                               pipelines,
+                               iceCtx,
+                               streams,
+                               now,
+                               report);
+
+  RUN_ON_THREAD(callbackThread,
+                WrapRunnableNM(&PeerConnectionImpl::OnStatsReport_m,
+                               pcHandle,
+                               rv,
+                               pipelines, // return for release on main thread
+                               report),
                 NS_DISPATCH_NORMAL);
 }
 
 void PeerConnectionImpl::OnStatsReport_m(
+    const std::string& pcHandle,
     nsresult result,
-    nsAutoPtr<std::vector<RefPtr<MediaPipeline>>> pipelines, //returned for release
+    const std::vector<RefPtr<MediaPipeline>>& pipelines, //returned for release
     nsAutoPtr<RTCStatsReportInternal> report) {
-  nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
-  if (pco) {
-    JSErrorResult rv;
-    if (NS_SUCCEEDED(result)) {
-      pco->OnGetStatsSuccess(*report, rv);
-    } else {
-      pco->OnGetStatsError(kInternalError,
-                           ObString("Failed to fetch statistics"),
-                           rv);
-    }
 
-    if (rv.Failed()) {
-      CSFLogError(logTag, "Error firing stats observer callback");
+  // Is the PeerConnectionImpl still around?
+  PeerConnectionWrapper pcw(pcHandle);
+  if (pcw.impl()) {
+    nsRefPtr<PeerConnectionObserver> pco =
+        do_QueryObjectReferent(pcw.impl()->mPCObserver);
+    if (pco) {
+      JSErrorResult rv;
+      if (NS_SUCCEEDED(result)) {
+        pco->OnGetStatsSuccess(*report, rv);
+      } else {
+        pco->OnGetStatsError(kInternalError,
+            ObString("Failed to fetch statistics"),
+            rv);
+      }
+
+      if (rv.Failed()) {
+        CSFLogError(logTag, "Error firing stats observer callback");
+      }
     }
   }
 }
 
-void PeerConnectionImpl::GetLogging_s(const std::string& pattern) {
+void PeerConnectionImpl::GetLogging_s(const std::string& pcHandle,
+                                      nsCOMPtr<nsIThread> callbackThread,
+                                      const std::string& pattern) {
   RLogRingBuffer* logs = RLogRingBuffer::GetInstance();
-  std::deque<std::string> result;
-  logs->Filter(pattern, 0, &result);
-  nsRefPtr<PeerConnectionImpl> pc(this);
-  RUN_ON_THREAD(mThread,
-                WrapRunnable(pc,
-                             &PeerConnectionImpl::OnGetLogging_m,
-                             pattern,
-                             result),
+  nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>);
+  logs->Filter(pattern, 0, result);
+  RUN_ON_THREAD(callbackThread,
+                WrapRunnableNM(&PeerConnectionImpl::OnGetLogging_m,
+                               pcHandle,
+                               pattern,
+                               result),
                 NS_DISPATCH_NORMAL);
 }
 
-void PeerConnectionImpl::OnGetLogging_m(const std::string& pattern,
-                                        const std::deque<std::string>& logging) {
-  nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
-  if (!pco) {
-    return;
-  }
+void PeerConnectionImpl::OnGetLogging_m(
+    const std::string& pcHandle,
+    const std::string& pattern,
+    nsAutoPtr<std::deque<std::string>> logging) {
 
-  JSErrorResult rv;
-  if (!logging.empty()) {
-    Sequence<nsString> nsLogs;
-    for (auto l = logging.begin(); l != logging.end(); ++l) {
-      nsLogs.AppendElement(ObString(l->c_str()));
+  // Is the PeerConnectionImpl still around?
+  PeerConnectionWrapper pcw(pcHandle);
+  if (pcw.impl()) {
+    nsRefPtr<PeerConnectionObserver> pco =
+        do_QueryObjectReferent(pcw.impl()->mPCObserver);
+    if (pco) {
+      JSErrorResult rv;
+      if (!logging->empty()) {
+        Sequence<nsString> nsLogs;
+        for (auto l = logging->begin(); l != logging->end(); ++l) {
+          nsLogs.AppendElement(ObString(l->c_str()));
+        }
+        pco->OnGetLoggingSuccess(nsLogs, rv);
+      } else {
+        pco->OnGetLoggingError(kInternalError,
+            ObString(("No logging matching pattern " + pattern).c_str()), rv);
+      }
+
+      if (rv.Failed()) {
+        CSFLogError(logTag, "Error firing stats observer callback");
+      }
     }
-    pco->OnGetLoggingSuccess(nsLogs, rv);
-  } else {
-    pco->OnGetLoggingError(kInternalError,
-        ObString(("No logging matching pattern " + pattern).c_str()), rv);
-  }
-
-  if (rv.Failed()) {
-    CSFLogError(logTag, "Error firing stats observer callback");
   }
 }
-
-
 #endif
 
 void
 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aStream);
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -218,16 +218,19 @@ public:
   const nsRefPtr<PeerConnectionMedia>& media() const {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mMedia;
   }
 
   // Handle system to allow weak references to be passed through C code
   virtual const std::string& GetHandle();
 
+  // Name suitable for exposing to content
+  virtual const std::string& GetName();
+
   // ICE events
   void IceConnectionStateChange(NrIceCtx* ctx,
                                 NrIceCtx::ConnectionState state);
   void IceGatheringStateChange(NrIceCtx* ctx,
                                NrIceCtx::GatheringState state);
   void IceStreamReady(NrIceMediaStream *aStream);
 
   static void ListenThread(void *aData);
@@ -536,42 +539,61 @@ private:
   nsresult IceGatheringStateChange_m(
       mozilla::dom::PCImplIceGatheringState aState);
 
   NS_IMETHOD FingerprintSplitHelper(
       std::string& fingerprint, size_t& spaceIdx) const;
 
 
 #ifdef MOZILLA_INTERNAL_API
+  // TODO(bcampen@mozilla.com): Once the dust settles on this stuff, it
+  // probably makes sense to make these static in PeerConnectionImpl.cpp
+  // (ie; stop exporting them)
+
   // Fills in an RTCStatsReportInternal. Must be run on STS.
-  void GetStats_s(
-      mozilla::TrackID trackId,
+  static void GetStats_s(
+      const std::string& pcHandle,
+      const std::string& pcName,
+      nsCOMPtr<nsIThread> callbackThread,
       bool internalStats,
-      nsAutoPtr<std::vector<mozilla::RefPtr<mozilla::MediaPipeline>>> pipelines,
+      const std::vector<mozilla::RefPtr<mozilla::MediaPipeline>> &pipelines,
+      const mozilla::RefPtr<NrIceCtx> &iceCtx,
+      const std::vector<mozilla::RefPtr<NrIceMediaStream>> &streams,
       DOMHighResTimeStamp now);
 
-  nsresult GetStatsImpl_s(
-      mozilla::TrackID trackId,
+  static nsresult GetStatsImpl_s(
       bool internalStats,
-      nsAutoPtr<std::vector<mozilla::RefPtr<mozilla::MediaPipeline>>> pipelines,
+      const std::vector<mozilla::RefPtr<mozilla::MediaPipeline>> &pipelines,
+      const mozilla::RefPtr<NrIceCtx> &iceCtx,
+      const std::vector<mozilla::RefPtr<NrIceMediaStream>> &streams,
       DOMHighResTimeStamp now,
       mozilla::dom::RTCStatsReportInternal *report);
 
+  static void FillStatsReport_s(
+      NrIceMediaStream& stream,
+      bool internalStats,
+      DOMHighResTimeStamp now,
+      mozilla::dom::RTCStatsReportInternal* stats);
+
   // Sends an RTCStatsReport to JS. Must run on main thread.
-  void OnStatsReport_m(
+  static void OnStatsReport_m(
+      const std::string& pcHandle,
       nsresult result,
-      nsAutoPtr<std::vector<mozilla::RefPtr<mozilla::MediaPipeline>>> pipelines,
+      const std::vector<mozilla::RefPtr<mozilla::MediaPipeline>> &pipelines,
       nsAutoPtr<mozilla::dom::RTCStatsReportInternal> report);
 
   // Fetches logs matching pattern from RLogRingBuffer. Must be run on STS.
-  void GetLogging_s(const std::string& pattern);
+  static void GetLogging_s(const std::string& pcHandle,
+                           nsCOMPtr<nsIThread> callbackThread,
+                           const std::string& pattern);
 
   // Sends logging to JS. Must run on main thread.
-  void OnGetLogging_m(const std::string& pattern,
-                      const std::deque<std::string>& logging);
+  static void OnGetLogging_m(const std::string& pcHandle,
+                             const std::string& pattern,
+                             nsAutoPtr<std::deque<std::string>> logging);
 #endif
 
   // Timecard used to measure processing time. This should be the first class
   // attribute so that we accurately measure the time required to instantiate
   // any other attributes of this class.
   Timecard *mTimeCard;
 
   // The call
@@ -601,16 +623,19 @@ private:
   std::string mRemoteFingerprint;
 
   // The DTLS identity
   mozilla::RefPtr<DtlsIdentity> mIdentity;
 
   // A handle to refer to this PC with
   std::string mHandle;
 
+  // A name for this PC that we are willing to expose to content.
+  std::string mName;
+
   // The target to run stuff on
   nsCOMPtr<nsIEventTarget> mSTSThread;
 
 #ifdef MOZILLA_INTERNAL_API
   // DataConnection that's used to get all the DataChannels
 	nsRefPtr<mozilla::DataChannelConnection> mDataConnection;
 #endif
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -143,17 +143,17 @@ PeerConnectionMedia::PeerConnectionMedia
       mMainThread(mParent->GetMainThread()),
       mSTSThread(mParent->GetSTSThread()) {}
 
 nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
                                    const std::vector<NrIceTurnServer>& turn_servers)
 {
   // TODO(ekr@rtfm.com): need some way to set not offerer later
   // Looks like a bug in the NrIceCtx API.
-  mIceCtx = NrIceCtx::Create("PC:" + mParent->GetHandle(), true);
+  mIceCtx = NrIceCtx::Create("PC:" + mParent->GetName(), true);
   if(!mIceCtx) {
     CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
   nsresult rv;
   if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) {
     CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
     return rv;
@@ -186,21 +186,21 @@ nsresult PeerConnectionMedia::Init(const
   mIceCtx->SignalConnectionStateChange.connect(
       this,
       &PeerConnectionMedia::IceConnectionStateChange);
 
   // Create three streams to start with.
   // One each for audio, video and DataChannel
   // TODO: this will be re-visited
   RefPtr<NrIceMediaStream> audioStream =
-    mIceCtx->CreateStream((mParent->GetHandle()+"/stream1/audio").c_str(), 2);
+    mIceCtx->CreateStream((mParent->GetName()+": stream1/audio").c_str(), 2);
   RefPtr<NrIceMediaStream> videoStream =
-    mIceCtx->CreateStream((mParent->GetHandle()+"/stream2/video").c_str(), 2);
+    mIceCtx->CreateStream((mParent->GetName()+": stream2/video").c_str(), 2);
   RefPtr<NrIceMediaStream> dcStream =
-    mIceCtx->CreateStream((mParent->GetHandle()+"/stream3/data").c_str(), 2);
+    mIceCtx->CreateStream((mParent->GetName()+": stream3/data").c_str(), 2);
 
   if (!audioStream) {
     CSFLogError(logTag, "%s: audio stream is NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
   } else {
     mIceStreams.push_back(audioStream);
   }
 
--- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp
@@ -192,16 +192,17 @@ class TestAgentSend : public TestAgent {
     }
 
     audio_pipeline_ = new mozilla::MediaPipelineTransmit(
         test_pc,
         nullptr,
         test_utils->sts_target(),
         audio_,
         1,
+        1,
         audio_conduit_,
         audio_rtp_transport_.flow_,
         audio_rtcp_transport_.flow_);
 
     audio_pipeline_->Init();
   }
 
   int GetAudioRtpCount() {
@@ -241,17 +242,17 @@ class TestAgentReceive : public TestAgen
     if (aIsRtcpMux) {
       ASSERT_FALSE(audio_rtcp_transport_.flow_);
     }
 
     audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(
         test_pc,
         nullptr,
         test_utils->sts_target(),
-        audio_->GetStream(), 1,
+        audio_->GetStream(), 1, 1,
         static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get()),
         audio_rtp_transport_.flow_, audio_rtcp_transport_.flow_);
 
     audio_pipeline_->Init();
   }
 
   int GetAudioRtpCount() {
     return audio_pipeline_->rtp_packets_received();