Bug 1463430 - persist webrtc stats ids draft
authorNico Grunbaum
Tue, 29 May 2018 21:05:13 -0700
changeset 809313 89f7a3ac36af54544069369f99f1a54371bb8e79
parent 809312 96399298b72f804bc5ce8d7dbf2039d108acd49b
push id113632
push userna-g@nostrum.com
push dateThu, 21 Jun 2018 20:06:27 +0000
bugs1463430
milestone62.0a1
Bug 1463430 - persist webrtc stats ids MozReview-Commit-ID: AdMLp96JmCf
dom/media/tests/mochitest/mochitest.ini
dom/media/tests/mochitest/test_peerConnection_stats_persist_ids.html
media/webrtc/signaling/gtest/mediaconduit_unittests.cpp
media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
media/webrtc/signaling/gtest/videoconduit_unittests.cpp
media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
media/webrtc/signaling/src/media-conduit/AudioConduit.h
media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
media/webrtc/signaling/src/media-conduit/MediaConduitStatisticsId.h
media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
media/webrtc/signaling/src/media-conduit/VideoConduit.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
media/webrtc/signaling/src/peerconnection/TransceiverImpl.h
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -332,16 +332,18 @@ skip-if = (android_version == '18') # an
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_threeUnbundledConnections.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_selftest.html]
 # Bug 1227781: Crash with bogus TURN server.
 [test_peerConnection_bug1227781.html]
 [test_peerConnection_stats.html]
 skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator, Bug 1373858)
+[test_peerConnection_stats_persist_ids.html]
+skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator, Bug 1373858)
 [test_peerConnection_sender_and_receiver_stats.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_trackless_sender_stats.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_verifyDescriptions.html]
 skip-if = (android_version == '18')
 [test_fingerprinting_resistance.html]
 [test_getUserMedia_nonDefaultRate.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_stats_persist_ids.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+createHTML({
+  bug: "1463610",
+  title: "Persist WebRTC Stats Ids"
+});
+
+const NUMBER_OF_TRACKS_PER_TYPE = 5;
+
+const checkSenderReceiverStatsIdPersistence = async test => {
+  const pcStats = await test.pcLocal.getStats();
+  pcStats.forEach((v, k) => {
+    dump(`@@NG received key ${k}\n`);
+  });
+};
+
+// This MUST be run after PC_*_WAIT_FOR_MEDIA_FLOW to ensure that we have RTP
+// before checking for RTCP.
+const waitForSyncedRtcp = async pc => {
+  // Ensures that RTCP is present
+  const ensureSyncedRtcp = async () => {
+    const 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 == "inbound-rtp" && v.isRemote == true
+          && v.roundTripTime === undefined) {
+        throw new Error(v.id + " is missing roundTripTime: "
+          + JSON.stringify(v));
+      }
+    }
+    return stats;
+  }
+  const waitPeriod = 500;
+  const maxTime = 15000;
+  for (let totalTime = maxTime; totalTime > 0; totalTime -= waitPeriod) {
+    try {
+      return await ensureSyncedRtcp();
+    } catch (e) {
+      info(e);
+      await wait(waitPeriod);
+    }
+  }
+  throw new Error("Waiting for synced RTCP timed out after at least " + maxTime
+    + "ms");
+}
+
+const PC_TEST_STATS_PERSISTENCE = async test => {
+  await waitForSyncedRtcp(test.pcLocal);
+  await waitForSyncedRtcp(test.pcRemote);
+  await checkSenderReceiverStatsIdPersistence(test);
+}
+
+runNetworkTest(function (options) {
+  const test = new PeerConnectionTest(options);
+  test.chain.removeAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW");
+  test.chain.append([PC_TEST_STATS_PERSISTENCE]);
+
+  const constraints = Array(NUMBER_OF_TRACKS_PER_TYPE)
+                        .fill([{audio: true}, {video: true}]).flatten();
+  test.setMediaConstraints(constraints, constraints);
+  test.run();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp
+++ b/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp
@@ -343,16 +343,18 @@ private:
   int numPkts;
   bool mAudio, mVideo;
 };
 
 using namespace mozilla;
 
 namespace test {
 
+static const MediaConduitStatisticsId sDummyStatsId{0};
+
 class TransportConduitTest : public ::testing::Test
 {
  public:
 
   TransportConduitTest()
   {
     //input and output file names
     iAudiofilename = "input.wav";
@@ -373,22 +375,22 @@ class TransportConduitTest : public ::te
     mVideoTransport = nullptr;
   }
 
   //1. Dump audio samples to dummy external transport
   void TestDummyAudioAndTransport()
   {
     //get pointer to AudioSessionConduit
     int err=0;
-    mAudioSession = mozilla::AudioSessionConduit::Create();
+    mAudioSession = mozilla::AudioSessionConduit::Create(sDummyStatsId);
     if( !mAudioSession ) {
       ASSERT_NE(mAudioSession, (void*)nullptr);
     }
 
-    mAudioSession2 = mozilla::AudioSessionConduit::Create();
+    mAudioSession2 = mozilla::AudioSessionConduit::Create(sDummyStatsId);
     if( !mAudioSession2 ) {
       ASSERT_NE(mAudioSession2, (void*)nullptr);
     }
 
     WebrtcMediaTransport* xport = new WebrtcMediaTransport();
     ASSERT_NE(xport, (void*)nullptr);
     xport->SetAudioSession(mAudioSession, mAudioSession2);
     mAudioTransport = xport;
@@ -434,17 +436,18 @@ class TransportConduitTest : public ::te
     cerr << "   ******************************************************** " << endl;
   }
 
   void TestVideoConduitCodecAPI()
   {
     int err = 0;
     RefPtr<mozilla::VideoSessionConduit> videoSession;
     //get pointer to VideoSessionConduit
-    videoSession = VideoSessionConduit::Create(WebRtcCallWrapper::Create());
+    videoSession = VideoSessionConduit::Create(WebRtcCallWrapper::Create(),
+                                               sDummyStatsId);
     if( !videoSession ) {
       ASSERT_NE(videoSession, (void*)nullptr);
     }
 
     std::vector<unsigned int> ssrcs = {SSRC};
     videoSession->SetLocalSSRCs(ssrcs);
 
     //Test Configure Recv Codec APIS
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
@@ -220,21 +220,23 @@ class TransportInfo {
     loopback_ = nullptr;
     flow_ = nullptr;
   }
 
   RefPtr<TransportFlow> flow_;
   TransportLayerLoopback *loopback_;
 };
 
+static const MediaConduitStatisticsId sDummyStatsId{0};
+
 class TestAgent {
  public:
   TestAgent() :
       audio_config_(109, "opus", 48000, 960, 2, 64000, false),
-      audio_conduit_(mozilla::AudioSessionConduit::Create()),
+      audio_conduit_(mozilla::AudioSessionConduit::Create(sDummyStatsId)),
       audio_pipeline_(),
       use_bundle_(false) {
   }
 
   static void ConnectRtp(TestAgent *client, TestAgent *server) {
     TransportInfo::InitAndConnect(client->audio_rtp_transport_,
                                   server->audio_rtp_transport_);
   }
--- a/media/webrtc/signaling/gtest/videoconduit_unittests.cpp
+++ b/media/webrtc/signaling/gtest/videoconduit_unittests.cpp
@@ -76,27 +76,30 @@ public:
     mVideoFrame = frame;
     ++mOnFrameCount;
   }
 
   size_t mOnFrameCount = 0;
   webrtc::VideoFrame mVideoFrame;
 };
 
+static const MediaConduitStatisticsId sDummyStatsId{0};
+
 class VideoConduitTest : public ::testing::Test {
 public:
 
   VideoConduitTest()
     : mCall(new MockCall())
     , mAdapter(new MockVideoAdapter)
   {
     NSS_NoDB_Init(nullptr);
 
     mVideoConduit = new WebrtcVideoConduit(WebRtcCallWrapper::Create(UniquePtr<MockCall>(mCall)),
-                                           UniquePtr<cricket::VideoAdapter>(mAdapter));
+                                           UniquePtr<cricket::VideoAdapter>(mAdapter),
+                                           sDummyStatsId);
     std::vector<unsigned int> ssrcs = {42};
     mVideoConduit->SetLocalSSRCs(ssrcs);
   }
 
   ~VideoConduitTest() override
   {
     mVideoConduit->DeleteStreams();
   }
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -38,25 +38,27 @@ static const char* acLogTag ="WebrtcAudi
 #undef LOGTAG
 #endif
 #define LOGTAG acLogTag
 
 // 32 bytes is what WebRTC CodecInst expects
 const unsigned int WebrtcAudioConduit::CODEC_PLNAME_SIZE = 32;
 
 using LocalDirection = MediaSessionConduitLocalDirection;
+
 /**
  * Factory Method for AudioConduit
  */
-RefPtr<AudioSessionConduit> AudioSessionConduit::Create()
+RefPtr<AudioSessionConduit>
+AudioSessionConduit::Create(MediaConduitStatisticsId aStatsId)
 {
   CSFLogDebug(LOGTAG,  "%s ", __FUNCTION__);
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  WebrtcAudioConduit* obj = new WebrtcAudioConduit();
+  WebrtcAudioConduit* obj = new WebrtcAudioConduit(aStatsId);
   if(obj->Init() != kMediaConduitNoError)
   {
     CSFLogError(LOGTAG,  "%s AudioConduit Init Failed ", __FUNCTION__);
     delete obj;
     return nullptr;
   }
   CSFLogDebug(LOGTAG,  "%s Successfully created AudioConduit ", __FUNCTION__);
   return obj;
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -168,17 +168,18 @@ public:
   virtual bool SendRtcp(const uint8_t *data,
                         size_t len) override;
 
   virtual uint64_t CodecPluginID() override { return 0; }
   virtual void SetPCHandle(const std::string& aPCHandle) override {}
 
   virtual void DeleteStreams() override {}
 
-  explicit WebrtcAudioConduit():
+  explicit WebrtcAudioConduit(MediaConduitStatisticsId aStatsId):
+                      AudioSessionConduit(aStatsId),
                       mVoiceEngine(nullptr),
                       mFakeAudioDevice(new webrtc::FakeAudioDeviceModule()),
                       mTransportMonitor("WebrtcAudioConduit"),
                       mTransmitterTransport(nullptr),
                       mReceiverTransport(nullptr),
                       mEngineTransmitting(false),
                       mEngineReceiving(false),
                       mChannel(-1),
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -10,17 +10,17 @@
 #include "nsDOMNavigationTiming.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/RefCounted.h"
 #include "mozilla/UniquePtr.h"
 #include "RtpSourceObserver.h"
 #include "CodecConfig.h"
 #include "VideoTypes.h"
 #include "MediaConduitErrors.h"
-
+#include "MediaConduitStatisticsId.h"
 #include "ImageContainer.h"
 
 #include "webrtc/call.h"
 #include "webrtc/config.h"
 #include "webrtc/common_types.h"
 #include "webrtc/common_types.h"
 #include "webrtc/api/video/video_frame_buffer.h"
 #include "webrtc/logging/rtc_event_log/rtc_event_log.h"
@@ -184,16 +184,28 @@ public:
 class MediaSessionConduit
 {
 protected:
   virtual ~MediaSessionConduit() {}
 
 public:
   enum Type { AUDIO, VIDEO } ;
 
+  class StatisticsIdGenerator {
+  public:
+    virtual ~StatisticsIdGenerator() {}
+    MediaConduitStatisticsId Generate() { return mNext++; }
+  private:
+    Atomic<MediaConduitStatisticsId, ReleaseAcquire> mNext{0};
+  };
+
+
+  MediaSessionConduit(MediaConduitStatisticsId aStatsId):
+                          mStatisticsId(aStatsId) {};
+
   static std::string
   LocalDirectionToString(const MediaSessionConduitLocalDirection aDirection) {
     return aDirection == MediaSessionConduitLocalDirection::kSend ?
                             "send" : "receive";
   }
 
   virtual Type type() const = 0;
 
@@ -271,16 +283,19 @@ public:
   virtual bool SetLocalCNAME(const char* cname) = 0;
 
   virtual bool SetLocalMID(const std::string& mid) = 0;
 
   /**
    * Functions returning stats needed by w3c stats model.
    */
 
+  // The unique identifier associated with statistics provided by this conduit
+  virtual MediaConduitStatisticsId GetStatisticsId() { return mStatisticsId; };
+
   virtual bool
   GetSendPacketTypeStats(webrtc::RtcpPacketTypeCounter* aPacketCounts) = 0;
 
   virtual bool
   GetRecvPacketTypeStats(webrtc::RtcpPacketTypeCounter* aPacketCounts) = 0;
 
   virtual bool GetVideoEncoderStats(double* framerateMean,
                                     double* framerateStdDev,
@@ -312,16 +327,18 @@ public:
   virtual uint64_t CodecPluginID() = 0;
 
   virtual void SetPCHandle(const std::string& aPCHandle) = 0;
 
   virtual void DeleteStreams() = 0;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSessionConduit)
 
+  private:
+    const MediaConduitStatisticsId mStatisticsId;
 };
 
 // Abstract base classes for external encoder/decoder.
 class CodecPluginID
 {
 public:
   virtual ~CodecPluginID() {}
 
@@ -347,30 +364,37 @@ public:
  */
 class VideoSessionConduit : public MediaSessionConduit
 {
 public:
   /**
    * Factory function to create and initialize a Video Conduit Session
    * @param  webrtc::Call instance shared by paired audio and video
    *         media conduits
+   * @param  aStatisticsId an identifier that is unique within the
+   *         PeerConnection that is used to identify the stats created
+   *         by inspecting this conduit
    * @result Concrete VideoSessionConduitObject or nullptr in the case
    *         of failure
    */
-  static RefPtr<VideoSessionConduit> Create(RefPtr<WebRtcCallWrapper> aCall);
+  static RefPtr<VideoSessionConduit>
+  Create(RefPtr<WebRtcCallWrapper> aCall,
+         MediaConduitStatisticsId aStatsId);
 
   enum FrameRequestType
   {
     FrameRequestNone,
     FrameRequestFir,
     FrameRequestPli,
     FrameRequestUnknown
   };
 
-  VideoSessionConduit() : mFrameRequestMethod(FrameRequestNone),
+  VideoSessionConduit(MediaConduitStatisticsId aStatsId):
+                          MediaSessionConduit(aStatsId),
+                          mFrameRequestMethod(FrameRequestNone),
                           mUsingNackBasic(false),
                           mUsingTmmbr(false),
                           mUsingFEC(false) {}
 
   virtual ~VideoSessionConduit() {}
 
   Type type() const override { return VIDEO; }
 
@@ -463,20 +487,27 @@ public:
 class AudioSessionConduit : public MediaSessionConduit
 {
 public:
 
  /**
    * Factory function to create and initialize an Audio Conduit Session
    * @param  webrtc::Call instance shared by paired audio and video
    *         media conduits
+   * @param  aStatisticsId an identifier that is unique within the
+   *         PeerConnection that is used to identify the stats created
+   *         by inspecting this conduit
    * @result Concrete AudioSessionConduitObject or nullptr in the case
    *         of failure
    */
-  static RefPtr<AudioSessionConduit> Create();
+  static RefPtr<AudioSessionConduit>
+  Create(MediaConduitStatisticsId aStats);
+
+  AudioSessionConduit(MediaConduitStatisticsId aStats):
+                          MediaSessionConduit(aStats) {};
 
   virtual ~AudioSessionConduit() {}
 
   Type type() const override { return AUDIO; }
 
   MediaConduitErrorCode
   SetLocalRTPExtensions(MediaSessionConduitLocalDirection aDirection,
                         const RtpExtList& extensions) override = 0;
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitStatisticsId.h
@@ -0,0 +1,10 @@
+/* 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/. */
+
+#ifndef MEDIA_CONDUIT_STATISTICS_ID_H_
+#define MEDIA_CONDUIT_STATISTICS_ID_H_
+namespace mozilla {
+  typedef uint64_t MediaConduitStatisticsId;
+}
+#endif
\ No newline at end of file
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -207,40 +207,44 @@ WebrtcVideoConduit::ReceiveStreamStatist
   mFramesDecoded = aStats.frame_counts.key_frames
                    + aStats.frame_counts.delta_frames;
 }
 
 /**
  * Factory Method for VideoConduit
  */
 RefPtr<VideoSessionConduit>
-VideoSessionConduit::Create(RefPtr<WebRtcCallWrapper> aCall)
+VideoSessionConduit::Create(RefPtr<WebRtcCallWrapper> aCall,
+                            MediaConduitStatisticsId aStatsId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(aCall, "missing required parameter: aCall");
   CSFLogVerbose(LOGTAG, "%s", __FUNCTION__);
 
   if (!aCall) {
     return nullptr;
   }
 
   UniquePtr<cricket::VideoAdapter> videoAdapter(new cricket::VideoAdapter(1));
   nsAutoPtr<WebrtcVideoConduit> obj(new WebrtcVideoConduit(aCall,
-                                    std::move(videoAdapter)));
+                                    std::move(videoAdapter),
+                                    aStatsId));
   if(obj->Init() != kMediaConduitNoError) {
     CSFLogError(LOGTAG, "%s VideoConduit Init Failed ", __FUNCTION__);
     return nullptr;
   }
   CSFLogVerbose(LOGTAG, "%s Successfully created VideoConduit ", __FUNCTION__);
   return obj.forget();
 }
 
 WebrtcVideoConduit::WebrtcVideoConduit(RefPtr<WebRtcCallWrapper> aCall,
-                                       UniquePtr<cricket::VideoAdapter>&& aVideoAdapter)
-  : mTransportMonitor("WebrtcVideoConduit")
+                                       UniquePtr<cricket::VideoAdapter>&& aVideoAdapter,
+                                       MediaConduitStatisticsId  aStatsId):
+  VideoSessionConduit(aStatsId)
+  , mTransportMonitor("WebrtcVideoConduit")
   , mRenderer(nullptr)
   , mVideoAdapter(std::move(aVideoAdapter))
   , mVideoBroadcaster()
   , mEngineTransmitting(false)
   , mEngineReceiving(false)
   , mCapId(-1)
   , mCodecMutex("VideoConduit codec db")
   , mRecvStream(nullptr)
@@ -273,17 +277,16 @@ WebrtcVideoConduit::WebrtcVideoConduit(R
   , mWaitingForInitialSsrc(true)
   , mRecvSSRC(0)
   , mRecvSSRCSetInProgress(false)
   , mSendCodecPlugin(nullptr)
   , mRecvCodecPlugin(nullptr)
   , mVideoStatsTimer(NS_NewTimer())
 {
   mRecvStreamConfig.renderer = this;
-
   // Video Stats Callback
   nsTimerCallbackFunc callback = [](nsITimer* aTimer, void* aClosure) {
     CSFLogDebug(LOGTAG, "StreamStats polling scheduled for VideoConduit: %p", aClosure);
     auto self = static_cast<WebrtcVideoConduit*>(aClosure);
     MutexAutoLock lock(self->mCodecMutex);
     if (self->mEngineTransmitting && self->mSendStream) {
       const auto& stats = self->mSendStream->GetStats();
       self->mSendStreamStats.Update(stats);
@@ -1201,17 +1204,16 @@ WebrtcVideoConduit::Init()
   CSFLogDebug(LOGTAG, "%s this=%p", __FUNCTION__, this);
   MediaConduitErrorCode result;
   // Run code that must run on MainThread first
   MOZ_ASSERT(NS_IsMainThread());
   result = InitMain();
   if (result != kMediaConduitNoError) {
     return result;
   }
-
   CSFLogError(LOGTAG, "%s Initialization Done", __FUNCTION__);
   return kMediaConduitNoError;
 }
 
 void
 WebrtcVideoConduit::DeleteStreams()
 {
   // We can't delete the VideoEngine until all these are released!
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -260,17 +260,19 @@ public:
     return mTemporalLayers;
   }
 
   webrtc::VideoCodecMode CodecMode() const {
     return mCodecMode;
   }
 
   WebrtcVideoConduit(RefPtr<WebRtcCallWrapper> aCall,
-                     UniquePtr<cricket::VideoAdapter>&& aVideoAdapter);
+                     UniquePtr<cricket::VideoAdapter>&& aVideoAdapter,
+                     MediaConduitStatisticsId aStatsId);
+
   virtual ~WebrtcVideoConduit();
 
   MediaConduitErrorCode InitMain();
   virtual MediaConduitErrorCode Init();
 
   std::vector<unsigned int> GetLocalSSRCs() const override;
   bool SetLocalSSRCs(const std::vector<unsigned int> & ssrcs) override;
   bool GetRemoteSSRC(unsigned int* ssrc) override;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -3427,17 +3427,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
       continue;
     }
     const MediaPipeline& mp = *query->pipelines[p];
     bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
     nsString mediaType = isAudio ?
         NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video");
     nsString idstr = mediaType;
     idstr.AppendLiteral("_");
-    idstr.AppendInt((uint32_t)p);
+    idstr.AppendInt(mp.Conduit()->GetStatisticsId());
 
     // TODO(@@NG):ssrcs handle Conduits having multiple stats at the same level
     // This is pending spec work
     // Gather pipeline stats.
     switch (mp.Direction()) {
       case MediaPipeline::DirectionType::TRANSMIT: {
         nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
         nsString remoteId;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -1153,17 +1153,18 @@ PeerConnectionMedia::AddTransceiver(
 
   RefPtr<TransceiverImpl> transceiver = new TransceiverImpl(
       mParent->GetHandle(),
       aJsepTransceiver,
       mMainThread.get(),
       mSTSThread.get(),
       &aReceiveTrack,
       aSendTrack,
-      mCall.get());
+      mCall.get(),
+      mStatsIdGenerator.Generate());
 
   if (!transceiver->IsValid()) {
     return NS_ERROR_FAILURE;
   }
 
   if (aSendTrack) {
     // implement checking for peerIdentity (where failure == black/silence)
     nsIDocument* doc = mParent->GetWindow()->GetExtantDoc();
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -363,14 +363,17 @@ class PeerConnectionMedia : public sigsl
   RefPtr<net::StunAddrsRequestChild> mStunAddrsRequest;
 
   // Used to track the state of the stun addr IPC request
   bool mLocalAddrsCompleted;
 
   // Used to store the result of the stun addr IPC request
   nsTArray<NrIceStunAddr> mStunAddrs;
 
+  // Used to create unique stats ids for conduits
+  MediaSessionConduit::StatisticsIdGenerator mStatsIdGenerator;
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PeerConnectionMedia)
 };
 
 } // namespace mozilla
 
 #endif
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -36,31 +36,32 @@ using LocalDirection = MediaSessionCondu
 
 TransceiverImpl::TransceiverImpl(
     const std::string& aPCHandle,
     JsepTransceiver* aJsepTransceiver,
     nsIEventTarget* aMainThread,
     nsIEventTarget* aStsThread,
     dom::MediaStreamTrack* aReceiveTrack,
     dom::MediaStreamTrack* aSendTrack,
-    WebRtcCallWrapper* aCallWrapper) :
+    WebRtcCallWrapper* aCallWrapper,
+    MediaConduitStatisticsId aStatsId) :
   mPCHandle(aPCHandle),
   mJsepTransceiver(aJsepTransceiver),
   mHaveStartedReceiving(false),
   mHaveSetupTransport(false),
   mMainThread(aMainThread),
   mStsThread(aStsThread),
   mReceiveTrack(aReceiveTrack),
   mSendTrack(aSendTrack),
   mCallWrapper(aCallWrapper)
 {
   if (IsVideo()) {
-    InitVideo();
+    InitVideo(aStatsId);
   } else {
-    InitAudio();
+    InitAudio(aStatsId);
   }
 
   if (!IsValid()) {
     return;
   }
 
   mConduit->SetPCHandle(mPCHandle);
 
@@ -74,19 +75,19 @@ TransceiverImpl::TransceiverImpl(
   mTransmitPipeline->SetTrack(mSendTrack);
 }
 
 TransceiverImpl::~TransceiverImpl() = default;
 
 NS_IMPL_ISUPPORTS0(TransceiverImpl)
 
 void
-TransceiverImpl::InitAudio()
+TransceiverImpl::InitAudio(MediaConduitStatisticsId aStatsId)
 {
-  mConduit = AudioSessionConduit::Create();
+  mConduit = AudioSessionConduit::Create(aStatsId);
 
   if (!mConduit) {
     MOZ_MTLOG(ML_ERROR, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
                         ": Failed to create AudioSessionConduit");
     // TODO(bug 1422897): We need a way to record this when it happens in the
     // wild.
     return;
   }
@@ -95,19 +96,19 @@ TransceiverImpl::InitAudio()
       mPCHandle,
       mMainThread.get(),
       mStsThread.get(),
       static_cast<AudioSessionConduit*>(mConduit.get()),
       mReceiveTrack);
 }
 
 void
-TransceiverImpl::InitVideo()
+TransceiverImpl::InitVideo(MediaConduitStatisticsId aStatsId)
 {
-  mConduit = VideoSessionConduit::Create(mCallWrapper);
+  mConduit = VideoSessionConduit::Create(mCallWrapper, aStatsId);
 
   if (!mConduit) {
     MOZ_MTLOG(ML_ERROR, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
                         ": Failed to create VideoSessionConduit");
     // TODO(bug 1422897): We need a way to record this when it happens in the
     // wild.
     return;
   }
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.h
@@ -7,16 +7,17 @@
 #include <string>
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 #include "nsTArray.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/dom/MediaStreamTrack.h"
 #include "ErrorList.h"
+#include "signaling/src/media-conduit/MediaConduitStatisticsId.h"
 #include "mtransport/transportflow.h"
 #include "signaling/src/jsep/JsepTransceiver.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 class PeerIdentity;
 class PeerConnectionMedia;
@@ -42,28 +43,30 @@ struct RTCRtpSourceEntry;
  * together. This includes:
  * MediaStreamTrack for rendering and capture
  * TransportFlow for RTP transmission/reception
  * Audio/VideoConduit for feeding RTP/RTCP into webrtc.org for decoding, and
  * feeding audio/video frames into webrtc.org for encoding into RTP/RTCP.
 */
 class TransceiverImpl : public nsISupports {
 public:
+  typedef uint64_t StatisticsId;
   /**
    * |aReceiveStream| is always set; this holds even if the remote end has not
    * negotiated one for this transceiver. |aSendTrack| might or might not be
    * set.
    */
   TransceiverImpl(const std::string& aPCHandle,
                   JsepTransceiver* aJsepTransceiver,
                   nsIEventTarget* aMainThread,
                   nsIEventTarget* aStsThread,
                   dom::MediaStreamTrack* aReceiveTrack,
                   dom::MediaStreamTrack* aSendTrack,
-                  WebRtcCallWrapper* aCallWrapper);
+                  WebRtcCallWrapper* aCallWrapper,
+                  MediaConduitStatisticsId aStatsId);
 
   bool IsValid() const
   {
     return !!mConduit;
   }
 
   nsresult UpdateSendTrack(dom::MediaStreamTrack* aSendTrack);
 
@@ -124,18 +127,18 @@ public:
                                              int64_t aTimestamp,
                                              bool aHasLevel,
                                              uint8_t aLevel);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 private:
   virtual ~TransceiverImpl();
-  void InitAudio();
-  void InitVideo();
+  void InitAudio(MediaConduitStatisticsId aStatsId);
+  void InitVideo(MediaConduitStatisticsId aStatsId);
   nsresult UpdateAudioConduit();
   nsresult UpdateVideoConduit();
   nsresult ConfigureVideoCodecMode(VideoSessionConduit& aConduit);
   void UpdateConduitRtpExtmap(const JsepTrackNegotiatedDetails& aDetails,
                               const MediaSessionConduitLocalDirection aDir);
   void Stop();
 
   const std::string mPCHandle;