Bug 1208316 - Implement MediaStream.active. r?jib, r?smaug draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 08 Sep 2016 13:44:49 +0200
changeset 432198 b08dec60a88fa371b0323e7bdbe147b9b92086ab
parent 431996 2c773b97167252cedcba0be0c7af9d4cab192ef5
child 432199 0c3317bc8410b2286f886802e662a648d8f433c5
push id34233
push userbmo:pehrson@telenordigital.com
push dateTue, 01 Nov 2016 13:21:40 +0000
reviewersjib, smaug
bugs1208316
milestone52.0a1
Bug 1208316 - Implement MediaStream.active. r?jib, r?smaug MozReview-Commit-ID: Fzk5vepqQ35
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/webidl/MediaStream.webidl
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -361,17 +361,18 @@ NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMe
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
                                MediaStreamTrackSourceGetter* aTrackSourceGetter)
   : mLogicalStreamStartTime(0), mWindow(aWindow),
     mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
     mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
-    mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false)
+    mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
+    mActive(false)
 {
   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));
@@ -613,23 +614,23 @@ DOMMediaStream::RemoveTrack(MediaStreamT
   // If the track comes from a TRACK_ANY input port (i.e., mOwnedPort), we need
   // to block it in the port. Doing this for a locked track is still OK as it
   // will first block the track, then destroy the port. Both cause the track to
   // end.
   // If the track has already ended, it's input port might be gone, so in those
   // cases blocking the underlying track should be avoided.
   if (!aTrack.Ended()) {
     BlockPlaybackTrack(toRemove);
+
+    bool removed = mTracks.RemoveElement(toRemove);
+    if (removed) {
+      NotifyTrackRemoved(&aTrack);
+    }
   }
 
-  DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
-  MOZ_ASSERT(removed);
-
-  NotifyTrackRemoved(&aTrack);
-
   LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
 }
 
 class ClonedStreamSourceGetter :
   public MediaStreamTrackSourceGetter
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -725,16 +726,22 @@ DOMMediaStream::CloneInternal(TrackForwa
                                                    TRACK_ANY, TRACK_ANY, 0, 0,
                                                    &tracksToBlock);
     }
   }
 
   return newStream.forget();
 }
 
+bool
+DOMMediaStream::Active() const
+{
+  return mActive;
+}
+
 MediaStreamTrack*
 DOMMediaStream::GetTrackById(const nsAString& aId) const
 {
   for (const RefPtr<TrackPort>& info : mTracks) {
     nsString id;
     info->GetTrack()->GetId(id);
     if (id == aId) {
       return info->GetTrack();
@@ -1175,16 +1182,38 @@ DOMMediaStream::OnTracksAvailable(OnTrac
 void
 DOMMediaStream::NotifyTracksCreated()
 {
   mTracksCreated = true;
   CheckTracksAvailable();
 }
 
 void
+DOMMediaStream::NotifyActive()
+{
+  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyActive(). ", this));
+
+  MOZ_ASSERT(mActive);
+  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
+    mTrackListeners[i]->NotifyActive();
+  }
+}
+
+void
+DOMMediaStream::NotifyInactive()
+{
+  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInactive(). ", this));
+
+  MOZ_ASSERT(!mActive);
+  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
+    mTrackListeners[i]->NotifyInactive();
+  }
+}
+
+void
 DOMMediaStream::CheckTracksAvailable()
 {
   if (!mTracksCreated) {
     return;
   }
   nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
   callbacks.SwapElements(mRunOnTracksAvailable);
 
@@ -1238,32 +1267,70 @@ DOMMediaStream::NotifyTrackAdded(const R
     RecomputePrincipal();
   }
 
   aTrack->AddPrincipalChangeObserver(this);
 
   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
     mTrackListeners[i]->NotifyTrackAdded(aTrack);
   }
+
+  if (mActive) {
+    return;
+  }
+
+  // Check if we became active.
+  bool active = false;
+  for (auto port : mTracks) {
+    if (!port->GetTrack()->Ended()) {
+      active = true;
+      break;
+    }
+  }
+
+  if (active) {
+    mActive = true;
+    NotifyActive();
+  }
 }
 
 void
 DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   aTrack->RemovePrincipalChangeObserver(this);
 
   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
     mTrackListeners[i]->NotifyTrackRemoved(aTrack);
+
   }
 
   // Don't call RecomputePrincipal here as the track may still exist in the
   // playback stream in the MediaStreamGraph. It will instead be called when the
   // track has been confirmed removed by the graph. See BlockPlaybackTrack().
+
+  if (!mActive) {
+    NS_ASSERTION(false, "Shouldn't remove a live track if already inactive");
+    return;
+  }
+
+  // Check if we became inactive.
+  bool active = false;
+  for (auto port : mTracks) {
+    if (!port->GetTrack()->Ended()) {
+      active = true;
+      break;
+    }
+  }
+
+  if (!active) {
+    mActive = false;
+    NotifyInactive();
+  }
 }
 
 nsresult
 DOMMediaStream::DispatchTrackEvent(const nsAString& aName,
                                    const RefPtr<MediaStreamTrack>& aTrack)
 {
   MOZ_ASSERT(aName == NS_LITERAL_STRING("addtrack"),
              "Only 'addtrack' is supported at this time");
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -221,28 +221,40 @@ class DOMMediaStream : public DOMEventTa
 public:
   typedef dom::MediaTrackConstraints MediaTrackConstraints;
 
   class TrackListener {
   public:
     virtual ~TrackListener() {}
 
     /**
-     * Called when the DOMMediaStream has a new track added, either by
-     * JS (addTrack()) or the source creating one.
+     * Called when the DOMMediaStream has a live track added, either by
+     * script (addTrack()) or the source creating one.
      */
     virtual void
     NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) {};
 
     /**
-     * Called when the DOMMediaStream removes a track, either by
-     * JS (removeTrack()) or the source ending it.
+     * Called when the DOMMediaStream removes a live track from playback, either
+     * by script (removeTrack(), track.stop()) or the source ending it.
      */
     virtual void
     NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) {};
+
+    /**
+     * Called when the DOMMediaStream has become active.
+     */
+    virtual void
+    NotifyActive() {};
+
+    /**
+     * Called when the DOMMediaStream has become inactive.
+     */
+    virtual void
+    NotifyInactive() {};
   };
 
   /**
    * TrackPort is a representation of a MediaStreamTrack-MediaInputPort pair
    * that make up a link between the Owned stream and the Playback stream.
    *
    * Semantically, the track is the identifier/key and the port the value of this
    * connection.
@@ -358,16 +370,18 @@ public:
   void GetTracks(nsTArray<RefPtr<MediaStreamTrack> >& aTracks) const;
   MediaStreamTrack* GetTrackById(const nsAString& aId) const;
   void AddTrack(MediaStreamTrack& aTrack);
   void RemoveTrack(MediaStreamTrack& aTrack);
 
   /** Identical to CloneInternal(TrackForwardingOption::EXPLICIT) */
   already_AddRefed<DOMMediaStream> Clone();
 
+  bool Active() const;
+
   IMPL_EVENT_HANDLER(addtrack)
 
   // NON-WebIDL
 
   /**
    * Option to provide to CloneInternal() of which tracks should be forwarded
    * from the source stream (`this`) to the returned stream clone.
    *
@@ -594,16 +608,22 @@ protected:
   void InitPlaybackStreamCommon(MediaStreamGraph* aGraph);
 
   void CheckTracksAvailable();
 
   // Called when MediaStreamGraph has finished an iteration where tracks were
   // created.
   void NotifyTracksCreated();
 
+  // Dispatches NotifyActive() to all registered track listeners.
+  void NotifyActive();
+
+  // Dispatches NotifyInactive() to all registered track listeners.
+  void NotifyInactive();
+
   // Dispatches NotifyTrackAdded() to all registered track listeners.
   void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack);
 
   // Dispatches NotifyTrackRemoved() to all registered track listeners.
   void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);
 
   // Dispatches "addtrack" or "removetrack".
   nsresult DispatchTrackEvent(const nsAString& aName,
@@ -694,16 +714,19 @@ protected:
   // Keep these alive while the stream is alive.
   nsTArray<nsCOMPtr<nsISupports>> mConsumersToKeepAlive;
 
   bool mNotifiedOfMediaStreamGraphShutdown;
 
   // The track listeners subscribe to changes in this stream's track set.
   nsTArray<TrackListener*> mTrackListeners;
 
+  // True if this stream has live tracks.
+  bool mActive;
+
 private:
   void NotifyPrincipalChanged();
   // Principal identifying who may access the collected contents of this stream.
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // Video principal is used by video element as access is requested to its
   // image data.
   nsCOMPtr<nsIPrincipal> mVideoPrincipal;
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -31,15 +31,13 @@ interface MediaStream : EventTarget {
     readonly    attribute DOMString    id;
     sequence<AudioStreamTrack> getAudioTracks ();
     sequence<VideoStreamTrack> getVideoTracks ();
     sequence<MediaStreamTrack> getTracks ();
     MediaStreamTrack?          getTrackById (DOMString trackId);
     void                       addTrack (MediaStreamTrack track);
     void                       removeTrack (MediaStreamTrack track);
     MediaStream                clone ();
-    // readonly    attribute boolean      active;
-    //             attribute EventHandler onactive;
-    //             attribute EventHandler oninactive;
+    readonly    attribute boolean      active;
                 attribute EventHandler onaddtrack;
     //             attribute EventHandler onremovetrack;
     readonly attribute double currentTime;
 };