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 id26002
push userryanvm@gmail.com
push dateWed, 15 Jan 2014 19:34:12 +0000
treeherdermozilla-central@09a8ed7cbb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersekr, jib
bugs906990
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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();