Bug 1195632. Part 2 - Have DecodedStream listen to push events of the media queues and call SendData() on its own without the help of MDSM. r=roc.
authorJW Wang <jwwang@mozilla.com>
Mon, 24 Aug 2015 10:04:21 +0800
changeset 291615 d7f6ba1197608932989a0ace028aa4d45fd66331
parent 291614 6521d38bea7a838946c9f2f1d40c985ffd911ff8
child 291616 16327a5d2961773729aca9f1928d6e7d0334e77f
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1195632
milestone43.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 1195632. Part 2 - Have DecodedStream listen to push events of the media queues and call SendData() on its own without the help of MDSM. r=roc.
dom/media/DecodedStream.cpp
dom/media/DecodedStream.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -375,16 +375,17 @@ nsRefPtr<GenericPromise>
 DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
 {
   AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
 
   mStartTime.emplace(aStartTime);
   mInfo = aInfo;
+  ConnectListener();
 
   class R : public nsRunnable {
     typedef MozPromiseHolder<GenericPromise> Promise;
     typedef void(DecodedStream::*Method)(Promise&&);
   public:
     R(DecodedStream* aThis, Method aMethod, Promise&& aPromise)
       : mThis(aThis), mMethod(aMethod)
     {
@@ -412,17 +413,19 @@ DecodedStream::StartPlayback(int64_t aSt
 void DecodedStream::StopPlayback()
 {
   AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   // Playback didn't even start at all.
   if (mStartTime.isNothing()) {
     return;
   }
+
   mStartTime.reset();
+  DisconnectListener();
 
   // Clear mData immediately when this playback session ends so we won't
   // send data to the wrong stream in SendData() in next playback session.
   DecodedStreamData* data = mData.release();
   // mData is not yet created on the main thread.
   if (!data) {
     return;
   }
@@ -450,16 +453,29 @@ DecodedStream::CreateData(MozPromiseHold
     // Resolve the promise to indicate the end of playback.
     aPromise.Resolve(true, __func__);
     return;
   }
 
   auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
   mData.reset(new DecodedStreamData(source, mPlaying, Move(aPromise)));
   mOutputStreamManager.Connect(mData->mStream);
+
+  // Start to send data to the stream immediately
+  nsRefPtr<DecodedStream> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
+    ReentrantMonitorAutoEnter mon(self->GetReentrantMonitor());
+    // Don't send data if playback has ended.
+    if (self->mStartTime.isSome()) {
+      self->SendData();
+    }
+  });
+  // Don't assert success because the owner thread might have begun shutdown
+  // while we are still dealing with jobs on the main thread.
+  mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
 }
 
 bool
 DecodedStream::HasConsumers() const
 {
   return !mOutputStreamManager.IsEmpty();
 }
 
@@ -819,9 +835,37 @@ DecodedStream::GetPosition() const
 bool
 DecodedStream::IsFinished() const
 {
   AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mData && mData->IsFinished();
 }
 
+void
+DecodedStream::ConnectListener()
+{
+  AssertOwnerThread();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  mAudioPushListener = mAudioQueue.PushEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mAudioFinishListener = mAudioQueue.FinishEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mVideoPushListener = mVideoQueue.PushEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mVideoFinishListener = mVideoQueue.FinishEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+}
+
+void
+DecodedStream::DisconnectListener()
+{
+  AssertOwnerThread();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  mAudioPushListener.Disconnect();
+  mVideoPushListener.Disconnect();
+  mAudioFinishListener.Disconnect();
+  mVideoFinishListener.Disconnect();
+}
+
 } // namespace mozilla
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -3,16 +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/. */
 
 #ifndef DecodedStream_h_
 #define DecodedStream_h_
 
 #include "nsTArray.h"
+#include "MediaEventSource.h"
 #include "MediaInfo.h"
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/nsRefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
@@ -122,33 +123,35 @@ public:
   void SetVolume(double aVolume);
   void SetSameOrigin(bool aSameOrigin);
 
   int64_t AudioEndTime() const;
   int64_t GetPosition() const;
   bool IsFinished() const;
   bool HasConsumers() const;
 
-  void SendData();
-
 protected:
   virtual ~DecodedStream();
 
 private:
   ReentrantMonitor& GetReentrantMonitor() const;
   void CreateData(MozPromiseHolder<GenericPromise>&& aPromise);
   void InitTracks();
   void AdvanceTracks();
   void SendAudio(double aVolume, bool aIsSameOrigin);
   void SendVideo(bool aIsSameOrigin);
+  void SendData();
 
   void AssertOwnerThread() const {
     MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   }
 
+  void ConnectListener();
+  void DisconnectListener();
+
   const nsRefPtr<AbstractThread> mOwnerThread;
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
   OutputStreamManager mOutputStreamManager;
 
   // TODO: This is a temp solution to get rid of decoder monitor on the main
   // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
@@ -163,13 +166,18 @@ private:
   double mVolume;
   bool mSameOrigin;
 
   Maybe<int64_t> mStartTime;
   MediaInfo mInfo;
 
   MediaQueue<MediaData>& mAudioQueue;
   MediaQueue<MediaData>& mVideoQueue;
+
+  MediaEventListener mAudioPushListener;
+  MediaEventListener mVideoPushListener;
+  MediaEventListener mAudioFinishListener;
+  MediaEventListener mVideoFinishListener;
 };
 
 } // namespace mozilla
 
 #endif // DecodedStream_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -364,24 +364,22 @@ int64_t MediaDecoderStateMachine::GetDec
   AssertCurrentThreadInMonitor();
   int64_t audioDecoded = AudioQueue().Duration();
   if (AudioEndTime() != -1) {
     audioDecoded += AudioEndTime() - GetMediaTime();
   }
   return audioDecoded;
 }
 
-void MediaDecoderStateMachine::SendStreamData()
+void MediaDecoderStateMachine::DiscardStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  mDecodedStream->SendData();
-
   const auto clockTime = GetClock();
   while (true) {
     const MediaData* a = AudioQueue().PeekFront();
 
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
     // the decoding speed.
@@ -563,20 +561,16 @@ MediaDecoderStateMachine::OnAudioDecoded
     case DECODER_STATE_DECODING: {
       Push(audio);
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       if (mIsAudioPrerolling && DonePrerollingAudio()) {
         StopPrerollingAudio();
       }
-      // Schedule the state machine to send stream data as soon as possible.
-      if (mAudioCaptured) {
-        ScheduleStateMachine();
-      }
       return;
     }
 
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
         // We've received a sample from a previous decode. Discard it.
         return;
       }
@@ -762,20 +756,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
-      // Schedule the state machine to notify track ended as soon as possible.
-      if (mAudioCaptured) {
-        ScheduleStateMachine();
-      }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
         // We've received a sample from a previous decode. Discard it.
         return;
       }
 
@@ -852,17 +842,17 @@ MediaDecoderStateMachine::OnVideoDecoded
 
       // Schedule the state machine to send stream data as soon as possible or
       // the VideoQueue() is empty before the Push().
       // VideoQueue() is empty implies the state machine thread doesn't have
       // precise time information about video frames. Once the first video
       // frame pushed in the queue, schedule the state machine as soon as
       // possible to render the video frame or delay the state machine thread
       // accurately.
-      if (mAudioCaptured || VideoQueue().GetSize() == 1) {
+      if (VideoQueue().GetSize() == 1) {
         ScheduleStateMachine();
       }
 
       // For non async readers, if the requested video sample was slow to
       // arrive, increase the amount of audio we buffer to ensure that we
       // don't run out of audio. This is unnecessary for async readers,
       // since they decode audio and video on different threads so they
       // are unlikely to run out of decoded audio.
@@ -2646,17 +2636,17 @@ void MediaDecoderStateMachine::UpdateRen
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (!IsPlaying() || mLogicallySeeking) {
     return;
   }
 
   if (mAudioCaptured) {
-    SendStreamData();
+    DiscardStreamData();
   }
 
   TimeStamp nowTime;
   const int64_t clockTime = GetClock(&nowTime);
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame and drop
   // the current frame.
   NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -318,20 +318,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     if (mReader) {
       mReader->BreakCycles();
     }
     mResource = nullptr;
     mDecoder = nullptr;
   }
 
-  // Copy queued audio/video data in the reader to any output MediaStreams that
-  // need it.
-  void SendStreamData();
-  void FinishStreamData();
+  // Discard audio/video data that are already played by MSG.
+  void DiscardStreamData();
   bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
   bool HaveEnoughDecodedVideo();
 
   // Returns true if the state machine has shutdown or is in the process of
   // shutting down. The decoder monitor must be held while calling this.
   bool IsShutdown();
 
   void QueueMetadata(const media::TimeUnit& aPublishTime,