Bug 1089798 - Implemenation of MediaStream IDs. r=bwc
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Thu, 19 Feb 2015 12:59:00 -0500
changeset 229990 d7755b48d81749ca675b2fa4ef65caa406dce424
parent 229989 f97ab75f35afeaeca1b59738ede8bcfbba83e747
child 229991 d3febf8ec84d35f42e72ce2d6acdcbc81396f872
push id55863
push userryanvm@gmail.com
push dateFri, 20 Feb 2015 15:14:27 +0000
treeherdermozilla-inbound@a30cdc2cc8d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc
bugs1089798
milestone38.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 1089798 - Implemenation of MediaStream IDs. r=bwc
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.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/src/peerconnection/PeerConnectionMedia.h
media/webrtc/signaling/test/FakeMediaStreams.h
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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 "DOMMediaStream.h"
 #include "nsContentUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIUUIDGenerator.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
 #include "mozilla/dom/AudioNode.h"
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "MediaStreamGraph.h"
@@ -141,16 +143,30 @@ NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMe
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
 DOMMediaStream::DOMMediaStream()
   : mLogicalStreamStartTime(0),
     mStream(nullptr), mHintContents(0), mTrackTypesAvailable(0),
     mNotifiedOfMediaStreamGraphShutdown(false), mCORSMode(CORS_NONE)
 {
+  nsresult rv;
+  nsCOMPtr<nsIUUIDGenerator> uuidgen =
+    do_GetService("@mozilla.org/uuid-generator;1", &rv);
+
+  if (NS_SUCCEEDED(rv) && uuidgen) {
+    nsID uuid;
+    memset(&uuid, 0, sizeof(uuid));
+    rv = uuidgen->GenerateUUIDInPlace(&uuid);
+    if (NS_SUCCEEDED(rv)) {
+      char buffer[NSID_LENGTH];
+      uuid.ToProvidedString(buffer);
+      mID = NS_ConvertASCIItoUTF16(buffer);
+    }
+  }
 }
 
 DOMMediaStream::~DOMMediaStream()
 {
   Destroy();
 }
 
 void
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -175,16 +175,20 @@ public:
    * will only be called during a forced shutdown due to application exit.
    */
   void NotifyMediaStreamGraphShutdown();
   /**
    * Called when the main-thread state of the MediaStream changed.
    */
   void NotifyStreamStateChanged();
 
+  // Webrtc allows the remote side to name a stream whatever it wants, and we
+  // need to surface this to content.
+  void AssignId(const nsAString& aID) { mID = aID; }
+
   // Indicate what track types we eventually expect to add to this stream
   enum {
     HINT_CONTENTS_AUDIO = 1 << 0,
     HINT_CONTENTS_VIDEO = 1 << 1,
     HINT_CONTENTS_UNKNOWN = 1 << 2
   };
   TrackTypeHints GetHintContents() const { return mHintContents; }
   void SetHintContents(TrackTypeHints aHintContents);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -195,73 +195,69 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     PeerConnectionWrapper wrapper(mPcHandle);
 
     if (!wrapper.impl() || wrapper.impl()->IsClosed()) {
       return;
     }
 
-    CSFLogInfo(logTag, "Returning success for OnAddStream()");
-    // We are running on main thread here so we shouldn't have a race
-    // on this callback
-
     nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
     aStream->GetTracks(tracks);
 
-    bool newStream = true;
+    std::string streamId = PeerConnectionImpl::GetStreamId(*aStream);
+    bool notifyStream = true;
 
     for (size_t i = 0; i < tracks.Length(); i++) {
       std::string trackId;
       // This is the first chance we get to set the string track id on this
       // track. It would be nice if we could specify this along with the numeric
       // track id from the start, but we're stuck doing this fixup after the
       // fact.
-      nsresult rv = wrapper.impl()->GetRemoteTrackId(aStream,
+      nsresult rv = wrapper.impl()->GetRemoteTrackId(streamId,
                                                      tracks[i]->GetTrackID(),
                                                      &trackId);
 
       if (NS_FAILED(rv)) {
         CSFLogError(logTag, "%s: Failed to get string track id for %u, rv = %u",
                             __FUNCTION__,
                             static_cast<unsigned>(tracks[i]->GetTrackID()),
                             static_cast<unsigned>(rv));
         MOZ_ASSERT(false);
         continue;
       }
 
       std::string origTrackId = PeerConnectionImpl::GetTrackId(*tracks[i]);
 
       if (origTrackId == trackId) {
         // Pre-existing track
-        newStream = false;
+        notifyStream = false;
         continue;
       }
 
       tracks[i]->AssignId(NS_ConvertUTF8toUTF16(trackId.c_str()));
 
       JSErrorResult jrv;
+      CSFLogInfo(logTag, "Calling OnAddTrack(%s)", trackId.c_str());
       mObserver->OnAddTrack(*tracks[i], jrv);
-      CSFLogInfo(logTag, "Calling OnAddTrack(%s)",
-                         PeerConnectionImpl::GetTrackId(*tracks[i]).c_str());
       if (jrv.Failed()) {
         CSFLogError(logTag, ": OnAddTrack(%u) failed! Error: %u",
                     static_cast<unsigned>(i),
                     static_cast<unsigned>(jrv.ErrorCode()));
       }
     }
 
-    if (newStream) {
+    if (notifyStream) {
       // Start currentTime from the point where this stream was successfully
       // returned.
       aStream->SetLogicalStreamStartTime(
           aStream->GetStream()->GetCurrentTime());
 
       JSErrorResult rv;
-      CSFLogInfo(logTag, "Calling OnAddStream");
+      CSFLogInfo(logTag, "Calling OnAddStream(%s)", streamId.c_str());
       mObserver->OnAddStream(*aStream, rv);
       if (rv.Failed()) {
         CSFLogError(logTag, ": OnAddStream() failed! Error: %u",
                     static_cast<unsigned>(rv.ErrorCode()));
       }
     }
   }
 
@@ -1658,16 +1654,22 @@ PeerConnectionImpl::SetRemoteDescription
         if (NS_FAILED(nrv)) {
           pco->OnSetRemoteDescriptionError(
               kInternalError,
               ObString("AddRemoteStream failed"),
               jrv);
           return NS_OK;
         }
         CSFLogDebug(logTag, "Added remote stream %s", info->GetId().c_str());
+
+#ifdef MOZILLA_INTERNAL_API
+        info->GetMediaStream()->AssignId(NS_ConvertUTF8toUTF16(streamId.c_str()));
+#else
+        info->GetMediaStream()->AssignId((streamId));
+#endif
       }
 
       size_t numNewAudioTracks = 0;
       size_t numNewVideoTracks = 0;
       size_t numPreexistingTrackIds = 0;
 
       for (auto j = tracks.begin(); j != tracks.end(); ++j) {
         RefPtr<JsepTrack> track = *j;
@@ -1924,40 +1926,52 @@ PeerConnectionImpl::PrincipalChanged(DOM
   } else {
     CSFLogInfo(logTag, "Can't update sink principal; document gone");
   }
 }
 #endif
 
 #ifdef MOZILLA_INTERNAL_API
 nsresult
-PeerConnectionImpl::GetRemoteTrackId(DOMMediaStream* mediaStream,
+PeerConnectionImpl::GetRemoteTrackId(const std::string streamId,
                                      TrackID numericTrackId,
                                      std::string* trackId) const
 {
   if (IsClosed()) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  return mMedia->GetRemoteTrackId(mediaStream, numericTrackId, trackId);
+  return mMedia->GetRemoteTrackId(streamId, numericTrackId, trackId);
 }
 #endif
 
 std::string
 PeerConnectionImpl::GetTrackId(const MediaStreamTrack& aTrack)
 {
 #ifdef MOZILLA_INTERNAL_API
   nsString wideTrackId;
   aTrack.GetId(wideTrackId);
   return NS_ConvertUTF16toUTF8(wideTrackId).get();
 #else
   return aTrack.GetId();
 #endif
 }
 
+std::string
+PeerConnectionImpl::GetStreamId(const DOMMediaStream& aStream)
+{
+#ifdef MOZILLA_INTERNAL_API
+  nsString wideStreamId;
+  aStream.GetId(wideStreamId);
+  return NS_ConvertUTF16toUTF8(wideStreamId).get();
+#else
+  return aStream.GetId();
+#endif
+}
+
 nsresult
 PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
                              const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (!aStreams.Length()) {
     CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
@@ -1971,20 +1985,19 @@ PeerConnectionImpl::AddTrack(MediaStream
                              DOMMediaStream& aMediaStream)
 {
   if (!aMediaStream.HasTrack(aTrack)) {
     CSFLogError(logTag, "%s: Track is not in stream", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
   uint32_t num = mMedia->LocalStreamsLength();
 
-  std::string streamId;
+  std::string streamId = PeerConnectionImpl::GetStreamId(aMediaStream);
   std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
-  // TODO(bug 1089798): streamId should really come from the MS.
-  nsresult res = mMedia->AddTrack(&aMediaStream, &streamId, trackId);
+  nsresult res = mMedia->AddTrack(&aMediaStream, streamId, trackId);
   if (NS_FAILED(res)) {
     return res;
   }
 
   CSFLogDebug(logTag, "Added track (%s) to stream %s",
                       trackId.c_str(), streamId.c_str());
 
   if (num != mMedia->LocalStreamsLength()) {
@@ -2038,18 +2051,18 @@ PeerConnectionImpl::RemoveTrack(MediaStr
 
   DOMMediaStream* stream = aTrack.GetStream();
 
   if (!stream) {
     CSFLogError(logTag, "%s: Track has no stream", __FUNCTION__);
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<LocalSourceStreamInfo> info =
-    media()->GetLocalStreamByDomStream(*stream);
+  std::string streamId = PeerConnectionImpl::GetStreamId(*stream);
+  nsRefPtr<LocalSourceStreamInfo> info = media()->GetLocalStreamById(streamId);
 
   if (!info) {
     CSFLogError(logTag, "%s: Unknown stream", __FUNCTION__);
     return NS_ERROR_INVALID_ARG;
   }
 
   std::string trackId(PeerConnectionImpl::GetTrackId(aTrack));
 
@@ -2109,18 +2122,18 @@ PeerConnectionImpl::ReplaceTrack(MediaSt
   // have.  Solution is to have JsepSession read track ids and use those.
 
   // Because DirectListeners see the SourceMediaStream's TrackID's, and not the
   // TrackUnionStream's TrackID's, this value won't currently match what is used in
   // MediaPipelineTransmit.  Bug 1056652
   //  TrackID thisID = aThisTrack.GetTrackID();
   //
 
-  nsRefPtr<LocalSourceStreamInfo> info =
-    media()->GetLocalStreamByDomStream(aStream);
+  std::string streamId = PeerConnectionImpl::GetStreamId(aStream);
+  nsRefPtr<LocalSourceStreamInfo> info = media()->GetLocalStreamById(streamId);
 
   if (!info || !info->HasTrack(origTrackId)) {
     CSFLogError(logTag, "Track to replace (%s) was never added",
                         origTrackId.c_str());
     pco->OnReplaceTrackError(kInvalidMediastreamTrack,
                              ObString("Track to replace was never added"),
                              jrv);
     return NS_OK;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -593,21 +593,22 @@ public:
       RTCStatsQuery *query);
 
   static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query);
 
   // for monitoring changes in stream ownership
   // PeerConnectionMedia can't do it because it doesn't know about principals
   virtual void PrincipalChanged(DOMMediaStream* aMediaStream) MOZ_OVERRIDE;
 
-  nsresult GetRemoteTrackId(DOMMediaStream* mediaStream,
+  nsresult GetRemoteTrackId(const std::string streamId,
                             TrackID numericTrackId,
                             std::string* trackId) const;
 #endif
 
+  static std::string GetStreamId(const DOMMediaStream& aStream);
   static std::string GetTrackId(const dom::MediaStreamTrack& track);
 
 private:
   virtual ~PeerConnectionImpl();
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
   NS_IMETHODIMP Initialize(PeerConnectionObserver& aObserver,
                            nsGlobalWindow* aWindow,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -576,109 +576,92 @@ PeerConnectionMedia::UpdateIceMediaStrea
       CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
                           static_cast<unsigned>(rv));
     }
   }
 }
 
 nsresult
 PeerConnectionMedia::AddTrack(DOMMediaStream* aMediaStream,
-                              std::string* streamId,
+                              const std::string& streamId,
                               const std::string& trackId)
 {
   ASSERT_ON_THREAD(mMainThread);
 
   if (!aMediaStream) {
     CSFLogError(logTag, "%s - aMediaStream is NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream);
 
   nsRefPtr<LocalSourceStreamInfo> localSourceStream =
-    GetLocalStreamByDomStream(*aMediaStream);
+    GetLocalStreamById(streamId);
 
   if (!localSourceStream) {
-    std::string id;
-    if (!mUuidGen->Generate(&id)) {
-      CSFLogError(logTag, "Failed to generate UUID for stream");
-      return NS_ERROR_FAILURE;
-    }
-
-    localSourceStream = new LocalSourceStreamInfo(aMediaStream, this, id);
+    localSourceStream = new LocalSourceStreamInfo(aMediaStream, this, streamId);
     mLocalSourceStreams.AppendElement(localSourceStream);
   }
 
   localSourceStream->AddTrack(trackId);
-  *streamId = localSourceStream->GetId();
   return NS_OK;
 }
 
 nsresult
 PeerConnectionMedia::RemoveLocalTrack(const std::string& streamId,
                                       const std::string& trackId)
 {
   ASSERT_ON_THREAD(mMainThread);
 
   CSFLogDebug(logTag, "%s: stream: %s track: %s", __FUNCTION__,
                       streamId.c_str(), trackId.c_str());
 
-  size_t i;
-  for (i = 0; i < mLocalSourceStreams.Length(); ++i) {
-    if (mLocalSourceStreams[i]->GetId() == streamId) {
-      break;
-    }
-  }
-
-  if (i == mLocalSourceStreams.Length()) {
+  nsRefPtr<LocalSourceStreamInfo> localSourceStream =
+    GetLocalStreamById(streamId);
+  if (!localSourceStream) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  mLocalSourceStreams[i]->RemoveTrack(trackId);
-  if (!(mLocalSourceStreams[i]->GetTrackCount())) {
-    mLocalSourceStreams.RemoveElementAt(i);
+  localSourceStream->RemoveTrack(trackId);
+  if (!localSourceStream->GetTrackCount()) {
+    mLocalSourceStreams.RemoveElement(localSourceStream);
   }
   return NS_OK;
 }
 
 nsresult
 PeerConnectionMedia::RemoveRemoteTrack(const std::string& streamId,
                                        const std::string& trackId)
 {
   ASSERT_ON_THREAD(mMainThread);
 
   CSFLogDebug(logTag, "%s: stream: %s track: %s", __FUNCTION__,
                       streamId.c_str(), trackId.c_str());
 
-  size_t i;
-  for (i = 0; i < mRemoteSourceStreams.Length(); ++i) {
-    if (mRemoteSourceStreams[i]->GetId() == streamId) {
-      break;
-    }
-  }
-
-  if (i == mRemoteSourceStreams.Length()) {
+  nsRefPtr<RemoteSourceStreamInfo> remoteSourceStream =
+    GetRemoteStreamById(streamId);
+  if (!remoteSourceStream) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  mRemoteSourceStreams[i]->RemoveTrack(trackId);
-  if (!(mRemoteSourceStreams[i]->GetTrackCount())) {
-    mRemoteSourceStreams.RemoveElementAt(i);
+  remoteSourceStream->RemoveTrack(trackId);
+  if (!remoteSourceStream->GetTrackCount()) {
+    mRemoteSourceStreams.RemoveElement(remoteSourceStream);
   }
   return NS_OK;
 }
 
 nsresult
-PeerConnectionMedia::GetRemoteTrackId(DOMMediaStream* mediaStream,
+PeerConnectionMedia::GetRemoteTrackId(const std::string streamId,
                                       TrackID numericTrackId,
                                       std::string* trackId) const
 {
   auto* ncThis = const_cast<PeerConnectionMedia*>(this);
   const RemoteSourceStreamInfo* info =
-    ncThis->GetRemoteStreamByDomStream(*mediaStream);
+    ncThis->GetRemoteStreamById(streamId);
 
   if (!info) {
     CSFLogError(logTag, "%s: Could not find stream info", __FUNCTION__);
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return info->GetTrackId(numericTrackId, trackId);
 }
@@ -771,45 +754,16 @@ PeerConnectionMedia::GetLocalStreamById(
 {
   ASSERT_ON_THREAD(mMainThread);
   for (size_t i = 0; i < mLocalSourceStreams.Length(); ++i) {
     if (id == mLocalSourceStreams[i]->GetId()) {
       return mLocalSourceStreams[i];
     }
   }
 
-  MOZ_ASSERT(false);
-  return nullptr;
-}
-
-LocalSourceStreamInfo*
-PeerConnectionMedia::GetLocalStreamByDomStream(const DOMMediaStream& stream)
-{
-  ASSERT_ON_THREAD(mMainThread);
-  for (size_t i = 0; i < mLocalSourceStreams.Length(); ++i) {
-    if (&stream == mLocalSourceStreams[i]->GetMediaStream()) {
-      return mLocalSourceStreams[i];
-    }
-  }
-
-  return nullptr;
-}
-
-RemoteSourceStreamInfo*
-PeerConnectionMedia::GetRemoteStreamByDomStream(
-    const DOMMediaStream& stream)
-{
-  ASSERT_ON_THREAD(mMainThread);
-  for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) {
-    if (&stream == mRemoteSourceStreams[i]->GetMediaStream()) {
-      return mRemoteSourceStreams[i];
-    }
-  }
-
-  MOZ_ASSERT(false);
   return nullptr;
 }
 
 RemoteSourceStreamInfo*
 PeerConnectionMedia::GetRemoteStreamByIndex(size_t aIndex)
 {
   ASSERT_ON_THREAD(mMainThread);
   MOZ_ASSERT(mRemoteSourceStreams.SafeElementAt(aIndex));
@@ -821,20 +775,16 @@ PeerConnectionMedia::GetRemoteStreamById
 {
   ASSERT_ON_THREAD(mMainThread);
   for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) {
     if (id == mRemoteSourceStreams[i]->GetId()) {
       return mRemoteSourceStreams[i];
     }
   }
 
-  // This does not have a MOZ_ASSERT like GetLocalStreamById because in the
-  // case of local streams, the stream id and stream info are created
-  // simultaneously, whereas in the remote case the stream id exists first,
-  // meaning we have to be able to check.
   return nullptr;
 }
 
 nsresult
 PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo)
 {
   ASSERT_ON_THREAD(mMainThread);
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -236,51 +236,45 @@ class PeerConnectionMedia : public sigsl
   // Process a trickle ICE candidate.
   void AddIceCandidate(const std::string& candidate, const std::string& mid,
                        uint32_t aMLine);
 
   // Handle complete media pipelines.
   nsresult UpdateMediaPipelines(const JsepSession& session);
 
   // Add a track (main thread only)
-  // TODO(bug 1089798): Once DOMMediaStream has an id field, use it instead of
-  // letting PCMedia choose a streamId
   nsresult AddTrack(DOMMediaStream* aMediaStream,
-                    std::string* streamId,
+                    const std::string& streamId,
                     const std::string& trackId);
 
   nsresult RemoveLocalTrack(const std::string& streamId,
                             const std::string& trackId);
   nsresult RemoveRemoteTrack(const std::string& streamId,
                             const std::string& trackId);
 
-  nsresult GetRemoteTrackId(DOMMediaStream* mediaStream,
+  nsresult GetRemoteTrackId(const std::string streamId,
                             TrackID numericTrackId,
                             std::string* trackId) const;
 
   // Get a specific local stream
   uint32_t LocalStreamsLength()
   {
     return mLocalSourceStreams.Length();
   }
   LocalSourceStreamInfo* GetLocalStreamByIndex(int index);
   LocalSourceStreamInfo* GetLocalStreamById(const std::string& id);
-  LocalSourceStreamInfo* GetLocalStreamByDomStream(
-      const DOMMediaStream& stream);
 
   // Get a specific remote stream
   uint32_t RemoteStreamsLength()
   {
     return mRemoteSourceStreams.Length();
   }
 
   RemoteSourceStreamInfo* GetRemoteStreamByIndex(size_t index);
   RemoteSourceStreamInfo* GetRemoteStreamById(const std::string& id);
-  RemoteSourceStreamInfo* GetRemoteStreamByDomStream(
-      const DOMMediaStream& stream);
 
   // Add a remote stream.
   nsresult AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo);
 
 #ifdef MOZILLA_INTERNAL_API
   // In cases where the peer isn't yet identified, we disable the pipeline (not
   // the stream, that would potentially affect others), so that it sends
   // black/silence.  Once the peer is identified, re-enable those streams.
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -280,16 +280,18 @@ public:
   }
 
   virtual void Stop() {} // Really DOMLocalMediaStream
 
   virtual bool AddDirectListener(Fake_MediaStreamListener *aListener) { return false; }
   virtual void RemoveDirectListener(Fake_MediaStreamListener *aListener) {}
 
   Fake_MediaStream *GetStream() { return mMediaStream; }
+  std::string GetId() const { return mID; }
+  void AssignId(const std::string& id) { mID = id; }
 
   // Hints to tell the SDP generator about whether this
   // MediaStream probably has audio and/or video
   typedef uint8_t TrackTypeHints;
   enum {
     HINT_CONTENTS_AUDIO = 0x01,
     HINT_CONTENTS_VIDEO = 0x02
   };
@@ -338,16 +340,18 @@ public:
 private:
   nsRefPtr<Fake_MediaStream> mMediaStream;
 
   // tells the SDP generator about whether this
   // MediaStream probably has audio and/or video
   uint32_t mHintContents;
   nsRefPtr<Fake_MediaStreamTrack> mVideoTrack;
   nsRefPtr<Fake_MediaStreamTrack> mAudioTrack;
+
+  std::string mID;
 };
 
 class Fake_MediaStreamBase : public Fake_MediaStream {
  public:
   Fake_MediaStreamBase() : mPeriodic(new Fake_MediaPeriodic(this)) {}
 
   virtual nsresult Start();
   virtual nsresult Stop();