Bug 1423241 - Expose MSG's GraphTime through main-thread-Watchable and move media element to it. r=padenot
authorAndreas Pehrson <apehrson@mozilla.com>
Fri, 23 Nov 2018 15:01:13 +0000
changeset 507046 430c57c4fb18674399b10c2d40d18f2bdca91ce3
parent 507045 82afe83c9c4f59c73bdbbb62c4ab85f8ffb9906d
child 507047 720f409b83f27d5c92c9194157b6f7ac65068361
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [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 - Expose MSG's GraphTime through main-thread-Watchable and move media element to it. r=padenot Differential Revision: https://phabricator.services.mozilla.com/D9101
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/MediaStreamGraphImpl.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2962,20 +2962,21 @@ HTMLMediaElement::Seeking() const
 {
   return mDecoder && mDecoder->IsSeeking();
 }
 
 double
 HTMLMediaElement::CurrentTime() const
 {
   if (MediaStream* stream = GetSrcMediaStream()) {
-    if (mSrcStreamPausedCurrentTime >= 0) {
-      return mSrcStreamPausedCurrentTime;
-    }
-    return stream->StreamTimeToSeconds(stream->GetCurrentTime());
+    MediaStreamGraph* graph = stream->Graph();
+    GraphTime currentTime = mSrcStreamPausedGraphTime == GRAPH_TIME_MAX ?
+      graph->CurrentTime() - mSrcStreamGraphTimeOffset :
+      mSrcStreamPausedGraphTime;
+    return stream->StreamTimeToSeconds(currentTime);
   }
 
   if (mDefaultPlaybackStartPosition == 0.0 && mDecoder) {
     return mDecoder->GetCurrentTime();
   }
 
   return mDefaultPlaybackStartPosition;
 }
@@ -5179,70 +5180,16 @@ HTMLMediaElement::FinishDecoderSetup(Med
     if (!mPausedForInactiveDocumentOrChannel) {
       mDecoder->Play();
     }
   }
 
   return NS_OK;
 }
 
-class HTMLMediaElement::StreamListener : public MediaStreamListener
-{
-public:
-  StreamListener(HTMLMediaElement* aElement, const char* aName)
-    : mElement(aElement)
-    , mMutex(aName)
-    , mPendingNotifyOutput(false)
-  {
-  }
-
-  void Forget()
-  {
-    mElement = nullptr;
-  }
-
-  // Main thread
-
-  void DoNotifyOutput()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    {
-      MutexAutoLock lock(mMutex);
-      mPendingNotifyOutput = false;
-    }
-    if (mElement && mElement->ReadyState() >= HAVE_CURRENT_DATA) {
-      mElement->FireTimeUpdate(true);
-    }
-  }
-
-  // This notification runs on the media graph thread so we need to
-  // dispatch events to the main thread.
-  virtual void NotifyOutput(MediaStreamGraph* aGraph,
-                            GraphTime aCurrentTime) override
-  {
-    MutexAutoLock lock(mMutex);
-    if (mPendingNotifyOutput) {
-      return;
-    }
-    mPendingNotifyOutput = true;
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(
-      NewRunnableMethod("dom::HTMLMediaElement::StreamListener::DoNotifyOutput",
-                        this,
-                        &StreamListener::DoNotifyOutput));
-  }
-
-private:
-  // These fields may only be accessed on the main thread
-  HTMLMediaElement* mElement;
-
-  // mMutex protects the fields below; they can be accessed on any thread
-  Mutex mMutex;
-  bool mPendingNotifyOutput;
-};
-
 class HTMLMediaElement::MediaStreamTracksAvailableCallback
   : public OnTracksAvailableCallback
 {
 public:
   explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement)
     : OnTracksAvailableCallback()
     , mElement(aElement)
   {
@@ -5306,19 +5253,16 @@ public:
     if (mElement->IsPlaybackEnded()) {
       return;
     }
     LOG(LogLevel::Debug,
         ("%p, mSrcStream %p became inactive",
          mElement.get(),
          mElement->mSrcStream.get()));
     MOZ_ASSERT(!mElement->mSrcStream->Active());
-    if (mElement->mMediaStreamListener) {
-      mElement->mMediaStreamListener->Forget();
-    }
     mElement->PlaybackEnded();
     mElement->UpdateReadyStateInternal();
   }
 
 protected:
   const WeakPtr<HTMLMediaElement> mElement;
 };
 
@@ -5327,36 +5271,37 @@ HTMLMediaElement::UpdateSrcMediaStreamPl
 {
   if (!mSrcStream) {
     return;
   }
   // We might be in cycle collection with mSrcStream->GetPlaybackStream()
   // already returning null due to unlinking.
 
   MediaStream* stream = GetSrcMediaStream();
+  MediaStreamGraph* graph = stream ? stream->Graph() : nullptr;
   bool shouldPlay = !(aFlags & REMOVING_SRC_STREAM) && !mPaused &&
                     !mPausedForInactiveDocumentOrChannel && stream;
   if (shouldPlay == mSrcStreamIsPlaying) {
     return;
   }
   mSrcStreamIsPlaying = shouldPlay;
 
   LOG(LogLevel::Debug,
       ("MediaElement %p %s playback of DOMMediaStream %p",
        this,
        shouldPlay ? "Setting up" : "Removing",
        mSrcStream.get()));
 
   if (shouldPlay) {
     mSrcStreamPlaybackEnded = false;
-    mSrcStreamPausedCurrentTime = -1;
-
-    mMediaStreamListener =
-      new StreamListener(this, "HTMLMediaElement::mMediaStreamListener");
-    stream->AddListener(mMediaStreamListener);
+    mSrcStreamGraphTimeOffset =
+      graph->CurrentTime() - mSrcStreamPausedGraphTime;
+    mSrcStreamPausedGraphTime = GRAPH_TIME_MAX;
+
+    mWatchManager.Watch(graph->CurrentTime(), &HTMLMediaElement::UpdateSrcStreamTime);
 
     stream->AddAudioOutput(this);
     SetVolumeInternal();
     if (mSink.second()) {
       NS_WARNING("setSinkId() when playing a MediaStream is not supported yet and will be ignored");
     }
 
     VideoFrameContainer* container = GetVideoFrameContainer();
@@ -5365,50 +5310,64 @@ HTMLMediaElement::UpdateSrcMediaStreamPl
     }
 
     SetCapturedOutputStreamsEnabled(true); // Unmute
     // If the input is a media stream, we don't check its data and always regard
     // it as audible when it's playing.
     SetAudibleState(true);
   } else {
     if (stream) {
-      mSrcStreamPausedCurrentTime = CurrentTime();
-
-      stream->RemoveListener(mMediaStreamListener);
+      mSrcStreamPausedGraphTime = graph->CurrentTime() - mSrcStreamGraphTimeOffset;
+
+      mWatchManager.Unwatch(graph->CurrentTime(), &HTMLMediaElement::UpdateSrcStreamTime);
 
       stream->RemoveAudioOutput(this);
       VideoFrameContainer* container = GetVideoFrameContainer();
       if (mSelectedVideoStreamTrack && container) {
         mSelectedVideoStreamTrack->RemoveVideoOutput(container);
       }
 
       SetCapturedOutputStreamsEnabled(false); // Mute
     }
     // If stream is null, then DOMMediaStream::Destroy must have been
     // called and that will remove all listeners/outputs.
-
-    mMediaStreamListener->Forget();
-    mMediaStreamListener = nullptr;
-  }
+  }
+}
+
+void
+HTMLMediaElement::UpdateSrcStreamTime()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mSrcStreamPausedGraphTime == GRAPH_TIME_MAX);
+  MOZ_ASSERT(!mPaused);
+  MOZ_ASSERT(!mSrcStreamPlaybackEnded);
+  FireTimeUpdate(true);
 }
 
 void
 HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
 {
-  NS_ASSERTION(!mSrcStream && !mMediaStreamListener &&
-                 !mVideoFrameListener,
+  NS_ASSERTION(!mSrcStream && !mVideoFrameListener,
                "Should have been ended already");
 
   mSrcStream = aStream;
 
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return;
   }
 
+  mSrcStreamPausedGraphTime = 0;
+  if (MediaStream* stream = GetSrcMediaStream()) {
+    if (MediaStreamGraph* graph = stream->Graph()) {
+      // The current graph time will represent 0 for this media element.
+      mSrcStreamPausedGraphTime = graph->CurrentTime();
+    }
+  }
+
   UpdateSrcMediaStreamPlaying();
 
   // If we pause this media element, track changes in the underlying stream
   // will continue to fire events at this element and alter its track list.
   // That's simpler than delaying the events, but probably confusing...
   nsTArray<RefPtr<MediaStreamTrack>> tracks;
   mSrcStream->GetTracks(tracks);
   for (const RefPtr<MediaStreamTrack>& track : tracks) {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -20,17 +20,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/TextTrackManager.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
 #include "PrincipalChangeObserver.h"
 #include "nsStubMutationObserver.h"
-#include "MediaSegment.h" // for PrincipalHandle
+#include "MediaSegment.h" // for PrincipalHandle, GraphTime
 
 // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
@@ -843,17 +843,16 @@ protected:
   virtual ~HTMLMediaElement();
 
   class AudioChannelAgentCallback;
   class ChannelLoader;
   class ErrorSink;
   class MediaLoadListener;
   class MediaStreamTracksAvailableCallback;
   class MediaStreamTrackListener;
-  class StreamListener;
   class VideoFrameListener;
   class ShutdownObserver;
 
   MediaDecoderOwner::NextFrameStatus NextFrameStatus();
 
   void SetDecoder(MediaDecoder* aDecoder);
 
   // Holds references to the DOM wrappers for the MediaStreams that we're
@@ -931,16 +930,22 @@ protected:
   void EndSrcMediaStreamPlayback();
   /**
    * Ensure we're playing mSrcStream if and only if we're not paused.
    */
   enum { REMOVING_SRC_STREAM = 0x1 };
   void UpdateSrcMediaStreamPlaying(uint32_t aFlags = 0);
 
   /**
+   * mSrcStream's graph's CurrentTime() has been updated. It might be time to
+   * fire "timeupdate".
+   */
+  void UpdateSrcStreamTime();
+
+  /**
    * Called by our DOMMediaStream::TrackListener when a new MediaStreamTrack has
    * been added to the playback stream of |mSrcStream|.
    */
   void NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aTrack);
 
   /**
    * Called by our DOMMediaStream::TrackListener when a MediaStreamTrack in
    * |mSrcStream|'s playback stream has ended.
@@ -1425,34 +1430,35 @@ protected:
   // Holds a reference to the DOM wrapper for the MediaStream that we're
   // actually playing.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<DOMMediaStream> mSrcStream;
 
   // True once mSrcStream's initial set of tracks are known.
   bool mSrcStreamTracksAvailable = false;
 
-  // If non-negative, the time we should return for currentTime while playing
-  // mSrcStream.
-  double mSrcStreamPausedCurrentTime = -1;
+  // If different from GRAPH_TIME_MAX, the time we should return for
+  // currentTime while playing mSrcStream.
+  GraphTime mSrcStreamPausedGraphTime = GRAPH_TIME_MAX;
+
+  // The offset in GraphTime that this media element started playing the
+  // playback stream of mSrcStream.
+  GraphTime mSrcStreamGraphTimeOffset = 0;
 
   // True once PlaybackEnded() is called and we're playing a MediaStream.
   // Reset to false if we start playing mSrcStream again.
   bool mSrcStreamPlaybackEnded = false;
 
   // Holds a reference to the stream connecting this stream to the capture sink.
   RefPtr<MediaInputPort> mCaptureStreamPort;
 
   // Holds references to the DOM wrappers for the MediaStreams that we're
   // writing to.
   nsTArray<OutputMediaStream> mOutputStreams;
 
-  // Holds a reference to the MediaStreamListener attached to mSrcStream's
-  // playback stream.
-  RefPtr<StreamListener> mMediaStreamListener;
   // Holds a reference to the size-getting track listener attached to
   // mSelectedVideoStreamTrack.
   RefPtr<VideoFrameListener> mVideoFrameListener;
   // The currently selected video stream track.
   RefPtr<VideoStreamTrack> mSelectedVideoStreamTrack;
 
   const RefPtr<ShutdownObserver> mShutdownObserver;
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1115,16 +1115,17 @@ void MediaStreamGraphImpl::PrepareUpdate
       StreamUpdate* update = mStreamUpdates.AppendElement();
       update->mStream = stream;
       // No blocking to worry about here, since we've passed
       // UpdateCurrentTimeForStreams.
       update->mNextMainThreadCurrentTime =
           stream->GraphTimeToStreamTime(mProcessedTime);
       update->mNextMainThreadFinished = stream->mNotifiedFinished;
     }
+    mNextMainThreadGraphTime = mProcessedTime;
     if (!mPendingUpdateRunnables.IsEmpty()) {
       mUpdateRunnables.AppendElements(std::move(mPendingUpdateRunnables));
     }
   }
 
   // If this is the final update, then a stable state event will soon be
   // posted just before this thread finishes, and so there is no need to also
   // post here.
@@ -1658,16 +1659,18 @@ void MediaStreamGraphImpl::RunInStableSt
     for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
       StreamUpdate* update = &mStreamUpdates[i];
       if (update->mStream) {
         ApplyStreamUpdate(update);
       }
     }
     mStreamUpdates.Clear();
 
+    mMainThreadGraphTime = mNextMainThreadGraphTime;
+
     if (mCurrentTaskMessageQueue.IsEmpty()) {
       if (LifecycleStateRef() == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP &&
           IsEmpty()) {
         // Complete shutdown. First, ensure that this graph is no longer used.
         // A new graph graph will be created if one is needed.
         // Asynchronously clean up old graph. We don't want to do this
         // synchronously because it spins the event loop waiting for threads
         // to shut down, and we don't want to do that in a stable state handler.
@@ -3329,17 +3332,18 @@ MediaStreamGraphImpl::MediaStreamGraphIm
       mAbstractMainThread(aMainThread),
       mSelfRef(this),
       mOutputChannels(std::min<uint32_t>(8, CubebUtils::MaxNumberOfChannels())),
       mGlobalVolume(CubebUtils::GetVolumeScale())
 #ifdef DEBUG
       ,
       mCanRunMessagesSynchronously(false)
 #endif
-{
+      ,
+      mMainThreadGraphTime(0, "MediaStreamGraphImpl::mMainThreadGraphTime") {
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
       // Always start with zero input channels.
       mDriver = new AudioCallbackDriver(this, 0);
     } else {
       mDriver = new SystemClockDriver(this);
     }
 
@@ -4000,9 +4004,14 @@ already_AddRefed<MediaInputPort> MediaSt
 
 void MediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(
     already_AddRefed<nsIRunnable> aRunnable) {
   AssertOnGraphThreadOrNotRunning();
   *mPendingUpdateRunnables.AppendElement() =
       AbstractMainThread()->CreateDirectTaskDrainer(std::move(aRunnable));
 }
 
+Watchable<mozilla::GraphTime>& MediaStreamGraphImpl::CurrentTime() {
+  MOZ_ASSERT(NS_IsMainThread());
+  return mMainThreadGraphTime;
+}
+
 }  // namespace mozilla
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -9,16 +9,17 @@
 #include "AudioStream.h"
 #include "MainThreadUtils.h"
 #include "MediaStreamTypes.h"
 #include "StreamTracks.h"
 #include "VideoSegment.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/StateWatching.h"
 #include "mozilla/TaskQueue.h"
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "nsIRunnable.h"
 #include "nsTArray.h"
 #include <speex/speex_resampler.h>
 
 class nsIRunnable;
@@ -1339,16 +1340,22 @@ class MediaStreamGraph {
   void UnregisterCaptureStreamForWindow(uint64_t aWindowId);
   already_AddRefed<MediaInputPort> ConnectToCaptureStream(
       uint64_t aWindowId, MediaStream* aMediaStream);
 
   void AssertOnGraphThreadOrNotRunning() const {
     MOZ_ASSERT(OnGraphThreadOrNotRunning());
   }
 
+  /**
+   * Returns a watchable of the graph's main-thread observable graph time.
+   * Main thread only.
+   */
+  virtual Watchable<GraphTime>& CurrentTime() = 0;
+
  protected:
   explicit MediaStreamGraph(TrackRate aSampleRate) : mSampleRate(aSampleRate) {
     MOZ_COUNT_CTOR(MediaStreamGraph);
   }
   virtual ~MediaStreamGraph() { MOZ_COUNT_DTOR(MediaStreamGraph); }
 
   // Intended only for assertions, either on graph thread or not running (in
   // which case we must be on the main thread).
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -540,16 +540,18 @@ class MediaStreamGraphImpl : public Medi
 
   // Capture Stream API. This allows to get a mixed-down output for a window.
   void RegisterCaptureStreamForWindow(uint64_t aWindowId,
                                       ProcessedMediaStream* aCaptureStream);
   void UnregisterCaptureStreamForWindow(uint64_t aWindowId);
   already_AddRefed<MediaInputPort> ConnectToCaptureStream(
       uint64_t aWindowId, MediaStream* aMediaStream);
 
+  Watchable<GraphTime>& CurrentTime() override;
+
   class StreamSet {
    public:
     class iterator {
      public:
       explicit iterator(MediaStreamGraphImpl& aGraph)
           : mGraph(&aGraph), mArrayNum(-1), mArrayIndex(0) {
         ++(*this);
       }
@@ -874,13 +876,25 @@ class MediaStreamGraphImpl : public Medi
   const float mGlobalVolume;
 
 #ifdef DEBUG
   /**
    * Used to assert when AppendMessage() runs ControlMessages synchronously.
    */
   bool mCanRunMessagesSynchronously;
 #endif
+
+  /**
+   * The graph's main-thread observable graph time.
+   * Updated by the stable state runnable after each iteration.
+   */
+  Watchable<GraphTime> mMainThreadGraphTime;
+
+  /**
+   * Set based on mProcessedTime at end of iteration.
+   * Read by stable state runnable on main thread. Protected by mMonitor.
+   */
+  GraphTime mNextMainThreadGraphTime = 0;
 };
 
 }  // namespace mozilla
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */