Bug 1423241 - Remove OnTracksAvailableCallback from MediaRecorder. r=padenot
authorAndreas Pehrson <apehrson@mozilla.com>
Fri, 23 Nov 2018 15:02:35 +0000
changeset 504279 3c5df9713725b2dd36b4af9eed890d70ed073d78
parent 504278 7fef8af7ad930efb067a7d6b75f646f9c809b0fd
child 504280 d4767402cbb352274a930b0c800626c5e34b49ae
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1423241
milestone65.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 1423241 - Remove OnTracksAvailableCallback from MediaRecorder. r=padenot Differential Revision: https://phabricator.services.mozilla.com/D12280
dom/media/MediaRecorder.cpp
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -343,28 +343,16 @@ class MediaRecorder::Session : public Pr
 
       return NS_OK;
     }
 
    private:
     RefPtr<Session> mSession;
   };
 
-  // To ensure that MediaRecorder has tracks to record.
-  class TracksAvailableCallback : public OnTracksAvailableCallback {
-   public:
-    explicit TracksAvailableCallback(Session* aSession) : mSession(aSession) {}
-
-    virtual void NotifyTracksAvailable(DOMMediaStream* aStream) {
-      mSession->MediaStreamReady(aStream);
-    }
-
-   private:
-    RefPtr<Session> mSession;
-  };
   // Main thread task.
   // To delete RecordingSession object.
   class DestroyRunnable : public Runnable {
    public:
     explicit DestroyRunnable(Session* aSession)
         : Runnable("dom::MediaRecorder::Session::DestroyRunnable"),
           mSession(aSession) {}
 
@@ -470,21 +458,21 @@ class MediaRecorder::Session : public Pr
    protected:
     RefPtr<TaskQueue> mEncoderThread;
     RefPtr<Session> mSession;
   };
 
   friend class EncoderErrorNotifierRunnable;
   friend class PushBlobRunnable;
   friend class DestroyRunnable;
-  friend class TracksAvailableCallback;
 
  public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
       : mRecorder(aRecorder),
+        mMediaStreamReady(false),
         mTimeSlice(aTimeSlice),
         mRunningState(RunningState::Idling) {
     MOZ_ASSERT(NS_IsMainThread());
 
     mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
                                       MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
@@ -496,20 +484,32 @@ class MediaRecorder::Session : public Pr
       DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR);
     }
   }
 
   void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override {
     LOG(LogLevel::Warning,
         ("Session.NotifyTrackAdded %p Raising error due to track set change",
          this));
-    DoSessionEndTask(NS_ERROR_ABORT);
+    if (mMediaStreamReady) {
+      DoSessionEndTask(NS_ERROR_ABORT);
+    }
+
+    NS_DispatchToMainThread(
+        NewRunnableMethod("MediaRecorder::Session::MediaStreamReady", this,
+                          &Session::MediaStreamReady));
+    return;
   }
 
   void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override {
+    if (!mMediaStreamReady) {
+      // We haven't chosen the track set to record yet.
+      return;
+    }
+
     if (aTrack->Ended()) {
       // TrackEncoder will pickup tracks that end itself.
       return;
     }
 
     MOZ_ASSERT(mEncoder);
     if (mEncoder) {
       mEncoder->RemoveMediaStreamTrack(aTrack);
@@ -526,19 +526,24 @@ class MediaRecorder::Session : public Pr
     MOZ_ASSERT(NS_IsMainThread());
 
     DOMMediaStream* domStream = mRecorder->Stream();
     if (domStream) {
       // The callback reports back when tracks are available and can be
       // attached to MediaEncoder. This allows `recorder.start()` before any
       // tracks are available. We have supported this historically and have
       // mochitests assuming this behavior.
-      TracksAvailableCallback* tracksAvailableCallback =
-          new TracksAvailableCallback(this);
-      domStream->OnTracksAvailable(tracksAvailableCallback);
+      mMediaStream = domStream;
+      mMediaStream->RegisterTrackListener(this);
+      nsTArray<RefPtr<MediaStreamTrack>> tracks(2);
+      mMediaStream->GetTracks(tracks);
+      for (const auto& track : tracks) {
+        // Notify of existing tracks, as the stream doesn't do this by itself.
+        NotifyTrackAdded(track);
+      }
       return;
     }
 
     if (mRecorder->mAudioNode) {
       // Check that we may access the audio node's content.
       if (!AudioNodePrincipalSubsumes()) {
         LOG(LogLevel::Warning,
             ("Session.Start AudioNode principal check failed"));
@@ -699,29 +704,33 @@ class MediaRecorder::Session : public Pr
       }
     } else if (aDestroyRunnable) {
       if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
       }
     }
   }
 
-  void MediaStreamReady(DOMMediaStream* aStream) {
-    MOZ_RELEASE_ASSERT(aStream);
+  void MediaStreamReady() {
+    if (!mMediaStream) {
+      // Already shut down. This can happen because MediaStreamReady is async.
+      return;
+    }
+
+    if (mMediaStreamReady) {
+      return;
+    }
 
     if (!mRunningState.isOk() ||
         mRunningState.unwrap() != RunningState::Idling) {
       return;
     }
 
-    mMediaStream = aStream;
-    aStream->RegisterTrackListener(this);
-
     nsTArray<RefPtr<mozilla::dom::MediaStreamTrack>> tracks;
-    aStream->GetTracks(tracks);
+    mMediaStream->GetTracks(tracks);
     uint8_t trackTypes = 0;
     int32_t audioTracks = 0;
     int32_t videoTracks = 0;
     for (auto& track : tracks) {
       if (track->Ended()) {
         continue;
       }
 
@@ -733,44 +742,49 @@ class MediaRecorder::Session : public Pr
       } else if (track->AsVideoStreamTrack()) {
         ++videoTracks;
         trackTypes |= ContainerWriter::CREATE_VIDEO_TRACK;
       } else {
         MOZ_CRASH("Unexpected track type");
       }
     }
 
+    if (trackTypes == 0) {
+      MOZ_ASSERT(audioTracks == 0);
+      MOZ_ASSERT(videoTracks == 0);
+      return;
+    }
+
+    mMediaStreamReady = true;
+
     if (audioTracks > 1 || videoTracks > 1) {
       // When MediaRecorder supports multiple tracks, we should set up a single
       // MediaInputPort from the input stream, and let main thread check
       // track principals async later.
       nsPIDOMWindowInner* window = mRecorder->GetParentObject();
       nsIDocument* document = window ? window->GetExtantDoc() : nullptr;
       nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                       NS_LITERAL_CSTRING("Media"), document,
                                       nsContentUtils::eDOM_PROPERTIES,
                                       "MediaRecorderMultiTracksNotSupported");
       DoSessionEndTask(NS_ERROR_ABORT);
       return;
     }
 
-    NS_ASSERTION(trackTypes != 0,
-                 "TracksAvailableCallback without any tracks available");
-
     // Check that we may access the tracks' content.
     if (!MediaStreamTracksPrincipalSubsumes()) {
-      LOG(LogLevel::Warning, ("Session.NotifyTracksAvailable MediaStreamTracks "
+      LOG(LogLevel::Warning, ("Session.MediaTracksReady MediaStreamTracks "
                               "principal check failed"));
       DoSessionEndTask(NS_ERROR_DOM_SECURITY_ERR);
       return;
     }
 
     LOG(LogLevel::Debug,
-        ("Session.NotifyTracksAvailable track type = (%d)", trackTypes));
-    InitEncoder(trackTypes, aStream->GraphRate());
+        ("Session.MediaTracksReady track type = (%d)", trackTypes));
+    InitEncoder(trackTypes, mMediaStream->GraphRate());
   }
 
   void ConnectMediaStreamTrack(MediaStreamTrack& aTrack) {
     for (auto& track : mMediaStreamTracks) {
       if (track->AsAudioStreamTrack() && aTrack.AsAudioStreamTrack()) {
         // We only allow one audio track. See bug 1276928.
         return;
       }
@@ -1158,16 +1172,19 @@ class MediaRecorder::Session : public Pr
 
   // Hold reference to MediaRecorder that ensure MediaRecorder is alive
   // if there is an active session. Access ONLY on main thread.
   RefPtr<MediaRecorder> mRecorder;
 
   // Stream currently recorded.
   RefPtr<DOMMediaStream> mMediaStream;
 
+  // True after we have decided on the track set to use for the recording.
+  bool mMediaStreamReady;
+
   // Tracks currently recorded. This should be a subset of mMediaStream's track
   // set.
   nsTArray<RefPtr<MediaStreamTrack>> mMediaStreamTracks;
 
   // Runnable thread for reading data from MediaEncoder.
   RefPtr<TaskQueue> mEncoderThread;
   // MediaEncoder pipeline.
   RefPtr<MediaEncoder> mEncoder;