Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r=roc
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 05 Jan 2016 10:16:21 +0800
changeset 348408 f7580ab5826a4c17eef0ec8b632b36d8ba4e04fa
parent 348407 d52afb558df4f59c5fb12415a8290a791d23bd94
child 348409 f65172114712e7d1831ea234340126f9d29134a1
push id14828
push userpehrsons@gmail.com
push dateThu, 07 Apr 2016 12:57:27 +0000
reviewersroc
bugs1208371
milestone48.0a1
Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r=roc This allows DOMMediaStream to assign MediaStreamTrackSources to dynamically created MediaStreamTracks. MozReview-Commit-ID: 3v91zLiqfl7
dom/camera/DOMCameraControl.cpp
dom/html/HTMLMediaElement.cpp
dom/media/CanvasCaptureMediaStream.cpp
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaManager.cpp
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -245,17 +245,17 @@ nsDOMCameraControl::DiscardCachedCameraI
   sCachedCameraControl = nullptr;
 }
 #endif
 
 nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
                                        const CameraConfiguration& aInitialConfig,
                                        Promise* aPromise,
                                        nsPIDOMWindowInner* aWindow)
-  : DOMMediaStream(aWindow)
+  : DOMMediaStream(aWindow, nullptr)
   , mCameraControl(nullptr)
   , mAudioChannelAgent(nullptr)
   , mGetCameraPromise(aPromise)
   , mWindow(aWindow)
   , mPreviewState(CameraControlListener::kPreviewStopped)
   , mRecording(false)
   , mRecordingStoppedDeferred(false)
   , mSetInitialConfig(false)
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1863,16 +1863,29 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
   return NS_OK;
 }
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
                                         MediaStreamGraph* aGraph)
 {
+  class CaptureStreamTrackSourceGetter : public MediaStreamTrackSourceGetter
+  {
+  public:
+    already_AddRefed<dom::MediaStreamTrackSource>
+    GetMediaStreamTrackSource(TrackID aInputTrackID) override
+    {
+      return do_AddRef(new BasicUnstoppableTrackSource());
+    }
+
+  protected:
+    virtual ~CaptureStreamTrackSourceGetter() {}
+  };
+
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
 #ifdef MOZ_EME
   if (ContainsRestrictedContent()) {
     return nullptr;
   }
@@ -1886,17 +1899,18 @@ HTMLMediaElement::CaptureStreamInternal(
   }
 
   if (!mOutputStreams.IsEmpty() &&
       aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
     return nullptr;
   }
 
   OutputMediaStream* out = mOutputStreams.AppendElement();
-  out->mStream = DOMMediaStream::CreateTrackUnionStream(window, aGraph);
+  MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter();
+  out->mStream = DOMMediaStream::CreateTrackUnionStream(window, aGraph, getter);
   RefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
   out->mStream->CombineWithPrincipal(principal);
   out->mStream->SetCORSMode(mCORSMode);
   out->mFinishWhenEnded = aFinishWhenEnded;
 
   mAudioCaptured = true;
   if (mDecoder) {
     mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(),
--- a/dom/media/CanvasCaptureMediaStream.cpp
+++ b/dom/media/CanvasCaptureMediaStream.cpp
@@ -204,17 +204,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Canva
 NS_IMPL_ADDREF_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
 NS_IMPL_RELEASE_INHERITED(CanvasCaptureMediaStream, DOMMediaStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CanvasCaptureMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
 CanvasCaptureMediaStream::CanvasCaptureMediaStream(nsPIDOMWindowInner* aWindow,
                                                    HTMLCanvasElement* aCanvas)
-  : DOMMediaStream(aWindow)
+  : DOMMediaStream(aWindow, nullptr)
   , mCanvas(aCanvas)
   , mOutputStreamDriver(nullptr)
 {
 }
 
 CanvasCaptureMediaStream::~CanvasCaptureMediaStream()
 {
   if (mOutputStreamDriver) {
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -99,16 +99,22 @@ DOMMediaStream::TrackPort::BlockTrackId(
     mInputPort->BlockTrackId(aTrackId);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::TrackPort, Release)
 
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSourceGetter)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSourceGetter)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSourceGetter)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSourceGetter)
 
 /**
  * Listener registered on the Owned stream to detect added and ended owned
  * tracks for keeping the list of MediaStreamTracks in sync with the tracks
  * added and ended directly at the source.
  */
 class DOMMediaStream::OwnedStreamListener : public MediaStreamListener {
 public:
@@ -130,18 +136,25 @@ public:
       mStream->GetOwnedStream(), aTrackId);
     if (!track) {
       // Track had not been created on main thread before, create it now.
       NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(),
                        "A new track was detected on the input stream; creating "
                        "a corresponding MediaStreamTrack. Initial tracks "
                        "should be added manually to immediately and "
                        "synchronously be available to JS.");
-      track = mStream->CreateOwnDOMTrack(aTrackId, aType, nsString(),
-                                         new BasicUnstoppableTrackSource());
+      RefPtr<MediaStreamTrackSource> source;
+      if (mStream->mTrackSourceGetter) {
+        source = mStream->mTrackSourceGetter->GetMediaStreamTrackSource(aTrackId);
+      }
+      if (!source) {
+        NS_ASSERTION(false, "Dynamic track created without an explicit TrackSource");
+        source = new BasicUnstoppableTrackSource();
+      }
+      track = mStream->CreateOwnDOMTrack(aTrackId, aType, nsString(), source);
     }
   }
 
   void DoNotifyTrackEnded(TrackID aTrackId)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!mStream) {
@@ -280,24 +293,26 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaS
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
                                                 DOMEventTargetHelper)
   tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackSourceGetter)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwnedTracks)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackSourceGetter)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMMediaStream)
   NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
@@ -313,22 +328,23 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAu
                                    mStreamNode)
 
 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
-DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow)
+DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
+                               MediaStreamTrackSourceGetter* aTrackSourceGetter)
   : mLogicalStreamStartTime(0), mWindow(aWindow),
     mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
     mOwnedPort(nullptr), mPlaybackPort(nullptr),
-    mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
-    mCORSMode(CORS_NONE)
+    mTrackSourceGetter(aTrackSourceGetter), mTracksCreated(false),
+    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));
@@ -422,17 +438,19 @@ DOMMediaStream::Constructor(const Global
                             ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
   if (!ownerWindow) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  RefPtr<DOMMediaStream> newStream = new DOMMediaStream(ownerWindow);
+  // Streams created from JS cannot have dynamically created tracks.
+  MediaStreamTrackSourceGetter* getter = nullptr;
+  RefPtr<DOMMediaStream> newStream = new DOMMediaStream(ownerWindow, getter);
 
   for (MediaStreamTrack& track : aTracks) {
     if (!newStream->GetPlaybackStream()) {
       MOZ_RELEASE_ASSERT(track.GetStream());
       MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream());
       MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()->Graph());
       MediaStreamGraph* graph = track.GetStream()->GetPlaybackStream()->Graph();
       newStream->InitPlaybackStreamCommon(graph);
@@ -670,37 +688,40 @@ DOMMediaStream::InitPlaybackStreamCommon
   mPlaybackStream->AddListener(mPlaybackListener);
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, mOwnedStream=%p, mPlaybackStream=%p",
                         this, mInputStream, mOwnedStream, mPlaybackStream));
 }
 
 already_AddRefed<DOMMediaStream>
 DOMMediaStream::CreateSourceStream(nsPIDOMWindowInner* aWindow,
-                                   MediaStreamGraph* aGraph)
+                                   MediaStreamGraph* aGraph,
+                                   MediaStreamTrackSourceGetter* aTrackSourceGetter)
 {
-  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow);
+  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow, aTrackSourceGetter);
   stream->InitSourceStream(aGraph);
   return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
 DOMMediaStream::CreateTrackUnionStream(nsPIDOMWindowInner* aWindow,
-                                       MediaStreamGraph* aGraph)
+                                       MediaStreamGraph* aGraph,
+                                       MediaStreamTrackSourceGetter* aTrackSourceGetter)
 {
-  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow);
+  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow, aTrackSourceGetter);
   stream->InitTrackUnionStream(aGraph);
   return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
 DOMMediaStream::CreateAudioCaptureStream(nsPIDOMWindowInner* aWindow,
                                          MediaStreamGraph* aGraph)
 {
-  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow);
+  MediaStreamTrackSourceGetter* getter = nullptr;
+  RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow, getter);
   stream->InitAudioCaptureStream(aGraph);
   return stream.forget();
 }
 
 void
 DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
 {
   // XXX Bug 1208371 - This enables/disables the track across clones.
@@ -1003,43 +1024,49 @@ DOMLocalMediaStream::StopImpl()
 {
   if (mInputStream && mInputStream->AsSourceStream()) {
     mInputStream->AsSourceStream()->EndAllTrackAndFinish();
   }
 }
 
 already_AddRefed<DOMLocalMediaStream>
 DOMLocalMediaStream::CreateSourceStream(nsPIDOMWindowInner* aWindow,
-                                        MediaStreamGraph* aGraph)
+                                        MediaStreamGraph* aGraph,
+                                        MediaStreamTrackSourceGetter* aTrackSourceGetter)
 {
-  RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(aWindow);
+  RefPtr<DOMLocalMediaStream> stream =
+    new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
   stream->InitSourceStream(aGraph);
   return stream.forget();
 }
 
 already_AddRefed<DOMLocalMediaStream>
 DOMLocalMediaStream::CreateTrackUnionStream(nsPIDOMWindowInner* aWindow,
-                                            MediaStreamGraph* aGraph)
+                                            MediaStreamGraph* aGraph,
+                                            MediaStreamTrackSourceGetter* aTrackSourceGetter)
 {
-  RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(aWindow);
+  RefPtr<DOMLocalMediaStream> stream =
+    new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
   stream->InitTrackUnionStream(aGraph);
   return stream.forget();
 }
 
 already_AddRefed<DOMLocalMediaStream>
 DOMLocalMediaStream::CreateAudioCaptureStream(nsPIDOMWindowInner* aWindow,
                                               MediaStreamGraph* aGraph)
 {
-  RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(aWindow);
+  // AudioCapture doesn't create tracks dynamically
+  MediaStreamTrackSourceGetter* getter = nullptr;
+  RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(aWindow, getter);
   stream->InitAudioCaptureStream(aGraph);
   return stream.forget();
 }
 
 DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(nsPIDOMWindowInner* aWindow, AudioNode* aNode)
-  : DOMMediaStream(aWindow),
+  : DOMMediaStream(aWindow, nullptr),
     mStreamNode(aNode)
 {
 }
 
 DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream()
 {
 }
 
@@ -1049,17 +1076,17 @@ DOMAudioNodeMediaStream::CreateTrackUnio
                                                 MediaStreamGraph* aGraph)
 {
   RefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aWindow, aNode);
   stream->InitTrackUnionStream(aGraph);
   return stream.forget();
 }
 
 DOMHwMediaStream::DOMHwMediaStream(nsPIDOMWindowInner* aWindow)
-  : DOMLocalMediaStream(aWindow)
+  : DOMLocalMediaStream(aWindow, nullptr)
 {
 }
 
 DOMHwMediaStream::~DOMHwMediaStream()
 {
 }
 
 already_AddRefed<DOMHwMediaStream>
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -38,16 +38,17 @@ class MediaStream;
 class MediaInputPort;
 class MediaStreamGraph;
 class ProcessedMediaStream;
 
 namespace dom {
 class AudioNode;
 class HTMLCanvasElement;
 class MediaStreamTrack;
+class MediaStreamTrackSource;
 class AudioStreamTrack;
 class VideoStreamTrack;
 class MediaStreamTrackSource;
 class AudioTrack;
 class VideoTrack;
 class AudioTrackList;
 class VideoTrackList;
 class MediaTrackListListener;
@@ -67,16 +68,43 @@ class MediaStreamDirectListener;
 
 class OnTracksAvailableCallback {
 public:
   virtual ~OnTracksAvailableCallback() {}
   virtual void NotifyTracksAvailable(DOMMediaStream* aStream) = 0;
 };
 
 /**
+ * Interface through which a DOMMediaStream can query its producer for a
+ * MediaStreamTrackSource. This will be used whenever a track occurs in the
+ * DOMMediaStream's owned stream that has not yet been created on the main
+ * thread (see DOMMediaStream::CreateOwnDOMTrack).
+ */
+class MediaStreamTrackSourceGetter : public nsISupports
+{
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSourceGetter)
+
+public:
+  MediaStreamTrackSourceGetter()
+  {
+    MOZ_COUNT_CTOR(MediaStreamTrackSourceGetter);
+  }
+
+  virtual already_AddRefed<dom::MediaStreamTrackSource>
+  GetMediaStreamTrackSource(TrackID aInputTrackID) = 0;
+
+protected:
+  virtual ~MediaStreamTrackSourceGetter()
+  {
+    MOZ_COUNT_DTOR(MediaStreamTrackSourceGetter);
+  }
+};
+
+/**
  * DOM wrapper for MediaStreams.
  *
  * To account for track operations such as clone(), addTrack() and
  * removeTrack(), a DOMMediaStream wraps three internal (and chained)
  * MediaStreams:
  *   1. mInputStream
  *      - Controlled by the owner/source of the DOMMediaStream.
  *        It's a stream of the type indicated by
@@ -286,17 +314,18 @@ public:
     RefPtr<MediaInputPort> mInputPort;
     RefPtr<MediaStreamTrack> mTrack;
 
     // Defines if we've been given ownership of the input port or if it's owned
     // externally. The owner is responsible for destroying the port.
     const InputPortOwnership mOwnership;
   };
 
-  explicit DOMMediaStream(nsPIDOMWindowInner* aWindow);
+  DOMMediaStream(nsPIDOMWindowInner* aWindow,
+                 MediaStreamTrackSourceGetter* aTrackSourceGetter);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMMediaStream,
                                            DOMEventTargetHelper)
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMMEDIASTREAM_IID)
 
   nsPIDOMWindowInner* GetParentObject() const
@@ -450,24 +479,26 @@ public:
 
   // 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; }
 
   /**
    * Create a DOMMediaStream whose underlying input stream is a SourceMediaStream.
    */
-  static already_AddRefed<DOMMediaStream>
-  CreateSourceStream(nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph);
+  static already_AddRefed<DOMMediaStream> CreateSourceStream(nsPIDOMWindowInner* aWindow,
+                                                             MediaStreamGraph* aGraph,
+                                                             MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create a DOMMediaStream whose underlying input stream is a TrackUnionStream.
    */
-  static already_AddRefed<DOMMediaStream>
-  CreateTrackUnionStream(nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph);
+  static already_AddRefed<DOMMediaStream> CreateTrackUnionStream(nsPIDOMWindowInner* aWindow,
+                                                                 MediaStreamGraph* aGraph,
+                                                                 MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an DOMMediaStream whose underlying input stream is an
    * AudioCaptureStream.
    */
   static already_AddRefed<DOMMediaStream>
   CreateAudioCaptureStream(nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph);
 
@@ -586,16 +617,20 @@ protected:
   RefPtr<MediaInputPort> mPlaybackPort;
 
   // MediaStreamTracks corresponding to tracks in our mOwnedStream.
   AutoTArray<RefPtr<TrackPort>, 2> mOwnedTracks;
 
   // MediaStreamTracks corresponding to tracks in our mPlaybackStream.
   AutoTArray<RefPtr<TrackPort>, 2> mTracks;
 
+  // The interface through which we can query the stream producer for
+  // track sources.
+  RefPtr<MediaStreamTrackSourceGetter> mTrackSourceGetter;
+
   RefPtr<OwnedStreamListener> mOwnedListener;
   RefPtr<PlaybackStreamListener> mPlaybackListener;
 
   nsTArray<nsAutoPtr<OnTracksAvailableCallback> > mRunOnTracksAvailable;
 
   // Set to true after MediaStreamGraph has created tracks for mPlaybackStream.
   bool mTracksCreated;
 
@@ -627,39 +662,42 @@ NS_DEFINE_STATIC_IID_ACCESSOR(DOMMediaSt
 
 #define NS_DOMLOCALMEDIASTREAM_IID \
 { 0xb1437260, 0xec61, 0x4dfa, \
   { 0x92, 0x54, 0x04, 0x44, 0xe2, 0xb5, 0x94, 0x9c } }
 
 class DOMLocalMediaStream : public DOMMediaStream
 {
 public:
-  explicit DOMLocalMediaStream(nsPIDOMWindowInner* aWindow)
-    : DOMMediaStream(aWindow) {}
+  explicit DOMLocalMediaStream(nsPIDOMWindowInner* aWindow,
+                               MediaStreamTrackSourceGetter* aTrackSourceGetter)
+    : DOMMediaStream(aWindow, aTrackSourceGetter) {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMLOCALMEDIASTREAM_IID)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Stop();
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
   CreateSourceStream(nsPIDOMWindowInner* aWindow,
-                     MediaStreamGraph* aGraph);
+                     MediaStreamGraph* aGraph,
+                     MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
   CreateTrackUnionStream(nsPIDOMWindowInner* aWindow,
-                         MediaStreamGraph* aGraph);
+                         MediaStreamGraph* aGraph,
+                         MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is an
    * AudioCaptureStream. */
   static already_AddRefed<DOMLocalMediaStream>
   CreateAudioCaptureStream(nsPIDOMWindowInner* aWindow,
                            MediaStreamGraph* aGraph);
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -662,27 +662,29 @@ nsresult AudioDevice::Restart(const dom:
  * that need to be cleaned up.
  */
 class nsDOMUserMediaStream : public DOMLocalMediaStream
 {
 public:
   static already_AddRefed<nsDOMUserMediaStream>
   CreateSourceStream(nsPIDOMWindowInner* aWindow,
                      GetUserMediaCallbackMediaStreamListener* aListener,
-                     MediaStreamGraph* aMSG)
+                     MediaStreamGraph* aMSG,
+                     MediaStreamTrackSourceGetter* aTrackSourceGetter)
   {
-    RefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aWindow,
-                                                                   aListener);
+    RefPtr<nsDOMUserMediaStream> stream =
+      new nsDOMUserMediaStream(aWindow, aListener, aTrackSourceGetter);
     stream->InitSourceStream(aMSG);
     return stream.forget();
   }
 
   nsDOMUserMediaStream(nsPIDOMWindowInner* aWindow,
-                       GetUserMediaCallbackMediaStreamListener* aListener) :
-    DOMLocalMediaStream(aWindow),
+                       GetUserMediaCallbackMediaStreamListener* aListener,
+                       MediaStreamTrackSourceGetter* aTrackSourceGetter) :
+    DOMLocalMediaStream(aWindow, aTrackSourceGetter),
     mListener(aListener)
   {}
 
   virtual ~nsDOMUserMediaStream()
   {
     StopImpl();
 
     if (GetSourceStream()) {
@@ -932,19 +934,20 @@ public:
         ~LocalTrackSource() {}
 
         RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
         const MediaSourceEnum mSource;
         const TrackID mTrackID;
       };
 
       // Normal case, connect the source stream to the track union stream to
-      // avoid us blocking
-      domStream = nsDOMUserMediaStream::CreateSourceStream(window, mListener,
-                                                           msg);
+      // avoid us blocking. Pass a null TrackSourceGetter since gUM should never
+      // add tracks dynamically.
+      domStream =
+        nsDOMUserMediaStream::CreateSourceStream(window, mListener, msg, nullptr);
 
       if (mAudioDevice) {
         nsString audioDeviceName;
         mAudioDevice->GetName(audioDeviceName);
         const MediaSourceEnum source =
           mAudioDevice->GetSource()->GetMediaSource();
         RefPtr<MediaStreamTrackSource> audioSource =
           new LocalTrackSource(mListener, source, kAudioTrack);