Bug 1208316 - Punch a hole for media element captureStream to only go inactive as source ends. r?jib draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 15 Sep 2016 14:18:22 +0200
changeset 432202 d211e77ab48ea2f83614901ae464457aa9242f31
parent 432201 e1adfff81a6ffcd419c03054d2597df1a3b0792d
child 432203 4e50aac5cd66b7633bd70b7f4c4a8afee3f7003c
push id34233
push userbmo:pehrson@telenordigital.com
push dateTue, 01 Nov 2016 13:21:40 +0000
reviewersjib
bugs1208316
milestone52.0a1
Bug 1208316 - Punch a hole for media element captureStream to only go inactive as source ends. r?jib MozReview-Commit-ID: 3H0m3fYMw1Y
dom/html/HTMLMediaElement.cpp
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2521,16 +2521,17 @@ HTMLMediaElement::CaptureStreamInternal(
   if (!mOutputStreams.IsEmpty() &&
       aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
     return nullptr;
   }
 
   OutputMediaStream* out = mOutputStreams.AppendElement();
   MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this);
   out->mStream = DOMMediaStream::CreateTrackUnionStreamAsInput(window, aGraph, getter);
+  out->mStream->SetInactiveOnFinish();
   out->mFinishWhenEnded = aFinishWhenEnded;
   out->mCapturingAudioOnly = aCaptureAudio;
 
   if (aCaptureAudio) {
     if (mSrcStream) {
       // We don't support applying volume and mute to the captured stream, when
       // capturing a MediaStream.
       nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -47,16 +47,27 @@ using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::media;
 
 static LazyLogModule gMediaStreamLog("MediaStream");
 #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
 
 const TrackID TRACK_VIDEO_PRIMARY = 1;
 
+static bool
+ContainsLiveTracks(nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks)
+{
+  for (auto& port : aTracks) {
+    if (port->GetTrack()->ReadyState() == MediaStreamTrackState::Live) {
+      return true;
+    }
+  }
+
+  return false;
+}
 
 DOMMediaStream::TrackPort::TrackPort(MediaInputPort* aInputPort,
                                      MediaStreamTrack* aTrack,
                                      const InputPortOwnership aOwnership)
   : mInputPort(aInputPort)
   , mTrack(aTrack)
   , mOwnership(aOwnership)
 {
@@ -266,16 +277,26 @@ public:
 
   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
   {
     nsCOMPtr<nsIRunnable> runnable =
       NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
   }
 
+
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamGraphEvent event) override
+  {
+    if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+        NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinished));
+    }
+  }
+
 private:
   // These fields may only be accessed on the main thread
   DOMMediaStream* mStream;
 };
 
 class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer
 {
 public:
@@ -370,17 +391,17 @@ NS_INTERFACE_MAP_END_INHERITING(DOMMedia
 
 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
                                MediaStreamTrackSourceGetter* aTrackSourceGetter)
   : mLogicalStreamStartTime(0), mWindow(aWindow),
     mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
     mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
     mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
     mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
-    mActive(false)
+    mActive(false), mSetInactiveOnFinish(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));
@@ -801,22 +822,28 @@ void
 DOMMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
 {
   if (GetInputStream() && GetInputStream()->AsSourceStream()) {
     GetInputStream()->AsSourceStream()->RemoveDirectListener(aListener);
   }
 }
 
 bool
-DOMMediaStream::IsFinished()
+DOMMediaStream::IsFinished() const
 {
   return !mPlaybackStream || mPlaybackStream->IsFinished();
 }
 
 void
+DOMMediaStream::SetInactiveOnFinish()
+{
+  mSetInactiveOnFinish = true;
+}
+
+void
 DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph)
 {
   InitInputStreamCommon(aGraph->CreateSourceStream(), aGraph);
   InitOwnedStreamCommon(aGraph);
   InitPlaybackStreamCommon(aGraph);
 }
 
 void
@@ -1194,16 +1221,33 @@ DOMMediaStream::OnTracksAvailable(OnTrac
 void
 DOMMediaStream::NotifyTracksCreated()
 {
   mTracksCreated = true;
   CheckTracksAvailable();
 }
 
 void
+DOMMediaStream::NotifyFinished()
+{
+  if (!mSetInactiveOnFinish) {
+    return;
+  }
+
+  if (!mActive) {
+    // This can happen if the stream never became active.
+    return;
+  }
+
+  MOZ_ASSERT(!ContainsLiveTracks(mTracks));
+  mActive = false;
+  NotifyInactive();
+}
+
+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();
   }
@@ -1286,25 +1330,17 @@ DOMMediaStream::NotifyTrackAdded(const R
     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) {
+  if (ContainsLiveTracks(mTracks)) {
     mActive = true;
     NotifyActive();
   }
 }
 
 void
 DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
 {
@@ -1322,26 +1358,24 @@ DOMMediaStream::NotifyTrackRemoved(const
   // 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 (mSetInactiveOnFinish) {
+    // For compatibility with mozCaptureStream we in some cases do not go
+    // inactive until the playback stream finishes.
+    return;
   }
 
-  if (!active) {
+  // Check if we became inactive.
+  if (!ContainsLiveTracks(mTracks)) {
     mActive = false;
     NotifyInactive();
   }
 }
 
 nsresult
 DOMMediaStream::DispatchTrackEvent(const nsAString& aName,
                                    const RefPtr<MediaStreamTrack>& aTrack)
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -454,17 +454,26 @@ public:
    * queuing. Returns a bool to let us know if direct data will be delivered.
    */
   bool AddDirectListener(DirectMediaStreamListener *aListener);
   void RemoveDirectListener(DirectMediaStreamListener *aListener);
 
   virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
   virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
 
-  bool IsFinished();
+  /**
+   * Legacy method that returns true when the playback stream has finished.
+   */
+  bool IsFinished() const;
+
+  /**
+   * Becomes inactive only when the playback stream has finished.
+   */
+  void SetInactiveOnFinish();
+
   /**
    * Returns a principal indicating who may access this stream. The stream contents
    * can only be accessed by principals subsuming this principal.
    */
   nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
   /**
    * Returns a principal indicating who may access video data of this stream.
@@ -608,16 +617,19 @@ protected:
   void InitPlaybackStreamCommon(MediaStreamGraph* aGraph);
 
   void CheckTracksAvailable();
 
   // Called when MediaStreamGraph has finished an iteration where tracks were
   // created.
   void NotifyTracksCreated();
 
+  // Called when our playback stream has finished in the MediaStreamGraph.
+  void NotifyFinished();
+
   // 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);
@@ -723,16 +735,21 @@ protected:
   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;
 
+  // True if this stream only sets mActive to false when its playback stream
+  // finishes. This is a hack to maintain legacy functionality for playing a
+  // HTMLMediaElement::MozCaptureStream(). See bug 1302379.
+  bool mSetInactiveOnFinish;
+
 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;