Bug 1208378 - Distinguish track sinks on whether their presence allows a source to stop. r?jib draft
authorAndreas Pehrson <pehrsons@mozilla.com>
Fri, 10 Nov 2017 15:08:02 +0100
changeset 700721 3e4fb9dfff3497716a98a4e800d52ef53516eb2f
parent 699912 8391ffc17336eb323f48cf7bc6ed9c3682d1bc2c
child 700722 eac419c4e8d6be6a44360e2baebbe890b22f96be
push id89947
push userbmo:apehrson@mozilla.com
push dateMon, 20 Nov 2017 20:14:29 +0000
reviewersjib
bugs1208378
milestone59.0a1
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
@@ -3184,16 +3184,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.