Bug 1505972 - request audio data when entering 'loopingDecoding' state if decoding has ended r=jya
authoralwu <alwu@mozilla.com>
Thu, 22 Nov 2018 13:27:21 +0000
changeset 504215 a773a42f4c001614c4ae826bc8ada7de29cea3e6
parent 504214 ac1c8b26e69085a1ce075939533eb081b9e997ce
child 504216 55b9e352f46dd27c4bc820b70906d91ef88e83a0
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)
reviewersjya
bugs1505972
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 1505972 - request audio data when entering 'loopingDecoding' state if decoding has ended r=jya When entering 'loopingDecoding' state, we should ensure we would continue to decoding even if the audio decoding has finished before. Differential Revision: https://phabricator.services.mozilla.com/D12589
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaQueue.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -751,16 +751,24 @@ class MediaDecoderStateMachine::Decoding
  */
 class MediaDecoderStateMachine::LoopingDecodingState
     : public MediaDecoderStateMachine::DecodingState {
  public:
   explicit LoopingDecodingState(Master* aPtr) : DecodingState(aPtr) {
     MOZ_ASSERT(mMaster->mLooping);
   }
 
+  void Enter() {
+    if (!mMaster->IsAudioDecoding()) {
+      SLOG("audio has ended, request the data again.");
+      RequestAudioDataFromStartPosition();
+    }
+    DecodingState::Enter();
+  }
+
   void Exit() override {
     mAudioDataRequest.DisconnectIfExists();
     mAudioSeekRequest.DisconnectIfExists();
     if (ShouldDiscardLoopedAudioData()) {
       mMaster->mAudioDataRequest.DisconnectIfExists();
       DiscardLoopedAudioData();
     }
     if (HasDecodedLastAudioFrame()) {
@@ -791,39 +799,41 @@ class MediaDecoderStateMachine::LoopingD
     // so we need to add the last ending time as the offset to correct the
     // audio data time in the next round when seamless looping is enabled.
     mAudioLoopingOffset = mMaster->mDecodedAudioEndTime;
 
     if (mMaster->mAudioDecodedDuration.isNothing()) {
       mMaster->mAudioDecodedDuration.emplace(mMaster->mDecodedAudioEndTime);
     }
 
-    SLOG(
-        "received EOS when seamless looping, starts seeking, "
-        "AudioLoopingOffset=[%" PRId64 "]",
-        mAudioLoopingOffset.ToMicroseconds());
+    SLOG("received EOS when seamless looping, starts seeking, "
+         "AudioLoopingOffset=[%" PRId64 "]",
+         mAudioLoopingOffset.ToMicroseconds());
+    RequestAudioDataFromStartPosition();
+  }
+
+ private:
+  void RequestAudioDataFromStartPosition() {
     Reader()->ResetDecode(TrackInfo::kAudioTrack);
     Reader()
         ->Seek(SeekTarget(media::TimeUnit::Zero(), SeekTarget::Accurate))
         ->Then(OwnerThread(), __func__,
                [this]() -> void {
                  mAudioSeekRequest.Complete();
-                 SLOG(
-                     "seeking completed, start to request first sample, "
-                     "queueing audio task - queued=%zu, decoder-queued=%zu",
-                     AudioQueue().GetSize(),
-                     Reader()->SizeOfAudioQueueInFrames());
-
-                 Reader()
-                     ->RequestAudioData()
+                 SLOG("seeking completed, start to request first sample, "
+                      "queueing audio task - queued=%zu, decoder-queued=%zu",
+                      AudioQueue().GetSize(),
+                      Reader()->SizeOfAudioQueueInFrames());
+
+                 Reader()->RequestAudioData()
                      ->Then(OwnerThread(), __func__,
                             [this](RefPtr<AudioData> aAudio) {
                               mAudioDataRequest.Complete();
-                              SLOG("got audio decoded sample [%" PRId64
-                                   ",%" PRId64 "]",
+                              SLOG("got audio decoded sample "
+                                   "[%" PRId64 ",%" PRId64 "]",
                                    aAudio->mTime.ToMicroseconds(),
                                    aAudio->GetEndTime().ToMicroseconds());
                               HandleAudioDecoded(aAudio);
                             },
                             [this](const MediaResult& aError) {
                               mAudioDataRequest.Complete();
                               HandleError(aError);
                             })
@@ -831,17 +841,16 @@ class MediaDecoderStateMachine::LoopingD
                },
                [this](const SeekRejectValue& aReject) -> void {
                  mAudioSeekRequest.Complete();
                  HandleError(aReject.mError);
                })
         ->Track(mAudioSeekRequest);
   }
 
- private:
   void HandleError(const MediaResult& aError) {
     SLOG("audio looping failed, aError=%s", aError.ErrorName().get());
     MOZ_ASSERT(aError != NS_ERROR_DOM_MEDIA_END_OF_STREAM);
     mMaster->DecodeError(aError);
   }
 
   void EnsureAudioDecodeTaskQueued() override {
     if (mAudioSeekRequest.Exists() || mAudioDataRequest.Exists()) {
@@ -1892,16 +1901,17 @@ class MediaDecoderStateMachine::Bufferin
   const uint32_t mBufferingWait = 15;
 };
 
 /**
  * Purpose: play all the decoded data and fire the 'ended' event.
  *
  * Transition to:
  *   SEEKING if any seek request.
+ *   LOOPING_DECODING if MDSM enable looping.
  */
 class MediaDecoderStateMachine::CompletedState
     : public MediaDecoderStateMachine::StateObject {
  public:
   explicit CompletedState(Master* aPtr) : StateObject(aPtr) {}
 
   void Enter() {
     // On Android, the life cycle of graphic buffer is equal to Android's codec,
@@ -1971,16 +1981,22 @@ class MediaDecoderStateMachine::Complete
 
       // MediaSink::GetEndTime() must be called before stopping playback.
       mMaster->StopMediaSink();
     }
   }
 
   State GetState() const override { return DECODER_STATE_COMPLETED; }
 
+  void HandleLoopingChanged() override {
+    if (mMaster->mLooping) {
+      SetDecodingState();
+    }
+  }
+
   void HandleAudioCaptured() override {
     // MediaSink is changed. Schedule Step() to check if we can start playback.
     mMaster->ScheduleStateMachine();
   }
 
   void HandleVideoSuspendTimeout() override {
     // Do nothing since no decoding is going on.
   }
@@ -2200,17 +2216,16 @@ void MediaDecoderStateMachine::DecodeMet
                                        std::move(aMetadata.mTags),
                                        MediaDecoderEventVisibility::Observable);
 
   // Check whether the media satisfies the requirement of seamless looing.
   // (Before checking the media is audio only, we need to get metadata first.)
   mMaster->mSeamlessLoopingAllowed = StaticPrefs::MediaSeamlessLooping() &&
                                      mMaster->HasAudio() &&
                                      !mMaster->HasVideo();
-  mMaster->LoopingChanged();
 
   SetState<DecodingFirstFrameState>();
 }
 
 void MediaDecoderStateMachine::DormantState::HandlePlayStateChanged(
     MediaDecoder::PlayState aPlayState) {
   if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
     // Exit dormant when the user wants to play.
@@ -2267,17 +2282,20 @@ void MediaDecoderStateMachine::DecodingS
       !mMaster->mVideoDecodeSuspendTimer.IsScheduled() &&
       !mMaster->mVideoDecodeSuspended) {
     // If the VideoDecodeMode is Suspend and the timer is not schedule, it means
     // the timer has timed out and we should suspend video decoding now if
     // necessary.
     HandleVideoSuspendTimeout();
   }
 
-  if (!mMaster->IsVideoDecoding() && !mMaster->IsAudioDecoding()) {
+  // If decoding has ended and we are not in looping, we don't need to decode
+  // anything later.
+  if (!mMaster->IsVideoDecoding() && !mMaster->IsAudioDecoding() &&
+      !mMaster->mLooping) {
     SetState<CompletedState>();
     return;
   }
 
   mOnAudioPopped =
       AudioQueue().PopFrontEvent().Connect(OwnerThread(), [this]() {
         if (mMaster->IsAudioDecoding() && !mMaster->HaveEnoughDecodedAudio()) {
           EnsureAudioDecodeTaskQueued();
@@ -3484,16 +3502,17 @@ void MediaDecoderStateMachine::SetPlayba
 
 void MediaDecoderStateMachine::PreservesPitchChanged() {
   MOZ_ASSERT(OnTaskQueue());
   mMediaSink->SetPreservesPitch(mPreservesPitch);
 }
 
 void MediaDecoderStateMachine::LoopingChanged() {
   MOZ_ASSERT(OnTaskQueue());
+  LOGV("LoopingChanged, looping=%d", mLooping.Ref());
   if (mSeamlessLoopingAllowed) {
     mStateObj->HandleLoopingChanged();
   }
 }
 
 RefPtr<GenericPromise> MediaDecoderStateMachine::InvokeSetSink(
     RefPtr<AudioDeviceInfo> aSink) {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -35,22 +35,26 @@ class MediaQueue : private nsDeque {
 
   inline size_t GetSize() const {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
     return nsDeque::GetSize();
   }
 
   inline void Push(T* aItem) {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
-    MOZ_ASSERT(!mEndOfStream);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     MOZ_ASSERT(aItem->GetEndTime() >= aItem->mTime);
     nsDeque::Push(aItem);
     mPushEvent.Notify(RefPtr<T>(aItem));
+    // Pushing new data after queue has ended means that the stream is active
+    // again, so we should not mark it as ended.
+    if (mEndOfStream) {
+      mEndOfStream = false;
+    }
   }
 
   inline already_AddRefed<T> PopFront() {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopFrontEvent.Notify(rv);
     }