Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r?roc draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 05 Nov 2015 15:42:22 +0800
changeset 306641 39f30ac191a51ae57aa2b99c437f3c8bcb1e9ef4
parent 306640 1adfa472c04460bf2b0aea9031fb42f0d8d9608f
child 306642 04e4136978cd87274327cbb79a142aca199b3aaf
push id7183
push userpehrsons@gmail.com
push dateThu, 05 Nov 2015 07:42:40 +0000
reviewersroc
bugs1208371
milestone45.0a1
Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r?roc This allows DOMMediaStream to assign MediaStreamTrackSources to dynamically created MediaStreamTracks.
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,
                                        nsPIDOMWindow* 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
@@ -1846,16 +1846,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:
+    virtual already_AddRefed<dom::MediaStreamTrackSource>
+    GetMediaStreamTrackSource(TrackID aInputTrackID) override
+    {
+      return do_AddRef(new BasicUnstoppableTrackSource());
+    }
+
+  protected:
+    virtual ~CaptureStreamTrackSourceGetter() {}
+  };
+
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
 #ifdef MOZ_EME
   if (ContainsRestrictedContent()) {
     return nullptr;
   }
@@ -1864,17 +1877,18 @@ HTMLMediaElement::CaptureStreamInternal(
   if (!aGraph) {
     MediaStreamGraph::GraphDriverType graphDriverType =
       HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
                  : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
     aGraph = MediaStreamGraph::GetInstance(graphDriverType, mAudioChannel);
   }
 
   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(nsIDOMWindow* 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
@@ -121,17 +121,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, 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, source);
     }
   }
 
   void DoNotifyTrackEnded(TrackID aTrackId)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!mStream) {
@@ -303,22 +311,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(nsIDOMWindow* aWindow)
+DOMMediaStream::DOMMediaStream(nsIDOMWindow* 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 (!gMediaStreamLog) {
     gMediaStreamLog = PR_NewLogModule("MediaStream");
   }
@@ -416,17 +425,19 @@ DOMMediaStream::Constructor(const Global
                             ErrorResult& aRv)
 {
   nsCOMPtr<nsIDOMWindow> 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);
@@ -664,37 +675,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(nsIDOMWindow* 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(nsIDOMWindow* 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(nsIDOMWindow* 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.
@@ -987,43 +1001,49 @@ DOMLocalMediaStream::StopImpl()
 {
   if (mInputStream && mInputStream->AsSourceStream()) {
     mInputStream->AsSourceStream()->EndAllTrackAndFinish();
   }
 }
 
 already_AddRefed<DOMLocalMediaStream>
 DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* 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(nsIDOMWindow* 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(nsIDOMWindow* 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(nsIDOMWindow* aWindow, AudioNode* aNode)
-  : DOMMediaStream(aWindow),
+  : DOMMediaStream(aWindow, nullptr),
     mStreamNode(aNode)
 {
 }
 
 DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream()
 {
 }
 
@@ -1033,17 +1053,17 @@ DOMAudioNodeMediaStream::CreateTrackUnio
                                                 MediaStreamGraph* aGraph)
 {
   RefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aWindow, aNode);
   stream->InitTrackUnionStream(aGraph);
   return stream.forget();
 }
 
 DOMHwMediaStream::DOMHwMediaStream(nsIDOMWindow* aWindow)
-  : DOMLocalMediaStream(aWindow)
+  : DOMLocalMediaStream(aWindow, nullptr)
 {
 #ifdef MOZ_WIDGET_GONK
   mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
   RefPtr<Image> img = mImageContainer->CreateImage(ImageFormat::OVERLAY_IMAGE);
   mOverlayImage = static_cast<layers::OverlayImage*>(img.get());
   nsAutoTArray<ImageContainer::NonOwningImage,1> images;
   images.AppendElement(ImageContainer::NonOwningImage(img));
   mImageContainer->SetCurrentImages(images);
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -11,16 +11,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "StreamBuffer.h"
 #include "nsIDOMWindow.h"
 #include "nsIPrincipal.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
 // currentTime getter.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
@@ -39,16 +40,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;
@@ -68,16 +70,42 @@ 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).
+ *
+ * This class is wholly owned by DOMMediaStream for the entirety of the
+ * stream's lifetime.
+ */
+class MediaStreamTrackSourceGetter
+{
+public:
+  MediaStreamTrackSourceGetter()
+  {
+    MOZ_COUNT_CTOR(MediaStreamTrackSourceGetter);
+  }
+
+  virtual already_AddRefed<dom::MediaStreamTrackSource>
+  GetMediaStreamTrackSource(TrackID aInputTrackID) = 0;
+
+  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
@@ -290,17 +318,17 @@ 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(nsIDOMWindow* aWindow);
+  DOMMediaStream(nsIDOMWindow* 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)
 
   nsIDOMWindow* GetParentObject() const
@@ -455,23 +483,25 @@ 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(nsIDOMWindow* aWindow,
-                                                             MediaStreamGraph* aGraph);
+                                                             MediaStreamGraph* aGraph,
+                                                             MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create a DOMMediaStream whose underlying input stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMMediaStream> CreateTrackUnionStream(nsIDOMWindow* aWindow,
-                                                                 MediaStreamGraph* aGraph);
+                                                                 MediaStreamGraph* aGraph,
+                                                                 MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an DOMMediaStream whose underlying input stream is an
    * AudioCaptureStream.
    */
   static already_AddRefed<DOMMediaStream> CreateAudioCaptureStream(
     nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
 
@@ -589,16 +619,20 @@ protected:
   RefPtr<MediaInputPort> mPlaybackPort;
 
   // MediaStreamTracks corresponding to tracks in our mOwnedStream.
   nsAutoTArray<RefPtr<TrackPort>, 2> mOwnedTracks;
 
   // MediaStreamTracks corresponding to tracks in our mPlaybackStream.
   nsAutoTArray<RefPtr<TrackPort>, 2> mTracks;
 
+  // The interface through which we can query the stream producer for
+  // track sources.
+  UniquePtr<MediaStreamTrackSourceGetter> mTrackSourceGetter;
+
   RefPtr<OwnedStreamListener> mOwnedListener;
   RefPtr<PlaybackStreamListener> mPlaybackListener;
 
   nsTArray<nsAutoPtr<OnTracksAvailableCallback> > mRunOnTracksAvailable;
 
   // Set to true after MediaStreamGraph has created tracks for mPlaybackStream.
   bool mTracksCreated;
 
@@ -630,39 +664,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(nsIDOMWindow* aWindow)
-    : DOMMediaStream(aWindow) {}
+  explicit DOMLocalMediaStream(nsIDOMWindow* 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(nsIDOMWindow* aWindow,
-                     MediaStreamGraph* aGraph);
+                     MediaStreamGraph* aGraph,
+                     MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
   CreateTrackUnionStream(nsIDOMWindow* aWindow,
-                         MediaStreamGraph* aGraph);
+                         MediaStreamGraph* aGraph,
+                         MediaStreamTrackSourceGetter* aTrackSourceGetter = nullptr);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is an
    * AudioCaptureStream. */
   static already_AddRefed<DOMLocalMediaStream> CreateAudioCaptureStream(
     nsIDOMWindow* aWindow, MediaStreamGraph* aGraph);
 
 protected:
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -635,27 +635,29 @@ nsresult AudioDevice::Restart(const dom:
  * that need to be cleaned up.
  */
 class nsDOMUserMediaStream : public DOMLocalMediaStream
 {
 public:
   static already_AddRefed<nsDOMUserMediaStream>
   CreateSourceStream(nsIDOMWindow* 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(nsIDOMWindow* aWindow,
-                       GetUserMediaCallbackMediaStreamListener* aListener) :
-    DOMLocalMediaStream(aWindow),
+                       GetUserMediaCallbackMediaStreamListener* aListener,
+                       MediaStreamTrackSourceGetter* aTrackSourceGetter) :
+    DOMLocalMediaStream(aWindow, aTrackSourceGetter),
     mListener(aListener),
     mEchoOn(true),
     mAgcOn(false),
     mNoiseOn(true),
 #ifdef MOZ_WEBRTC
     mEcho(webrtc::kEcDefault),
     mAgc(webrtc::kAgcDefault),
     mNoise(webrtc::kNsDefault),
@@ -969,20 +971,40 @@ public:
       protected:
         ~LocalTrackSource() {}
 
         RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
         const MediaSourceEnum mSource;
         const TrackID mTrackID;
       };
 
+      class LocalTrackSourceGetter : public MediaStreamTrackSourceGetter
+      {
+      public:
+        explicit LocalTrackSourceGetter(GetUserMediaCallbackMediaStreamListener* aListener)
+          : mListener(aListener) {}
+
+        already_AddRefed<MediaStreamTrackSource>
+        GetMediaStreamTrackSource(TrackID aInputTrackID) override
+        {
+          LOG(("GetMediaStreamTrackSource() for track id %d", aInputTrackID));
+          return do_AddRef(new LocalTrackSource(mListener,
+                                                MediaSourceEnum::Other,
+                                                aInputTrackID));
+        }
+
+      protected:
+        virtual ~LocalTrackSourceGetter() {}
+
+        RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
+      };
       // Normal case, connect the source stream to the track union stream to
       // avoid us blocking
-      domStream = nsDOMUserMediaStream::CreateSourceStream(window, mListener,
-                                                           msg);
+      domStream = nsDOMUserMediaStream::CreateSourceStream(
+        window, mListener, msg, new LocalTrackSourceGetter(mListener));
 
       if (mAudioDevice) {
         const MediaSourceEnum source =
           mAudioDevice->GetSource()->GetMediaSource();
         RefPtr<MediaStreamTrackSource> audioSource =
           new LocalTrackSource(mListener, source, kAudioTrack);
         domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO,
                                      audioSource);