Bug 1208371 - Add a MediaStreamTrackSource interface. r=roc
authorAndreas Pehrson <pehrsons@gmail.com>
Wed, 06 Apr 2016 14:46:56 +0200
changeset 292065 d52afb558df4f59c5fb12415a8290a791d23bd94
parent 292064 2f5a37a6a06d01cd1d8c8eea6b48fb54408197e4
child 292066 f7580ab5826a4c17eef0ec8b632b36d8ba4e04fa
push id30152
push userkwierso@gmail.com
push dateThu, 07 Apr 2016 20:42:20 +0000
treeherdermozilla-central@06678484909c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1208371
milestone48.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 1208371 - Add a MediaStreamTrackSource interface. r=roc This lets a MediaStreamTrack communicate with its source/producer on the main thread. It's for now used for stopping a track at the source and retrieving some metadata, but it could also be a link between actual sinks of a track and the source, to for instance let the source optimize by scaling down the resolution when all sinks want lowres-video. MozReview-Commit-ID: D4SJLr0aqhJ
dom/camera/DOMCameraControl.cpp
dom/html/HTMLCanvasElement.cpp
dom/html/HTMLMediaElement.cpp
dom/media/AudioStreamTrack.h
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaManager.cpp
dom/media/MediaStreamTrack.cpp
dom/media/MediaStreamTrack.h
dom/media/VideoStreamTrack.h
dom/media/imagecapture/ImageCapture.cpp
dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/test/FakeMediaStreams.h
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -524,17 +524,18 @@ nsDOMCameraControl::GetCameraStream() co
   return mInput;
 }
 
 void
 nsDOMCameraControl::TrackCreated(TrackID aTrackID) {
   // This track is not connected through a port.
   MediaInputPort* inputPort = nullptr;
   dom::VideoStreamTrack* track =
-    new dom::VideoStreamTrack(this, aTrackID, nsString());
+    new dom::VideoStreamTrack(this, aTrackID, nsString(),
+                              new BasicUnstoppableTrackSource());
   RefPtr<TrackPort> port =
     new TrackPort(inputPort, track,
                   TrackPort::InputPortOwnership::OWNED);
   mTracks.AppendElement(port.forget());
   NotifyTrackAdded(track);
 }
 
 #define THROW_IF_NO_CAMERACONTROL(...)                                          \
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -13,16 +13,17 @@
 #include "MediaSegment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasCaptureMediaStream.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
+#include "mozilla/dom/MediaStreamTrack.h"
 #include "mozilla/dom/MouseEvent.h"
 #include "mozilla/dom/OffscreenCanvas.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
@@ -677,17 +678,18 @@ HTMLCanvasElement::CaptureStream(const O
 
   TrackID videoTrackId = 1;
   nsresult rv = stream->Init(aFrameRate, videoTrackId);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
-  stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
+  stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString(),
+                            new BasicUnstoppableTrackSource());
 
   rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return stream.forget();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1900,21 +1900,25 @@ HTMLMediaElement::CaptureStreamInternal(
   mAudioCaptured = true;
   if (mDecoder) {
     mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(),
                               aFinishWhenEnded);
     if (mReadyState >= HAVE_METADATA) {
       // Expose the tracks to JS directly.
       if (HasAudio()) {
         TrackID audioTrackId = mMediaInfo.mAudio.mTrackId;
-        out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO, nsString());
+        RefPtr<MediaStreamTrackSource> trackSource = new BasicUnstoppableTrackSource();
+        out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO,
+                                        nsString(), trackSource);
       }
       if (HasVideo()) {
         TrackID videoTrackId = mMediaInfo.mVideo.mTrackId;
-        out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
+        RefPtr<MediaStreamTrackSource> trackSource = new BasicUnstoppableTrackSource();
+        out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO,
+                                        nsString(), trackSource);
       }
     }
   }
   RefPtr<DOMMediaStream> result = out->mStream;
   return result.forget();
 }
 
 already_AddRefed<DOMMediaStream>
--- a/dom/media/AudioStreamTrack.h
+++ b/dom/media/AudioStreamTrack.h
@@ -9,18 +9,19 @@
 #include "MediaStreamTrack.h"
 #include "DOMMediaStream.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioStreamTrack : public MediaStreamTrack {
 public:
-  AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
-    : MediaStreamTrack(aStream, aTrackID, aLabel) {}
+  AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+                   const nsString& aLabel, MediaStreamTrackSource* aSource)
+    : MediaStreamTrack(aStream, aTrackID, aLabel, aSource) {}
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioStreamTrack* AsAudioStreamTrack() override { return this; }
 
   // WebIDL
   void GetKind(nsAString& aKind) override { aKind.AssignLiteral("audio"); }
 };
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -123,25 +123,26 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!mStream) {
       return;
     }
 
     MediaStreamTrack* track = mStream->FindOwnedDOMTrack(
       mStream->GetOwnedStream(), aTrackId);
-    if (track) {
-      // This track has already been manually created. Abort.
-      return;
+    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());
     }
-
-    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.");
-    mStream->CreateOwnDOMTrack(aTrackId, aType, nsString());
   }
 
   void DoNotifyTrackEnded(TrackID aTrackId)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!mStream) {
       return;
@@ -612,22 +613,25 @@ DOMMediaStream::InitTrackUnionStream(Med
   InitPlaybackStreamCommon(aGraph);
 }
 
 void
 DOMMediaStream::InitAudioCaptureStream(MediaStreamGraph* aGraph)
 {
   const TrackID AUDIO_TRACK = 1;
 
+  RefPtr<BasicUnstoppableTrackSource> audioCaptureSource =
+    new BasicUnstoppableTrackSource(MediaSourceEnum::AudioCapture);
+
   AudioCaptureStream* audioCaptureStream =
     static_cast<AudioCaptureStream*>(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK));
   InitInputStreamCommon(audioCaptureStream, aGraph);
   InitOwnedStreamCommon(aGraph);
   InitPlaybackStreamCommon(aGraph);
-  CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString());
+  CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString(), audioCaptureSource);
   audioCaptureStream->Start();
 }
 
 void
 DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
                                       MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(!mOwnedStream, "Input stream must be initialized before owned stream");
@@ -700,24 +704,16 @@ void
 DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
 {
   // XXX Bug 1208371 - This enables/disables the track across clones.
   if (mInputStream) {
     mInputStream->SetTrackEnabled(aTrackID, aEnabled);
   }
 }
 
-void
-DOMMediaStream::StopTrack(TrackID aTrackID)
-{
-  if (mInputStream && mInputStream->AsSourceStream()) {
-    mInputStream->AsSourceStream()->EndTrack(aTrackID);
-  }
-}
-
 already_AddRefed<Promise>
 DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID,
                                         const MediaTrackConstraints& aConstraints,
                                         ErrorResult &aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> promise = Promise::Create(go, aRv);
   MOZ_RELEASE_ASSERT(!aRv.Failed());
@@ -779,30 +775,32 @@ DOMMediaStream::AddPrincipalChangeObserv
 
 bool
 DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
 {
   return mPrincipalChangeObservers.RemoveElement(aObserver);
 }
 
 MediaStreamTrack*
-DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel)
+DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
+                                  const nsString& aLabel,
+                                  MediaStreamTrackSource* aSource)
 {
   MOZ_RELEASE_ASSERT(mInputStream);
   MOZ_RELEASE_ASSERT(mOwnedStream);
 
   MOZ_ASSERT(FindOwnedDOMTrack(GetOwnedStream(), aTrackID) == nullptr);
 
   MediaStreamTrack* track;
   switch (aType) {
   case MediaSegment::AUDIO:
-    track = new AudioStreamTrack(this, aTrackID, aLabel);
+    track = new AudioStreamTrack(this, aTrackID, aLabel, aSource);
     break;
   case MediaSegment::VIDEO:
-    track = new VideoStreamTrack(this, aTrackID, aLabel);
+    track = new VideoStreamTrack(this, aTrackID, aLabel, aSource);
     break;
   default:
     MOZ_CRASH("Unhandled track type");
   }
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID));
 
   RefPtr<TrackPort> ownedTrackPort =
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -30,27 +30,27 @@
 #endif
 
 namespace mozilla {
 
 class DOMHwMediaStream;
 class DOMLocalMediaStream;
 class DOMMediaStream;
 class MediaStream;
-class MediaEngineSource;
 class MediaInputPort;
 class MediaStreamGraph;
 class ProcessedMediaStream;
 
 namespace dom {
 class AudioNode;
 class HTMLCanvasElement;
 class MediaStreamTrack;
 class AudioStreamTrack;
 class VideoStreamTrack;
+class MediaStreamTrackSource;
 class AudioTrack;
 class VideoTrack;
 class AudioTrackList;
 class VideoTrackList;
 class MediaTrackListListener;
 struct MediaTrackConstraints;
 } // namespace dom
 
@@ -179,16 +179,17 @@ public:
  *                                                     (pointing to t2 in A')
  */
 class DOMMediaStream : public DOMEventTargetHelper
 {
   friend class DOMLocalMediaStream;
   typedef dom::MediaStreamTrack MediaStreamTrack;
   typedef dom::AudioStreamTrack AudioStreamTrack;
   typedef dom::VideoStreamTrack VideoStreamTrack;
+  typedef dom::MediaStreamTrackSource MediaStreamTrackSource;
   typedef dom::AudioTrack AudioTrack;
   typedef dom::VideoTrack VideoTrack;
   typedef dom::AudioTrackList AudioTrackList;
   typedef dom::VideoTrackList VideoTrackList;
 
 public:
   typedef dom::MediaTrackConstraints MediaTrackConstraints;
 
@@ -376,18 +377,16 @@ public:
   virtual void RemoveDirectListener(MediaStreamDirectListener *aListener) {}
 
   /**
    * Overridden in DOMLocalMediaStreams to allow getUserMedia to disable
    * media at the SourceMediaStream.
    */
   virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
 
-  virtual void StopTrack(TrackID aTrackID);
-
   virtual already_AddRefed<dom::Promise>
   ApplyConstraintsToTrack(TrackID aTrackID,
                           const MediaTrackConstraints& aConstraints,
                           ErrorResult &aRv);
 
   virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
   virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
 
@@ -478,17 +477,19 @@ public:
   }
 
   /**
    * Called for each track in our owned stream to indicate to JS that we
    * are carrying that track.
    *
    * Creates a MediaStreamTrack, adds it to mTracks and returns it.
    */
-  MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel);
+  MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
+                                      const nsString& aLabel,
+                                      MediaStreamTrackSource* aSource);
 
   // When the initial set of tracks has been added, run
   // aCallback->NotifyTracksAvailable.
   // It is allowed to do anything, including run script.
   // aCallback may run immediately during this call if tracks are already
   // available!
   // We only care about track additions, we'll fire the notification even if
   // some of the tracks have been removed.
@@ -636,18 +637,16 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMLOCALMEDIASTREAM_IID)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Stop();
 
-  virtual MediaEngineSource* GetMediaEngine(TrackID aTrackID) { return nullptr; }
-
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
   CreateSourceStream(nsPIDOMWindowInner* aWindow,
                      MediaStreamGraph* aGraph);
 
   /**
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -136,16 +136,17 @@ GetMediaManagerLog()
 
 using dom::ConstrainDOMStringParameters;
 using dom::File;
 using dom::GetUserMediaRequest;
 using dom::MediaSourceEnum;
 using dom::MediaStreamConstraints;
 using dom::MediaStreamError;
 using dom::MediaStreamTrack;
+using dom::MediaStreamTrackSource;
 using dom::MediaTrackConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using dom::OwningStringOrStringSequence;
 using dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters;
 using dom::Promise;
 using dom::Sequence;
 using media::NewRunnableFrom;
@@ -661,67 +662,39 @@ 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,
-                     AudioDevice* aAudioDevice,
-                     VideoDevice* aVideoDevice,
                      MediaStreamGraph* aMSG)
   {
     RefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aWindow,
-                                                                   aListener,
-                                                                   aAudioDevice,
-                                                                   aVideoDevice);
+                                                                   aListener);
     stream->InitSourceStream(aMSG);
     return stream.forget();
   }
 
   nsDOMUserMediaStream(nsPIDOMWindowInner* aWindow,
-                       GetUserMediaCallbackMediaStreamListener* aListener,
-                       AudioDevice *aAudioDevice,
-                       VideoDevice *aVideoDevice) :
+                       GetUserMediaCallbackMediaStreamListener* aListener) :
     DOMLocalMediaStream(aWindow),
-    mListener(aListener),
-    mAudioDevice(aAudioDevice),
-    mVideoDevice(aVideoDevice)
+    mListener(aListener)
   {}
 
   virtual ~nsDOMUserMediaStream()
   {
     StopImpl();
 
     if (GetSourceStream()) {
       GetSourceStream()->Destroy();
     }
   }
 
-  // For gUM streams, we have a trackunion which assigns TrackIDs.  However, for a
-  // single-source trackunion like we have here, the TrackUnion will assign trackids
-  // that match the source's trackids, so we can avoid needing a mapping function.
-  // XXX This will not handle more complex cases well.
-  void StopTrack(TrackID aTrackID) override
-  {
-    if (GetSourceStream()) {
-      GetSourceStream()->EndTrack(aTrackID);
-      // We could override NotifyMediaStreamTrackEnded(), and maybe should, but it's
-      // risky to do late in a release since that will affect all track ends, and not
-      // just StopTrack()s.
-      RefPtr<dom::MediaStreamTrack> ownedTrack = FindOwnedDOMTrack(mOwnedStream, aTrackID);
-      if (ownedTrack) {
-        mListener->StopTrack(aTrackID);
-      } else {
-        LOG(("StopTrack(%d) on non-existent track", aTrackID));
-      }
-    }
-  }
-
   already_AddRefed<Promise>
   ApplyConstraintsToTrack(TrackID aTrackID,
                           const MediaTrackConstraints& aConstraints,
                           ErrorResult &aRv) override
   {
     nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
     RefPtr<Promise> promise = Promise::Create(go, aRv);
 
@@ -779,41 +752,25 @@ public:
     }
   }
 
   DOMLocalMediaStream* AsDOMLocalMediaStream() override
   {
     return this;
   }
 
-  MediaEngineSource* GetMediaEngine(TrackID aTrackID) override
-  {
-    // MediaEngine supports only one video and on video track now and TrackID is
-    // fixed in MediaEngine.
-    if (aTrackID == kVideoTrack) {
-      return mVideoDevice ? mVideoDevice->GetSource() : nullptr;
-    }
-    else if (aTrackID == kAudioTrack) {
-      return mAudioDevice ? mAudioDevice->GetSource() : nullptr;
-    }
-
-    return nullptr;
-  }
-
   SourceMediaStream* GetSourceStream()
   {
     if (GetInputStream()) {
       return GetInputStream()->AsSourceStream();
     }
     return nullptr;
   }
 
   RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
-  RefPtr<AudioDevice> mAudioDevice; // so we can turn on AEC
-  RefPtr<VideoDevice> mVideoDevice;
 };
 
 
 void
 MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
 {
   MM_LOG(("%s , rv=%d", errorLog, rv));
   NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(mStream.forget(),
@@ -944,31 +901,70 @@ public:
       // It should be possible to pipe the capture stream to anything. CORS is
       // not a problem here, we got explicit user content.
       domStream->SetPrincipal(window->GetExtantDoc()->NodePrincipal());
       stream = msg->CreateSourceStream(nullptr); // Placeholder
       msg->RegisterCaptureStreamForWindow(
             mWindowID, domStream->GetInputStream()->AsProcessedStream());
       window->SetAudioCapture(true);
     } else {
+      class LocalTrackSource : public MediaStreamTrackSource
+      {
+      public:
+        LocalTrackSource(GetUserMediaCallbackMediaStreamListener* aListener,
+                         const MediaSourceEnum aSource,
+                         const TrackID aTrackID)
+          : MediaStreamTrackSource(false), mListener(aListener),
+            mSource(aSource), mTrackID(aTrackID) {}
+
+        MediaSourceEnum GetMediaSource() const override
+        {
+          return mSource;
+        }
+
+        void Stop() override
+        {
+          if (mListener) {
+            mListener->StopTrack(mTrackID);
+            mListener = nullptr;
+          }
+        }
+
+      protected:
+        ~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,
-                                                           mAudioDevice, mVideoDevice,
                                                            msg);
 
       if (mAudioDevice) {
         nsString audioDeviceName;
         mAudioDevice->GetName(audioDeviceName);
-        domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioDeviceName);
+        const MediaSourceEnum source =
+          mAudioDevice->GetSource()->GetMediaSource();
+        RefPtr<MediaStreamTrackSource> audioSource =
+          new LocalTrackSource(mListener, source, kAudioTrack);
+        domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO,
+                                     audioDeviceName, audioSource);
       }
       if (mVideoDevice) {
         nsString videoDeviceName;
         mVideoDevice->GetName(videoDeviceName);
-        domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoDeviceName);
+        const MediaSourceEnum source =
+          mVideoDevice->GetSource()->GetMediaSource();
+        RefPtr<MediaStreamTrackSource> videoSource =
+          new LocalTrackSource(mListener, source, kVideoTrack);
+        domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO,
+                                     videoDeviceName, videoSource);
       }
 
       nsCOMPtr<nsIPrincipal> principal;
       if (mPeerIdentity) {
         principal = nsNullPrincipal::Create();
         domStream->SetPeerIdentity(mPeerIdentity.forget());
       } else {
         principal = window->GetExtantDoc()->NodePrincipal();
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -14,24 +14,37 @@
 #endif
 
 static PRLogModuleInfo* gMediaStreamTrackLog;
 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
 
 namespace mozilla {
 namespace dom {
 
-MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
-  : mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mEnded(false), mEnabled(true)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSource)
+
+MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+                                   const nsString& aLabel,
+                                   MediaStreamTrackSource* aSource)
+  : mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mSource(aSource),
+    mEnded(false), mEnabled(true), mRemote(aSource->IsRemote()), mStopped(false)
 {
 
   if (!gMediaStreamTrackLog) {
     gMediaStreamTrackLog = PR_NewLogModule("MediaStreamTrack");
   }
 
+  MOZ_RELEASE_ASSERT(mSource);
+  mSource->RegisterSink();
+
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
 
   nsID uuid;
   memset(&uuid, 0, sizeof(uuid));
   if (uuidgen) {
     uuidgen->GenerateUUIDInPlace(&uuid);
@@ -41,18 +54,32 @@ MediaStreamTrack::MediaStreamTrack(DOMMe
   uuid.ToProvidedString(chars);
   mID = NS_ConvertASCIItoUTF16(chars);
 }
 
 MediaStreamTrack::~MediaStreamTrack()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamTrack, DOMEventTargetHelper,
-                                   mOwningStream, mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningStream)
+  tmp->mSource->UnregisterSink();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningStream)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 void
 MediaStreamTrack::GetId(nsAString& aID) const
@@ -70,17 +97,33 @@ MediaStreamTrack::SetEnabled(bool aEnabl
   mOwningStream->SetTrackEnabled(mTrackID, aEnabled);
 }
 
 void
 MediaStreamTrack::Stop()
 {
   LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
 
-  mOwningStream->StopTrack(mTrackID);
+  if (mStopped) {
+    LOG(LogLevel::Warning, ("MediaStreamTrack %p Already stopped", this));
+    return;
+  }
+
+  if (mRemote) {
+    LOG(LogLevel::Warning, ("MediaStreamTrack %p is remote. Can't be stopped.", this));
+    return;
+  }
+
+  if (!mSource) {
+    MOZ_ASSERT(false);
+    return;
+  }
+
+  mSource->UnregisterSink();
+  mStopped = true;
 }
 
 already_AddRefed<Promise>
 MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
                                    ErrorResult &aRv)
 {
   if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) {
     nsString str;
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -2,39 +2,149 @@
 /* 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 MEDIASTREAMTRACK_H_
 #define MEDIASTREAMTRACK_H_
 
 #include "mozilla/DOMEventTargetHelper.h"
+#include "nsError.h"
 #include "nsID.h"
 #include "StreamBuffer.h"
 #include "MediaTrackConstraints.h"
 
 namespace mozilla {
 
 class DOMMediaStream;
+class MediaEnginePhotoCallback;
 
 namespace dom {
 
 class AudioStreamTrack;
 class VideoStreamTrack;
 
 /**
+ * Common interface through which a MediaStreamTrack can communicate with its
+ * producer on the main thread.
+ *
+ * Kept alive by a strong ref in all MediaStreamTracks (original and clones)
+ * sharing this source.
+ */
+class MediaStreamTrackSource : public nsISupports
+{
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
+
+public:
+  explicit MediaStreamTrackSource(const bool aIsRemote)
+    : mNrSinks(0), mIsRemote(aIsRemote), mStopped(false)
+  {
+    MOZ_COUNT_CTOR(MediaStreamTrackSource);
+  }
+
+  /**
+   * Gets the source's MediaSourceEnum for usage by PeerConnections.
+   */
+  virtual MediaSourceEnum GetMediaSource() const = 0;
+
+  /**
+   * Indicates whether the track is remote or not per the MediaCapture and
+   * Streams spec.
+   */
+  virtual bool IsRemote() const { return mIsRemote; }
+
+  /**
+   * Forwards a photo request to backends that support it. Other backends return
+   * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
+   * should be used.
+   */
+  virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; }
+
+  /**
+   * Called by the source interface when all registered sinks have unregistered.
+   */
+  virtual void Stop() = 0;
+
+  /**
+   * Called by each MediaStreamTrack clone on initialization.
+   */
+  void RegisterSink()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (mStopped) {
+      return;
+    }
+    ++mNrSinks;
+  }
+
+  /**
+   * Called by each MediaStreamTrack clone on track.Stop().
+   */
+  void UnregisterSink()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    NS_ASSERTION(mNrSinks > 0, "Unmatched UnregisterSink()");
+    --mNrSinks;
+    if (mNrSinks == 0 && !IsRemote()) {
+      Stop();
+      mStopped = true;
+    }
+  }
+
+protected:
+  virtual ~MediaStreamTrackSource()
+  {
+    MOZ_COUNT_DTOR(MediaStreamTrackSource);
+    NS_ASSERTION(mNrSinks == 0, "Some sinks did not unregister");
+  }
+
+  // Number of currently registered sinks.
+  size_t mNrSinks;
+
+  // True if this is a remote track source, i.e., a PeerConnection.
+  const bool mIsRemote;
+
+  // True if this source is not remote, all MediaStreamTrack users have
+  // unregistered from this source and Stop() has been called.
+  bool mStopped;
+};
+
+/**
+ * Basic implementation of MediaStreamTrackSource that ignores Stop().
+ */
+class BasicUnstoppableTrackSource : public MediaStreamTrackSource
+{
+public:
+  explicit BasicUnstoppableTrackSource(const MediaSourceEnum aMediaSource =
+                                         MediaSourceEnum::Other)
+    : MediaStreamTrackSource(true), mMediaSource(aMediaSource) {}
+
+  MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
+
+  void Stop() override {}
+
+protected:
+  ~BasicUnstoppableTrackSource() {}
+
+  const MediaSourceEnum mMediaSource;
+};
+
+/**
  * Class representing a track in a DOMMediaStream.
  */
 class MediaStreamTrack : public DOMEventTargetHelper {
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
    * MediaStream owned by aStream.
    */
-  MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel);
+  MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+                   const nsString& aLabel,
+                   MediaStreamTrackSource* aSource);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
                                            DOMEventTargetHelper)
 
   DOMMediaStream* GetParentObject() const { return mOwningStream; }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
 
@@ -60,28 +170,38 @@ public:
   void Stop();
   already_AddRefed<Promise>
   ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
 
   bool Ended() const { return mEnded; }
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
+  MediaStreamTrackSource& GetSource() const
+  {
+    MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction");
+    return *mSource;
+  }
+
   // Webrtc allows the remote side to name tracks whatever it wants, and we
   // need to surface this to content.
   void AssignId(const nsAString& aID) { mID = aID; }
 
 protected:
   virtual ~MediaStreamTrack();
 
   RefPtr<DOMMediaStream> mOwningStream;
   TrackID mTrackID;
+  TrackID mInputTrackID;
+  RefPtr<MediaStreamTrackSource> mSource;
   RefPtr<MediaStreamTrack> mOriginalTrack;
   nsString mID;
   nsString mLabel;
   bool mEnded;
   bool mEnabled;
+  const bool mRemote;
+  bool mStopped;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* MEDIASTREAMTRACK_H_ */
--- a/dom/media/VideoStreamTrack.h
+++ b/dom/media/VideoStreamTrack.h
@@ -9,18 +9,20 @@
 #include "MediaStreamTrack.h"
 #include "DOMMediaStream.h"
 
 namespace mozilla {
 namespace dom {
 
 class VideoStreamTrack : public MediaStreamTrack {
 public:
-  VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
-    : MediaStreamTrack(aStream, aTrackID, aLabel) {}
+  VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+                   const nsString& aLabel,
+                   MediaStreamTrackSource* aSource)
+    : MediaStreamTrack(aStream, aTrackID, aLabel, aSource) {}
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   VideoStreamTrack* AsVideoStreamTrack() override { return this; }
 
   // WebIDL
   void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); }
 };
--- a/dom/media/imagecapture/ImageCapture.cpp
+++ b/dom/media/imagecapture/ImageCapture.cpp
@@ -76,23 +76,24 @@ nsresult
 ImageCapture::TakePhotoByMediaEngine()
 {
   // Callback for TakPhoto(), it also monitor the principal. If principal
   // changes, it returns PHOTO_ERROR with security error.
   class TakePhotoCallback : public MediaEnginePhotoCallback,
                             public DOMMediaStream::PrincipalChangeObserver
   {
   public:
-    TakePhotoCallback(DOMMediaStream* aStream, ImageCapture* aImageCapture)
-      : mStream(aStream)
+    TakePhotoCallback(VideoStreamTrack* aVideoTrack, ImageCapture* aImageCapture)
+      : mVideoTrack(aVideoTrack)
       , mImageCapture(aImageCapture)
       , mPrincipalChanged(false)
     {
       MOZ_ASSERT(NS_IsMainThread());
-      mStream->AddPrincipalChangeObserver(this);
+      MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
+      mVideoTrack->GetStream()->AddPrincipalChangeObserver(this);
     }
 
     void PrincipalChanged(DOMMediaStream* aMediaStream) override
     {
       mPrincipalChanged = true;
     }
 
     nsresult PhotoComplete(already_AddRefed<Blob> aBlob) override
@@ -109,35 +110,28 @@ ImageCapture::TakePhotoByMediaEngine()
     {
       return mImageCapture->PostErrorEvent(ImageCaptureError::PHOTO_ERROR, aRv);
     }
 
   protected:
     ~TakePhotoCallback()
     {
       MOZ_ASSERT(NS_IsMainThread());
-      mStream->RemovePrincipalChangeObserver(this);
+      MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
+      mVideoTrack->GetStream()->RemovePrincipalChangeObserver(this);
     }
 
-    RefPtr<DOMMediaStream> mStream;
+    RefPtr<VideoStreamTrack> mVideoTrack;
     RefPtr<ImageCapture> mImageCapture;
     bool mPrincipalChanged;
   };
 
-  RefPtr<DOMMediaStream> domStream = mVideoStreamTrack->GetStream();
-  DOMLocalMediaStream* domLocalStream = domStream->AsDOMLocalMediaStream();
-  if (domLocalStream) {
-    RefPtr<MediaEngineSource> mediaEngine =
-      domLocalStream->GetMediaEngine(mVideoStreamTrack->GetTrackID());
-    RefPtr<MediaEnginePhotoCallback> callback =
-      new TakePhotoCallback(domStream, this);
-    return mediaEngine->TakePhoto(callback);
-  }
-
-  return NS_ERROR_NOT_IMPLEMENTED;
+  RefPtr<MediaEnginePhotoCallback> callback =
+    new TakePhotoCallback(mVideoStreamTrack, this);
+  return mVideoStreamTrack->GetSource().TakePhoto(callback);
 }
 
 void
 ImageCapture::TakePhoto(ErrorResult& aResult)
 {
   // According to spec, VideoStreamTrack.readyState must be "live"; however
   // gecko doesn't implement it yet (bug 910249). Instead of readyState, we
   // check VideoStreamTrack.enable before bug 910249 is fixed.
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamAudioDestinationNode.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/MediaStreamAudioDestinationNodeBinding.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
+#include "MediaStreamTrack.h"
 #include "TrackUnionStream.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamAudioDestinationNode, AudioNode, mDOMStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioDestinationNode)
@@ -29,17 +30,20 @@ MediaStreamAudioDestinationNode::MediaSt
               ChannelCountMode::Explicit,
               ChannelInterpretation::Speakers)
   , mDOMStream(
       DOMAudioNodeMediaStream::CreateTrackUnionStream(GetOwner(),
                                                       this,
                                                       aContext->Graph()))
 {
   // Ensure an audio track with the correct ID is exposed to JS
-  mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK, MediaSegment::AUDIO, nsString());
+  RefPtr<MediaStreamTrackSource> source =
+    new BasicUnstoppableTrackSource(MediaSourceEnum::AudioCapture);
+  mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK,
+                                MediaSegment::AUDIO, nsString(), source);
 
   ProcessedMediaStream* outputStream = mDOMStream->GetInputStream()->AsProcessedStream();
   MOZ_ASSERT(!!outputStream);
   AudioNodeEngine* engine = new AudioNodeEngine(this);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::EXTERNAL_OUTPUT);
   mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
 
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -844,20 +844,17 @@ MediaPipelineFactory::ConfigureVideoCode
   RefPtr<DOMMediaStream> mediastream =
     mPCMedia->GetLocalStreamById(aTrack.GetStreamId())->GetMediaStream();
 
   DOMLocalMediaStream* domLocalStream = mediastream->AsDOMLocalMediaStream();
   if (!domLocalStream) {
     return NS_OK;
   }
 
-  MediaEngineSource *engine =
-    domLocalStream->GetMediaEngine(videotrack->GetTrackID());
-
-  dom::MediaSourceEnum source = engine->GetMediaSource();
+  dom::MediaSourceEnum source = videotrack->GetSource().GetMediaSource();
   webrtc::VideoCodecMode mode = webrtc::kRealtimeVideo;
   switch (source) {
     case dom::MediaSourceEnum::Browser:
     case dom::MediaSourceEnum::Screen:
     case dom::MediaSourceEnum::Application:
     case dom::MediaSourceEnum::Window:
       mode = webrtc::kScreensharing;
       break;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1874,16 +1874,29 @@ PeerConnectionImpl::SetRemoteDescription
             ++numNewAudioTracks;
           } else if (track->GetMediaType() == SdpMediaSection::kVideo) {
             ++numNewVideoTracks;
           } else {
             MOZ_ASSERT(false);
             continue;
           }
           info->AddTrack(track->GetTrackId());
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+          RefPtr<MediaStreamTrackSource> source =
+            new BasicUnstoppableTrackSource(MediaSourceEnum::Other);
+          if (track->GetMediaType() == SdpMediaSection::kAudio) {
+            info->GetMediaStream()->CreateOwnDOMTrack(
+              info->GetNumericTrackId(track->GetTrackId()),
+              MediaSegment::AUDIO, nsString(), source);
+          } else {
+            info->GetMediaStream()->CreateOwnDOMTrack(
+              info->GetNumericTrackId(track->GetTrackId()),
+              MediaSegment::VIDEO, nsString(), source);
+          }
+#endif
           CSFLogDebug(logTag, "Added remote track %s/%s",
                       info->GetId().c_str(), track->GetTrackId().c_str());
         } else {
           ++numPreexistingTrackIds;
         }
       }
 
       // Now that the streams are all set up, notify about track availability.
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -281,16 +281,24 @@ class Fake_SourceMediaStream : public Fa
   bool mStop;
   RefPtr<Fake_MediaPeriodic> mPeriodic;
   RefPtr<Fake_VideoSink> mSink;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 class Fake_DOMMediaStream;
 
+class Fake_MediaStreamTrackSource
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrackSource)
+
+protected:
+  virtual ~Fake_MediaStreamTrackSource() {}
+};
+
 class Fake_MediaStreamTrack
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrack)
 
   Fake_MediaStreamTrack(bool aIsVideo, Fake_DOMMediaStream* aStream) :
     mIsVideo (aIsVideo),
     mStream (aStream)
@@ -418,17 +426,18 @@ public:
   OwnsTrack(const Fake_MediaStreamTrack& aTrack) const
   {
     return HasTrack(aTrack);
   }
 
   void SetTrackEnabled(mozilla::TrackID aTrackID, bool aEnabled) {}
 
   Fake_MediaStreamTrack*
-  CreateOwnDOMTrack(mozilla::TrackID aTrackID, mozilla::MediaSegment::Type aType)
+  CreateOwnDOMTrack(mozilla::TrackID aTrackID, mozilla::MediaSegment::Type aType,
+                    const nsString& aLabel, Fake_MediaStreamTrackSource* aSource)
   {
     switch(aType) {
       case mozilla::MediaSegment::AUDIO: {
         return mAudioTrack;
       }
       case mozilla::MediaSegment::VIDEO: {
         return mVideoTrack;
       }
@@ -498,11 +507,15 @@ class Fake_VideoStreamSource : public Fa
 
 namespace mozilla {
 typedef Fake_MediaStream MediaStream;
 typedef Fake_SourceMediaStream SourceMediaStream;
 typedef Fake_MediaStreamListener MediaStreamListener;
 typedef Fake_MediaStreamDirectListener MediaStreamDirectListener;
 typedef Fake_DOMMediaStream DOMMediaStream;
 typedef Fake_DOMMediaStream DOMLocalMediaStream;
+
+namespace dom {
+typedef Fake_MediaStreamTrackSource MediaStreamTrackSource;
+}
 }
 
 #endif