Bug 1423241 - Remove OnTracksAvailableCallback from MediaManager. r=padenot
authorAndreas Pehrson <apehrson@mozilla.com>
Fri, 23 Nov 2018 15:02:29 +0000
changeset 504278 7fef8af7ad930efb067a7d6b75f646f9c809b0fd
parent 504277 ed7c9d7a635de4c0c14c76745a8998986af3d234
child 504279 3c5df9713725b2dd36b4af9eed890d70ed073d78
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 MediaManager. r=padenot Differential Revision: https://phabricator.services.mozilla.com/D12279
dom/media/MediaManager.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaManager.h"
 
 #include "AllocationHandle.h"
 #include "AudioDeviceInfo.h"
-#include "MediaStreamGraph.h"
+#include "MediaStreamGraphImpl.h"
 #include "MediaTimer.h"
 #include "mozilla/dom/MediaStreamTrack.h"
 #include "mozilla/dom/MediaDeviceInfo.h"
 #include "MediaStreamListener.h"
 #include "nsArray.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsHashPropertyBag.h"
@@ -1146,51 +1146,86 @@ class GetUserMediaStreamRunnable : publi
         mWindowListener(aWindowListener),
         mSourceListener(aSourceListener),
         mPrincipalInfo(aPrincipalInfo),
         mPeerIdentity(aPeerIdentity),
         mManager(MediaManager::GetInstance()) {}
 
   ~GetUserMediaStreamRunnable() {}
 
-  class TracksAvailableCallback : public OnTracksAvailableCallback {
+  class TracksCreatedListener : public MediaStreamTrackListener {
    public:
-    TracksAvailableCallback(
+    TracksCreatedListener(
         MediaManager* aManager,
         const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>&
             aSuccess,
-        const RefPtr<GetUserMediaWindowListener>& aWindowListener,
-        DOMMediaStream* aStream)
+        GetUserMediaWindowListener* aWindowListener, DOMMediaStream* aStream,
+        MediaStreamTrack* aTrack)
         : mWindowListener(aWindowListener),
           mOnSuccess(aSuccess),
           mManager(aManager),
-          mStream(aStream) {}
-    void NotifyTracksAvailable(DOMMediaStream* aStream) override {
-      // We're on the main thread, so no worries here.
-      if (!mManager->IsWindowListenerStillActive(mWindowListener)) {
+          mGraph(aTrack->GraphImpl()),
+          mStream(new nsMainThreadPtrHolder<DOMMediaStream>(
+              "TracksCreatedListener::mStream", aStream)),
+          mTrack(new nsMainThreadPtrHolder<MediaStreamTrack>(
+              "TracksCreatedListener::mTrack", aTrack)) {}
+    void NotifyOutput(MediaStreamGraph* aGraph,
+                      StreamTime aCurrentTrackTime) override {
+      // It's enough to know that one of the tracks have output, as both tracks
+      // are guaranteed to be created in the graph at this point.
+
+      if (mDispatchedTracksCreated) {
         return;
       }
-
-      // This is safe since we're on main-thread, and the windowlist can only
-      // be invalidated from the main-thread (see OnNavigation)
-      LOG(("Returning success for getUserMedia()"));
-      CallOnSuccess(mOnSuccess, *aStream);
+      mDispatchedTracksCreated = true;
+      nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+          "TracksCreatedListener::NotifyOutput Notifier",
+          [self = RefPtr<TracksCreatedListener>(this), this]() {
+            mTrack->RemoveListener(this);
+
+            if (!mManager->IsWindowListenerStillActive(mWindowListener)) {
+              return;
+            }
+
+            // This is safe since we're on main-thread, and the windowlist can
+            // only be invalidated from the main-thread (see OnNavigation)
+            LOG(("Returning success for getUserMedia()"));
+            CallOnSuccess(mOnSuccess, *mStream);
+          });
+      // DispatchToMainThreadAfterStreamStateUpdate will make the runnable run
+      // in stable state. But since the runnable runs JS we need to make a
+      // double dispatch.
+      mGraph->DispatchToMainThreadAfterStreamStateUpdate(NS_NewRunnableFunction(
+          "TracksCreatedListener::NotifyOutput Stable State Notifier",
+          [graph = mGraph, r = std::move(r)]() mutable {
+            graph->Dispatch(r.forget());
+          }));
     }
-    RefPtr<GetUserMediaWindowListener> mWindowListener;
-    nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback> mOnSuccess;
-    RefPtr<MediaManager> mManager;
-    // Keep the DOMMediaStream alive until the NotifyTracksAvailable callback
-    // has fired, otherwise we might immediately destroy the DOMMediaStream and
+    void NotifyRemoved() override {
+      mGraph->Dispatch(NS_NewRunnableFunction(
+          "TracksCreatedListener::NotifyRemoved CycleBreaker",
+          [self = RefPtr<TracksCreatedListener>(this)]() {
+            self->mTrack->RemoveListener(self);
+          }));
+    }
+    const RefPtr<GetUserMediaWindowListener> mWindowListener;
+    const nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback>
+        mOnSuccess;
+    const RefPtr<MediaManager> mManager;
+    const RefPtr<MediaStreamGraphImpl> mGraph;
+    // Keep the DOMMediaStream alive until the success callback has been called,
+    // otherwise we might immediately destroy the DOMMediaStream and
     // shut down the underlying MediaStream prematurely.
-    // This creates a cycle which is broken when NotifyTracksAvailable
-    // is fired (which will happen unless the browser shuts down,
-    // since we only add this callback when we've successfully appended
-    // the desired tracks in the MediaStreamGraph) or when
-    // DOMMediaStream::NotifyMediaStreamGraphShutdown is called.
-    RefPtr<DOMMediaStream> mStream;
+    // This creates a cycle which is broken when we're destroyed, i.e., either
+    // when we've called the success callback and thus removed the listener from
+    // the graph, or on graph shutdown.
+    nsMainThreadPtrHandle<DOMMediaStream> mStream;
+    nsMainThreadPtrHandle<MediaStreamTrack> mTrack;
+    // Graph thread only.
+    bool mDispatchedTracksCreated = false;
   };
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(NS_IsMainThread());
     LOG(("GetUserMediaStreamRunnable::Run()"));
     nsGlobalWindowInner* globalWindow =
         nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
@@ -1205,36 +1240,34 @@ class GetUserMediaStreamRunnable : publi
     }
 
     MediaStreamGraph::GraphDriverType graphDriverType =
         mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER
                      : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
     MediaStreamGraph* msg = MediaStreamGraph::GetInstance(
         graphDriverType, window, MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
 
-    nsMainThreadPtrHandle<DOMMediaStream> domStream;
+    RefPtr<DOMMediaStream> domStream;
     RefPtr<SourceMediaStream> stream;
     // AudioCapture is a special case, here, in the sense that we're not really
     // using the audio source and the SourceMediaStream, which acts as
     // placeholders. We re-route a number of stream internaly in the MSG and mix
     // them down instead.
     if (mAudioDevice &&
         mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) {
       NS_WARNING(
           "MediaCaptureWindowState doesn't handle "
           "MediaSourceEnum::AudioCapture. This must be fixed with UX "
           "before shipping.");
       // It should be possible to pipe the capture stream to anything. CORS is
       // not a problem here, we got explicit user content.
       nsCOMPtr<nsIPrincipal> principal =
           window->GetExtantDoc()->NodePrincipal();
-      domStream = new nsMainThreadPtrHolder<DOMMediaStream>(
-          "GetUserMediaStreamRunnable::AudioCaptureDOMStreamMainThreadHolder",
-          DOMMediaStream::CreateAudioCaptureStreamAsInput(window, principal,
-                                                          msg));
+      domStream = DOMMediaStream::CreateAudioCaptureStreamAsInput(
+          window, principal, msg);
 
       stream = msg->CreateSourceStream();  // Placeholder
       msg->RegisterCaptureStreamForWindow(
           mWindowID, domStream->GetInputStream()->AsProcessedStream());
       window->SetAudioCapture(true);
     } else {
       class LocalTrackSource : public MediaStreamTrackSource {
        public:
@@ -1356,19 +1389,17 @@ class GetUserMediaStreamRunnable : publi
             window->GetExtantDoc()->NodePrincipal());
       } else {
         principal = window->GetExtantDoc()->NodePrincipal();
       }
 
       // Normal case, connect the source stream to the track union stream to
       // avoid us blocking. Pass a simple TrackSourceGetter for potential
       // fake tracks. Apart from them gUM never adds tracks dynamically.
-      domStream = new nsMainThreadPtrHolder<DOMMediaStream>(
-          "GetUserMediaStreamRunnable::DOMMediaStreamMainThreadHolder",
-          DOMMediaStream::CreateSourceStreamAsInput(window, msg));
+      domStream = DOMMediaStream::CreateSourceStreamAsInput(window, msg);
       stream = domStream->GetInputStream()->AsSourceStream();
 
       if (mAudioDevice) {
         nsString audioDeviceName;
         mAudioDevice->GetName(audioDeviceName);
         const MediaSourceEnum source = mAudioDevice->GetMediaSource();
         RefPtr<MediaStreamTrackSource> audioSource =
             new LocalTrackSource(principal, audioDeviceName, mSourceListener,
@@ -1408,40 +1439,36 @@ class GetUserMediaStreamRunnable : publi
     }
 
     // Activate our source listener. We'll call Start() on the source when we
     // get a callback that the MediaStream has started consuming. The listener
     // is freed when the page is invalidated (on navigation or close).
     mWindowListener->Activate(mSourceListener, stream, mAudioDevice,
                               mVideoDevice);
 
-    // Note: includes JS callbacks; must be released on MainThread
-    typedef Refcountable<UniquePtr<TracksAvailableCallback>> Callback;
-    nsMainThreadPtrHandle<Callback> callback(new nsMainThreadPtrHolder<
-                                             Callback>(
-        "GetUserMediaStreamRunnable::TracksAvailableCallbackMainThreadHolder",
-        MakeAndAddRef<Callback>(new TracksAvailableCallback(
-            mManager, mOnSuccess, mWindowListener, domStream))));
+    nsTArray<RefPtr<MediaStreamTrack>> tracks(2);
+    domStream->GetTracks(tracks);
+    RefPtr<MediaStreamTrack> track = tracks[0];
+    auto tracksCreatedListener = MakeRefPtr<TracksCreatedListener>(
+        mManager, mOnSuccess, mWindowListener, domStream, track);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of domStream through the lambda to the nested chrome
     // notification lambda to ensure it's kept alive until that lambda runs or
     // is discarded.
     mSourceListener->InitializeAsync()->Then(
         GetMainThreadSerialEventTarget(), __func__,
-        [manager = mManager, domStream, callback,
-         windowListener = mWindowListener]() {
+        [manager = mManager, windowListener = mWindowListener, track,
+         tracksCreatedListener]() {
           LOG(
               ("GetUserMediaStreamRunnable::Run: starting success callback "
                "following InitializeAsync()"));
           // Initiating and starting devices succeeded.
-          // onTracksAvailableCallback must be added to domStream on main
-          // thread.
-          domStream->OnTracksAvailable(callback->release());
+          track->AddListener(tracksCreatedListener);
           windowListener->ChromeAffectingStateChanged();
           manager->SendPendingGUMRequest();
         },
         [manager = mManager, windowID = mWindowID,
          onFailure =
              std::move(mOnFailure)](const RefPtr<MediaMgrError>& error) {
           LOG(
               ("GetUserMediaStreamRunnable::Run: starting failure callback "