Bug 1189624 - Have AudioSink listen to MediaQueue events to know whether to continue playback. r=kinetik.
authorJW Wang <jwwang@mozilla.com>
Thu, 06 Aug 2015 10:16:49 +0800
changeset 288137 f146339499323c25c2b5e009e5f520dea60be791
parent 288136 a3243d1ec51f6d1d565cdd9f8229333f8a4ebe26
child 288138 8c47f6709cf5284a46b95154accf1bd801442c9c
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1189624
milestone42.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 1189624 - Have AudioSink listen to MediaQueue events to know whether to continue playback. r=kinetik.
dom/media/AudioSink.cpp
dom/media/AudioSink.h
dom/media/MediaDecoderStateMachine.cpp
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -53,16 +53,43 @@ AudioSink::DispatchTask(already_AddRefed
 {
   DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
   // There isn't much we can do if Dispatch() fails.
   // Just assert it to keep things simple.
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 void
+AudioSink::OnAudioQueueEvent()
+{
+  AssertOnAudioThread();
+  if (!mAudioLoopScheduled) {
+    AudioLoop();
+  }
+}
+
+void
+AudioSink::ConnectListener()
+{
+  AssertOnAudioThread();
+  mPushListener = AudioQueue().PushEvent().Connect(
+    mThread, this, &AudioSink::OnAudioQueueEvent);
+  mFinishListener = AudioQueue().FinishEvent().Connect(
+    mThread, this, &AudioSink::OnAudioQueueEvent);
+}
+
+void
+AudioSink::DisconnectListener()
+{
+  AssertOnAudioThread();
+  mPushListener.Disconnect();
+  mFinishListener.Disconnect();
+}
+
+void
 AudioSink::ScheduleNextLoop()
 {
   AssertOnAudioThread();
   if (mAudioLoopScheduled) {
     return;
   }
   mAudioLoopScheduled = true;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
@@ -216,22 +243,16 @@ AudioSink::SetPlaying(bool aPlaying)
     // Wake up the audio loop to play next sample.
     if (aPlaying && !self->mAudioLoopScheduled) {
       self->AudioLoop();
     }
   });
   DispatchTask(r.forget());
 }
 
-void
-AudioSink::NotifyData()
-{
-  ScheduleNextLoopCrossThread();
-}
-
 nsresult
 AudioSink::InitializeAudioStream()
 {
   // AudioStream initialization can block for extended periods in unusual
   // circumstances, so we take care to drop the decoder monitor while
   // initializing.
   RefPtr<AudioStream> audioStream(new AudioStream());
   nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
@@ -311,38 +332,40 @@ AudioSink::AudioLoop()
       nsresult rv = InitializeAudioStream();
       if (NS_FAILED(rv)) {
         NS_WARNING("Initializing AudioStream failed.");
         mEndPromise.Reject(rv, __func__);
         SetState(AUDIOSINK_STATE_ERROR);
         break;
       }
       SetState(AUDIOSINK_STATE_PLAYING);
+      ConnectListener();
       break;
     }
 
     case AUDIOSINK_STATE_PLAYING: {
       if (WaitingForAudioToPlay()) {
-        // NotifyData() will schedule next loop.
+        // OnAudioQueueEvent() will schedule next loop.
         break;
       }
       if (!IsPlaybackContinuing()) {
         SetState(AUDIOSINK_STATE_COMPLETE);
         break;
       }
       if (!PlayAudio()) {
         SetState(AUDIOSINK_STATE_COMPLETE);
         break;
       }
       // Schedule next loop to play next sample.
       ScheduleNextLoop();
       break;
     }
 
     case AUDIOSINK_STATE_COMPLETE: {
+      DisconnectListener();
       FinishAudioLoop();
       SetState(AUDIOSINK_STATE_SHUTDOWN);
       break;
     }
 
     case AUDIOSINK_STATE_SHUTDOWN:
       break;
 
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -48,36 +48,36 @@ public:
   // Shut down the AudioSink's resources.
   void Shutdown();
 
   void SetVolume(double aVolume);
   void SetPlaybackRate(double aPlaybackRate);
   void SetPreservesPitch(bool aPreservesPitch);
   void SetPlaying(bool aPlaying);
 
-  // Wake up the audio loop if it is waiting for data to play or the audio
-  // queue is finished.
-  void NotifyData();
-
 private:
   enum State {
     AUDIOSINK_STATE_INIT,
     AUDIOSINK_STATE_PLAYING,
     AUDIOSINK_STATE_COMPLETE,
     AUDIOSINK_STATE_SHUTDOWN,
     AUDIOSINK_STATE_ERROR
   };
 
   ~AudioSink() {}
 
   void DispatchTask(already_AddRefed<nsIRunnable>&& event);
   void SetState(State aState);
   void ScheduleNextLoop();
   void ScheduleNextLoopCrossThread();
 
+  void OnAudioQueueEvent();
+  void ConnectListener();
+  void DisconnectListener();
+
   // The main loop for the audio thread. Sent to the thread as
   // an nsRunnableMethod. This continually does blocking writes to
   // to audio stream to play audio data.
   void AudioLoop();
 
   // Allocate and initialize mAudioStream.  Returns NS_OK on success.
   nsresult InitializeAudioStream();
 
@@ -170,13 +170,16 @@ private:
 
   dom::AudioChannel mChannel;
 
   bool mStopAudioThread;
 
   bool mPlaying;
 
   MozPromiseHolder<GenericPromise> mEndPromise;
+
+  MediaEventListener mPushListener;
+  MediaEventListener mFinishListener;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -628,19 +628,16 @@ MediaDecoderStateMachine::Push(AudioData
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   AudioQueue().Push(aSample);
   UpdateNextFrameStatus();
   DispatchDecodeTasksIfNeeded();
 
-  if (mAudioSink) {
-    mAudioSink->NotifyData();
-  }
 }
 
 void
 MediaDecoderStateMachine::PushFront(AudioData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
 
@@ -774,20 +771,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
       mDecoder->GetReentrantMonitor().NotifyAll();
-      // Tell AudioSink to wake up for audio queue is finished.
-      if (mAudioSink) {
-        mAudioSink->NotifyData();
-      }
       // Schedule the state machine to notify track ended as soon as possible.
       if (mAudioCaptured) {
         ScheduleStateMachine();
       }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {