Bug 1208378 - Distinguish track sinks on whether their presence allows a source to stop. r=jib
authorAndreas Pehrson <pehrsons@mozilla.com>
Fri, 10 Nov 2017 15:08:02 +0100
changeset 392788 a13e81bd644344b4d4d045215137ebbbf2b06793
parent 392787 b28d805676ddeb390b8d6a2ffdddbafca47a46f5
child 392789 b2cc4de35eb18336f217f9356ebadc31a7c807dd
push id97527
push usertoros@mozilla.com
push dateTue, 21 Nov 2017 10:20:05 +0000
treeherdermozilla-inbound@60d0f3ee0c43 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs1208378
milestone59.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 1208378 - Distinguish track sinks on whether their presence allows a source to stop. r=jib There are currently two types of sinks for a MediaStreamTrackSource. Actual MediaStreamTracks and HTMLMediaElement::StreamCaptureTrackSource. A source needs actual tracks as sinks to not stop() the underlying source. A StreamCaptureTrackSource, however, should not count toward keeping a source alive (otherwise HTMLMediaElement.mozCaptureStream() would prevent track.stop() from working on the track feeding the media element). MozReview-Commit-ID: 9MBAyZFZUIQ
dom/html/HTMLMediaElement.cpp
dom/media/MediaStreamTrack.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3193,16 +3193,25 @@ public:
       mElement->NotifyOutputTrackStopped(mOwningStream, mDestinationTrackID);
     }
     mElement = nullptr;
     mOwningStream = nullptr;
 
     Destroy();
   }
 
+  /**
+   * Do not keep the track source alive. The source lifetime is controlled by
+   * its associated tracks.
+   */
+  bool KeepsSourceAlive() const override
+  {
+    return false;
+  }
+
   void PrincipalChanged() override
   {
     if (!mCapturedTrackSource) {
       // This could happen during shutdown.
       return;
     }
 
     mPrincipal = mCapturedTrackSource->GetPrincipal();
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -56,16 +56,29 @@ class MediaStreamTrackSource : public ns
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
 
 public:
   class Sink : public SupportsWeakPtr<Sink>
   {
   public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaStreamTrackSource::Sink)
+
+    /**
+     * Must be constant throughout the Sink's lifetime.
+     *
+     * Return true to keep the MediaStreamTrackSource where this sink is
+     * registered alive.
+     * Return false to allow the source to stop.
+     *
+     * Typically MediaStreamTrack::Sink returns true and other Sinks
+     * (like HTMLMediaElement::StreamCaptureTrackSource) return false.
+     */
+    virtual bool KeepsSourceAlive() const = 0;
+
     virtual void PrincipalChanged() = 0;
     virtual void MutedChanged(bool aNewState) = 0;
   };
 
   MediaStreamTrackSource(nsIPrincipal* aPrincipal,
                          const nsString& aLabel)
     : mPrincipal(aPrincipal),
       mLabel(aLabel),
@@ -133,17 +146,18 @@ public:
 
   /**
    * Same for GetSettings (no-op).
    */
   virtual void
   GetSettings(dom::MediaTrackSettings& aResult) {};
 
   /**
-   * Called by the source interface when all registered sinks have unregistered.
+   * Called by the source interface when all registered sinks with
+   * KeepsSourceAlive() == true have unregistered.
    */
   virtual void Stop() = 0;
 
   /**
    * Called by each MediaStreamTrack clone on initialization.
    */
   void RegisterSink(Sink* aSink)
   {
@@ -162,28 +176,40 @@ public:
    * source (us) or destruction.
    */
   void UnregisterSink(Sink* aSink)
   {
     MOZ_ASSERT(NS_IsMainThread());
     while(mSinks.RemoveElement(nullptr)) {
       MOZ_ASSERT_UNREACHABLE("Sink was not explicitly removed");
     }
-    if (mSinks.RemoveElement(aSink) && mSinks.IsEmpty()) {
-      MOZ_ASSERT(!mStopped);
+    if (mSinks.RemoveElement(aSink) && !IsActive()) {
+      MOZ_ASSERT(!aSink->KeepsSourceAlive() || !mStopped,
+                 "When the last sink keeping the source alive is removed, "
+                 "we should still be live");
       Stop();
       mStopped = true;
     }
   }
 
 protected:
   virtual ~MediaStreamTrackSource()
   {
   }
 
+  bool IsActive()
+  {
+    for (const WeakPtr<Sink>& sink : mSinks) {
+      if (sink && sink->KeepsSourceAlive()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /**
    * Called by a sub class when the principal has changed.
    * Notifies all sinks.
    */
   void PrincipalChanged()
   {
     MOZ_ASSERT(NS_IsMainThread());
     nsTArray<WeakPtr<Sink>> sinks(mSinks);
@@ -390,17 +416,28 @@ public:
     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; }
 
   // Implementation of MediaStreamTrackSource::Sink
+
+  /**
+   * Keep the track source alive. This track and any clones are controlling the
+   * lifetime of the source by being registered as its sinks.
+   */
+  bool KeepsSourceAlive() const override
+  {
+    return true;
+  }
+
   void PrincipalChanged() override;
+
   /**
    * 4.3.1 Life-cycle and Media flow - Media flow
    * To set a track's muted state to newState, the User Agent MUST run the
    * following steps:
    *  1. Let track be the MediaStreamTrack in question.
    *  2. Set track's muted attribute to newState.
    *  3. If newState is true let eventName be mute, otherwise unmute.
    *  4. Fire a simple event named eventName on track.