Bug 1191696. Part 1 - Handle the promise returned by DecodedStream::StartPlayback in MDSM. r=roc.
authorJW Wang <jwwang@mozilla.com>
Mon, 17 Aug 2015 07:52:28 +0800
changeset 257974 6dd75dc65de38db09c04203f906733513a688213
parent 257973 f93e180301f3f227b243fa0d71050bb0f0159405
child 257975 e44c94a159dbf8d50b52a4e2d099498e64576c8b
push id29238
push userryanvm@gmail.com
push dateMon, 17 Aug 2015 13:06:57 +0000
treeherdermozilla-central@a6eeb28458fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1191696
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 1191696. Part 1 - Handle the promise returned by DecodedStream::StartPlayback in 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
@@ -1,21 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "AudioSegment.h"
 #include "DecodedStream.h"
+#include "MediaData.h"
+#include "MediaQueue.h"
 #include "MediaStreamGraph.h"
-#include "AudioSegment.h"
+#include "SharedBuffer.h"
 #include "VideoSegment.h"
-#include "MediaQueue.h"
-#include "MediaData.h"
-#include "SharedBuffer.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 class DecodedStreamGraphListener : public MediaStreamListener {
   typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
 public:
   explicit DecodedStreamGraphListener(MediaStream* aStream)
@@ -239,24 +239,25 @@ DecodedStream::DecodedStream(MediaQueue<
   , mVideoQueue(aVideoQueue)
 {
 }
 
 DecodedStream::~DecodedStream()
 {
 }
 
-void
+nsRefPtr<GenericPromise>
 DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  if (mStartTime.isNothing()) {
-    mStartTime.emplace(aStartTime);
-    mInfo = aInfo;
-  }
+  MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
+  mStartTime.emplace(aStartTime);
+  mInfo = aInfo;
+  // TODO: fix me in next patches.
+  return nullptr;
 }
 
 void DecodedStream::StopPlayback()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mStartTime.reset();
 }
 
@@ -675,36 +676,39 @@ DecodedStream::AdvanceTracks()
     endPosition = std::max(endPosition, videoEnd);
   }
 
   if (!mData->mHaveSentFinish) {
     mData->mStream->AdvanceKnownTracksTime(endPosition);
   }
 }
 
-bool
+void
 DecodedStream::SendData()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
 
+  // Nothing to do when the stream is finished.
+  if (mData->mHaveSentFinish) {
+    return;
+  }
+
   InitTracks();
   SendAudio(mVolume, mSameOrigin);
   SendVideo(mSameOrigin);
   AdvanceTracks();
 
   bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) &&
                   (!mInfo.HasVideo() || mVideoQueue.IsFinished());
 
   if (finished && !mData->mHaveSentFinish) {
     mData->mHaveSentFinish = true;
     mData->mStream->Finish();
   }
-
-  return finished;
 }
 
 int64_t
 DecodedStream::AudioEndTime() const
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (mStartTime.isSome() && mInfo.HasAudio()) {
     CheckedInt64 t = mStartTime.ref() +
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -7,16 +7,17 @@
 #ifndef DecodedStream_h_
 #define DecodedStream_h_
 
 #include "nsTArray.h"
 #include "MediaInfo.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/MozPromise.h"
 #include "mozilla/nsRefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/Point.h"
 
 namespace mozilla {
 
 class DecodedStream;
@@ -48,17 +49,21 @@ public:
 class DecodedStream {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
 public:
   DecodedStream(MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue);
 
   // Mimic MDSM::StartAudioThread.
   // Must be called before any calls to SendData().
-  void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo);
+  //
+  // Return a promise which will be resolved when the stream is finished
+  // or rejected if any error.
+  nsRefPtr<GenericPromise> StartPlayback(int64_t aStartTime,
+                                         const MediaInfo& aInfo);
   // Mimic MDSM::StopAudioThread.
   void StopPlayback();
 
   void DestroyData();
   void RecreateData();
   void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
   void Remove(MediaStream* aStream);
 
@@ -66,18 +71,17 @@ public:
   void SetVolume(double aVolume);
   void SetSameOrigin(bool aSameOrigin);
 
   int64_t AudioEndTime() const;
   int64_t GetPosition() const;
   bool IsFinished() const;
   bool HasConsumers() const;
 
-  // Return true if stream is finished.
-  bool SendData();
+  void SendData();
 
 protected:
   virtual ~DecodedStream();
 
 private:
   ReentrantMonitor& GetReentrantMonitor() const;
   void RecreateData(MediaStreamGraph* aGraph);
   void Connect(OutputStreamData* aStream);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -371,38 +371,32 @@ int64_t MediaDecoderStateMachine::GetDec
 }
 
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  bool finished = mDecodedStream->SendData();
+  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.
     if (a && a->mTime <= clockTime) {
       nsRefPtr<MediaData> releaseMe = AudioQueue().PopFront();
       continue;
     }
     break;
   }
-
-  // To be consistent with AudioSink, |mAudioCompleted| is not set
-  // until all samples are drained.
-  if (finished && AudioQueue().GetSize() == 0) {
-    mAudioCompleted = true;
-  }
 }
 
 bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (AudioQueue().GetSize() == 0 ||
@@ -1074,22 +1068,17 @@ void MediaDecoderStateMachine::MaybeStar
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
 
   mDecoder->DispatchPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
   StartAudioThread();
-
-  // Tell DecodedStream to start playback with specified start time and media
-  // info. This is consistent with how we create AudioSink in StartAudioThread().
-  if (mAudioCaptured) {
-    mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
-  }
+  StartDecodedStream();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime);
@@ -1787,16 +1776,42 @@ MediaDecoderStateMachine::StartAudioThre
         &MediaDecoderStateMachine::OnAudioSinkError));
 
     mAudioSink->SetVolume(mVolume);
     mAudioSink->SetPlaybackRate(mPlaybackRate);
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
 }
 
+void
+MediaDecoderStateMachine::StopDecodedStream()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  AssertCurrentThreadInMonitor();
+  mDecodedStream->StopPlayback();
+  mDecodedStreamPromise.DisconnectIfExists();
+}
+
+void
+MediaDecoderStateMachine::StartDecodedStream()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  AssertCurrentThreadInMonitor();
+
+  // Tell DecodedStream to start playback with specified start time and media
+  // info. This is consistent with how we create AudioSink in StartAudioThread().
+  if (mAudioCaptured && !mDecodedStreamPromise.Exists()) {
+    mDecodedStreamPromise.Begin(
+      mDecodedStream->StartPlayback(GetMediaTime(), mInfo)->Then(
+        OwnerThread(), __func__, this,
+        &MediaDecoderStateMachine::OnDecodedStreamFinish,
+        &MediaDecoderStateMachine::OnDecodedStreamError));
+  }
+}
+
 int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(),
                "Should only call AudioDecodedUsecs() when we have audio");
   // The amount of audio we have decoded is the amount of audio data we've
   // already decoded and pushed to the hardware, plus the amount of audio
   // data waiting to be pushed to the hardware.
@@ -2390,17 +2405,17 @@ nsresult MediaDecoderStateMachine::RunSt
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
 
         // Stop audio sink after call to AudioEndTime() above, otherwise it will
         // return an incorrect value due to a null mAudioSink.
         StopAudioThread();
-        mDecodedStream->StopPlayback();
+        StopDecodedStream();
       }
 
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
@@ -2420,17 +2435,17 @@ MediaDecoderStateMachine::Reset()
              mState == DECODER_STATE_SEEKING ||
              mState == DECODER_STATE_DORMANT ||
              mState == DECODER_STATE_DECODING_NONE);
 
   // Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue
   // outside of the decoder monitor while we are clearing the queue and causes
   // crash for no samples to be popped.
   StopAudioThread();
-  mDecodedStream->StopPlayback();
+  StopDecodedStream();
 
   mVideoFrameEndTime = -1;
   mDecodedVideoEndTime = -1;
   mDecodedAudioEndTime = -1;
   mAudioCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
   mFirstVideoFrameAfterSeek = nullptr;
@@ -3071,16 +3086,42 @@ void MediaDecoderStateMachine::OnAudioSi
     return;
   }
 
   // Otherwise notify media decoder/element about this error for it makes
   // no sense to play an audio-only file without sound output.
   DecodeError();
 }
 
+void
+MediaDecoderStateMachine::OnDecodedStreamFinish()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
+
+  mDecodedStreamPromise.Complete();
+  if (mInfo.HasAudio()) {
+    mAudioCompleted = true;
+  }
+  // To notify PlaybackEnded as soon as possible.
+  ScheduleStateMachine();
+}
+
+void
+MediaDecoderStateMachine::OnDecodedStreamError()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
+
+  mDecodedStreamPromise.Complete();
+  DecodeError();
+}
+
 uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
@@ -3094,33 +3135,34 @@ void MediaDecoderStateMachine::DispatchA
     ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
     if (!self->mAudioCaptured) {
       // Stop the audio sink if it's running.
       self->StopAudioThread();
       self->mAudioCaptured = true;
       // Start DecodedStream if we are already playing. Otherwise it will be
       // handled in MaybeStartPlayback().
       if (self->IsPlaying()) {
-        self->mDecodedStream->StartPlayback(self->GetMediaTime(), self->mInfo);
+        self->StartDecodedStream();
       }
       self->ScheduleStateMachine();
     }
   });
   OwnerThread()->Dispatch(r.forget());
 }
 
 void MediaDecoderStateMachine::DispatchAudioUncaptured()
 {
   nsRefPtr<MediaDecoderStateMachine> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
   {
     MOZ_ASSERT(self->OnTaskQueue());
     ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
     if (self->mAudioCaptured) {
-      // Start again the audio sink
+      self->StopDecodedStream();
+      // Start again the audio sink.
       self->mAudioCaptured = false;
       if (self->IsPlaying()) {
         self->StartAudioThread();
       }
       self->ScheduleStateMachine();
     }
   });
   OwnerThread()->Dispatch(r.forget());
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -513,16 +513,20 @@ protected:
   // Stops the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
   void StopAudioThread();
 
   // Starts the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
   void StartAudioThread();
 
+  void StopDecodedStream();
+
+  void StartDecodedStream();
+
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
   // Notification method invoked when mLogicallySeeking changes.
   void LogicallySeekingChanged();
 
   // Notification method invoked when mSameOriginMedia changes.
   void SameOriginMediaChanged();
@@ -665,16 +669,20 @@ protected:
 private:
   // Resolved by the AudioSink to signal that all outstanding work is complete
   // and the sink is shutting down.
   void OnAudioSinkComplete();
 
   // Rejected by the AudioSink to signal errors.
   void OnAudioSinkError();
 
+  void OnDecodedStreamFinish();
+
+  void OnDecodedStreamError();
+
   // Return true if the video decoder's decode speed can not catch up the
   // play time.
   bool NeedToSkipToNextKeyframe();
 
   // The decoder object that created this state machine. The state machine
   // holds a strong reference to the decoder to ensure that the decoder stays
   // alive once media element has started the decoder shutdown process, and has
   // dropped its reference to the decoder. This enables the state machine to
@@ -1279,16 +1287,17 @@ private:
   // can be read on any thread while holding the monitor, or on the main thread
   // without holding the monitor.
   nsRefPtr<DecodedStream> mDecodedStream;
 
   // Media data resource from the decoder.
   nsRefPtr<MediaResource> mResource;
 
   MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise;
+  MozPromiseRequestHolder<GenericPromise> mDecodedStreamPromise;
 
   MediaEventListener mAudioQueueListener;
   MediaEventListener mVideoQueueListener;
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;