Bug 1158448 Part 2 - Remove ApplyStateToStateMachine and PLAY_STATE_SEEKING r=jwwang,bwu
authorSotaro Ikeda <sikeda@mozilla.com>
Thu, 07 May 2015 08:19:46 -0700
changeset 274184 d55f825951b42409710cf2350e55623c6bcd0c86
parent 274183 5edde270844458f6d0d2debe25cb075878fc1afd
child 274185 2a68efa2d877244e2b5ac5ffe926e355bafec162
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang, bwu
bugs1158448
milestone40.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 1158448 Part 2 - Remove ApplyStateToStateMachine and PLAY_STATE_SEEKING r=jwwang,bwu
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/omx/AudioOffloadPlayer.cpp
dom/media/omx/MediaOmxCommonDecoder.cpp
dom/media/omx/MediaOmxCommonDecoder.h
dom/media/omx/RtspMediaCodecDecoder.cpp
dom/media/omx/RtspMediaCodecDecoder.h
dom/media/omx/RtspOmxDecoder.cpp
dom/media/omx/RtspOmxDecoder.h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -273,17 +273,16 @@ void MediaDecoder::CancelDormantTimer()
   }
 }
 
 void MediaDecoder::Pause()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (mPlayState == PLAY_STATE_LOADING ||
-      mPlayState == PLAY_STATE_SEEKING ||
       IsEnded()) {
     mNextState = PLAY_STATE_PAUSED;
     return;
   }
 
   ChangeState(PLAY_STATE_PAUSED);
 }
 
@@ -432,16 +431,30 @@ MediaDecoder::OutputStreamData::Init(Med
   aStream->AddListener(mListener);
 }
 
 MediaDecoder::OutputStreamData::~OutputStreamData()
 {
   mListener->Forget();
 }
 
+void MediaDecoder::UpdateDecodedStream()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  if (mDecodedStream) {
+    bool blockForPlayState = mPlayState != PLAY_STATE_PLAYING || mLogicallySeeking;
+    if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) {
+      mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
+      mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
+    }
+  }
+}
+
 void MediaDecoder::DestroyDecodedStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   // Avoid the redundant blocking to output stream.
   if (!GetDecodedStream()) {
     return;
@@ -605,16 +618,18 @@ MediaDecoder::MediaDecoder() :
   mDuration(-1),
   mMediaSeekable(true),
   mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
   mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
              "MediaDecoder::mPlayState (Canonical)"),
   mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
              "MediaDecoder::mNextState (Canonical)"),
+  mLogicallySeeking(AbstractThread::MainThread(), false,
+             "MediaDecoder::mLogicallySeeking (Canonical)"),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
   mMinimizePreroll(false),
@@ -788,54 +803,66 @@ nsresult MediaDecoder::Play()
 
   NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
   if (mPausedForPlaybackRateNull) {
     return NS_OK;
   }
   ScheduleStateMachineThread();
   if (IsEnded()) {
     return Seek(0, SeekTarget::PrevSyncPoint);
-  } else if (mPlayState == PLAY_STATE_LOADING || mPlayState == PLAY_STATE_SEEKING) {
+  } else if (mPlayState == PLAY_STATE_LOADING) {
     mNextState = PLAY_STATE_PLAYING;
     return NS_OK;
   }
 
   ChangeState(PLAY_STATE_PLAYING);
   return NS_OK;
 }
 
 nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  NS_ENSURE_TRUE(!mShuttingDown, NS_ERROR_FAILURE);
+
   UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
 
   MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
 
   int64_t timeUsecs = 0;
   nsresult rv = SecondsToUsecs(aTime, timeUsecs);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
   mCurrentTime = aTime;
   mWasEndedWhenEnteredDormant = false;
 
-  // If we are already in the seeking state, the new seek overrides the old one.
-  if (mPlayState != PLAY_STATE_LOADING) {
-    mSeekRequest.DisconnectIfExists();
+  mLogicallySeeking = true;
+  UpdateDecodedStream();
+  SeekTarget target = SeekTarget(timeUsecs, aSeekType);
+  CallSeek(target);
+
+  if (mPlayState == PLAY_STATE_ENDED) {
     bool paused = false;
     if (mOwner) {
       paused = mOwner->GetPaused();
     }
-    mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING;
     PinForSeek();
-    ChangeState(PLAY_STATE_SEEKING);
+    ChangeState(paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING);
   }
+  return NS_OK;
+}
 
-  return ScheduleStateMachineThread();
+void MediaDecoder::CallSeek(const SeekTarget& aTarget)
+{
+  mSeekRequest.DisconnectIfExists();
+  mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
+                                    mDecoderStateMachine.get(), __func__,
+                                    &MediaDecoderStateMachine::Seek, aTarget)
+    ->RefableThen(AbstractThread::MainThread(), __func__, this,
+                  &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
 }
 
 double MediaDecoder::GetCurrentTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mCurrentTime;
 }
 
@@ -914,17 +941,16 @@ void MediaDecoder::MetadataLoaded(nsAuto
 const char*
 MediaDecoder::PlayStateStr()
 {
   switch (mPlayState) {
     case PLAY_STATE_START: return "PLAY_STATE_START";
     case PLAY_STATE_LOADING: return "PLAY_STATE_LOADING";
     case PLAY_STATE_PAUSED: return "PLAY_STATE_PAUSED";
     case PLAY_STATE_PLAYING: return "PLAY_STATE_PLAYING";
-    case PLAY_STATE_SEEKING: return "PLAY_STATE_SEEKING";
     case PLAY_STATE_ENDED: return "PLAY_STATE_ENDED";
     case PLAY_STATE_SHUTDOWN: return "PLAY_STATE_SHUTDOWN";
     default: return "INVALID_PLAY_STATE";
   }
 }
 
 void MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
                                     MediaDecoderEventVisibility aEventVisibility)
@@ -951,22 +977,17 @@ void MediaDecoder::FirstFrameLoaded(nsAu
   // This can run cache callbacks.
   mResource->EnsureCacheUpToDate();
 
   // The element can run javascript via events
   // before reaching here, so only change the
   // state if we're still set to the original
   // loading state.
   if (mPlayState == PLAY_STATE_LOADING && !mIsDormant) {
-    if (mRequestedSeekTarget.IsValid()) {
-      ChangeState(PLAY_STATE_SEEKING);
-    }
-    else {
-      ChangeState(mNextState);
-    }
+    ChangeState(mNextState);
   }
 
   // Run NotifySuspendedStatusChanged now to give us a chance to notice
   // that autoplay should run.
   NotifySuspendedStatusChanged();
 }
 
 void MediaDecoder::ResetConnectionState()
@@ -1020,18 +1041,17 @@ bool MediaDecoder::IsSameOriginMedia()
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
   return mSameOriginMedia;
 }
 
 bool MediaDecoder::IsSeeking() const
 {
   MOZ_ASSERT(NS_IsMainThread());
-  return !mIsDormant && (mPlayState == PLAY_STATE_SEEKING ||
-    (mPlayState == PLAY_STATE_LOADING && mRequestedSeekTarget.IsValid()));
+  return mLogicallySeeking;
 }
 
 bool MediaDecoder::IsEndedOrShutdown() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return IsEnded() || mPlayState == PLAY_STATE_SHUTDOWN;
 }
 
@@ -1041,17 +1061,17 @@ bool MediaDecoder::IsEnded() const
          (mWasEndedWhenEnteredDormant && (mPlayState != PLAY_STATE_SHUTDOWN));
 }
 
 void MediaDecoder::PlaybackEnded()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown ||
-      mPlayState == PLAY_STATE_SEEKING ||
+      mLogicallySeeking ||
       mPlayState == PLAY_STATE_LOADING) {
     return;
   }
 
   PlaybackPositionChanged();
   ChangeState(PLAY_STATE_ENDED);
   InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
 
@@ -1220,40 +1240,34 @@ void MediaDecoder::OnSeekResolved(SeekRe
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSeekRequest.Complete();
 
   if (mShuttingDown)
     return;
 
   bool fireEnded = false;
-  bool seekWasAborted = false;
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
     // An additional seek was requested while the current seek was
     // in operation.
-    if (mRequestedSeekTarget.IsValid()) {
-      ChangeState(PLAY_STATE_SEEKING);
-      seekWasAborted = true;
-    } else {
-      UnpinForSeek();
-      fireEnded = aVal.mAtEnd;
-      if (aVal.mAtEnd) {
-        ChangeState(PLAY_STATE_ENDED);
-      } else if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
-        ChangeState(aVal.mAtEnd ? PLAY_STATE_ENDED : mNextState);
-      }
+    UnpinForSeek();
+    fireEnded = aVal.mAtEnd;
+    if (aVal.mAtEnd) {
+      ChangeState(PLAY_STATE_ENDED);
     }
+    mLogicallySeeking = false;
+    UpdateDecodedStream();
   }
 
   PlaybackPositionChanged(aVal.mEventVisibility);
 
   if (mOwner) {
-    if (!seekWasAborted && (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
+    if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
       mOwner->SeekCompleted();
       if (fireEnded) {
         mOwner->PlaybackEnded();
       }
     }
   }
 }
 
@@ -1279,45 +1293,28 @@ void MediaDecoder::ChangeState(PlayState
     mNextState = PLAY_STATE_PAUSED;
   }
 
   if (mPlayState == PLAY_STATE_SHUTDOWN) {
     GetReentrantMonitor().NotifyAll();
     return;
   }
 
-  if (mDecodedStream) {
-    bool blockForPlayState = aState != PLAY_STATE_PLAYING;
-    if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) {
-      mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
-      mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
-    }
-  }
-
   DECODER_LOG("ChangeState %s => %s",
               gPlayStateStr[mPlayState], gPlayStateStr[aState]);
   mPlayState = aState;
 
+  UpdateDecodedStream();
+
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (IsEnded()) {
     RemoveMediaTracks();
   }
 
-  // XXXbholley - We should refactor things and move this to the code that
-  // actually initiates the seek.
-  if (mPlayState == PLAY_STATE_SEEKING) {
-    mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
-                                      mDecoderStateMachine.get(), __func__,
-                                      &MediaDecoderStateMachine::Seek, mRequestedSeekTarget)
-      ->RefableThen(AbstractThread::MainThread(), __func__, this,
-                    &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
-    mRequestedSeekTarget.Reset();
-  }
-
   ScheduleStateMachineThread();
 
   CancelDormantTimer();
   // Start dormant timer if necessary
   StartDormantTimer();
 
   GetReentrantMonitor().NotifyAll();
 }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -290,17 +290,16 @@ public:
   NS_DECL_NSIOBSERVER
 
   // Enumeration for the valid play states (see mPlayState)
   enum PlayState {
     PLAY_STATE_START,
     PLAY_STATE_LOADING,
     PLAY_STATE_PAUSED,
     PLAY_STATE_PLAYING,
-    PLAY_STATE_SEEKING,
     PLAY_STATE_ENDED,
     PLAY_STATE_SHUTDOWN
   };
 
   // Must be called exactly once, on the main thread, during startup.
   static void InitStatics();
 
   MediaDecoder();
@@ -508,16 +507,19 @@ public:
     nsRefPtr<MediaInputPort> mPort;
     nsRefPtr<OutputStreamListener> mListener;
   };
 
   /**
    * Connects mDecodedStream->mStream to aStream->mStream.
    */
   void ConnectDecodedStreamToOutputStream(OutputStreamData* aStream);
+
+  void UpdateDecodedStream();
+
   /**
    * Disconnects mDecodedStream->mStream from all outputs and clears
    * mDecodedStream.
    */
   void DestroyDecodedStream();
   /**
    * Recreates mDecodedStream. Call this to create mDecodedStream at first,
    * and when seeking, to ensure a new stream is set up with fresh buffers.
@@ -750,19 +752,16 @@ public:
 
   // Send a new set of metadata to the state machine, to be dispatched to the
   // main thread to be presented when the |currentTime| of the media is greater
   // or equal to aPublishTime.
   void QueueMetadata(int64_t aPublishTime,
                      nsAutoPtr<MediaInfo> aInfo,
                      nsAutoPtr<MetadataTags> aTags) override;
 
-  int64_t GetSeekTime() { return mRequestedSeekTarget.mTime; }
-  void ResetSeekTime() { mRequestedSeekTarget.Reset(); }
-
   /******
    * The following methods must only be called on the main
    * thread.
    ******/
 
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
@@ -798,17 +797,21 @@ public:
   //
   // Acquires the monitor. Call from any thread.
   virtual bool IsExpectingMoreData();
 
   // Called when the video has completed playing.
   // Call on the main thread only.
   void PlaybackEnded();
 
-  void OnSeekRejected() { mSeekRequest.Complete(); }
+  void OnSeekRejected()
+  {
+    mSeekRequest.Complete();
+    mLogicallySeeking = false;
+  }
   void OnSeekResolved(SeekResolveValue aVal);
 
   // Seeking has started. Inform the element on the main
   // thread.
   void SeekingStarted(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
 
   // Called when the backend has changed the current playback
   // position. It dispatches a timeupdate event and invalidates the frame.
@@ -1149,29 +1152,26 @@ protected:
   Canonical<PlayState> mPlayState;
 
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
   // Any change to the state must call NotifyAll on the monitor.
   // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.
   Canonical<PlayState> mNextState;
+
+  // True if the decoder is seeking.
+  Canonical<bool> mLogicallySeeking;
 public:
   AbstractCanonical<PlayState>* CanonicalPlayState() { return &mPlayState; }
   AbstractCanonical<PlayState>* CanonicalNextPlayState() { return &mNextState; }
+  AbstractCanonical<bool>* CanonicalLogicallySeeking() { return &mLogicallySeeking; }
 protected:
 
-  // Position to seek to when the seek notification is received by the
-  // decode thread.
-  // This can only be changed on the main thread while holding the decoder
-  // monitor. Thus, it can be safely read while holding the decoder monitor
-  // OR on the main thread.
-  // If the SeekTarget's IsValid() accessor returns false, then no seek has
-  // been requested. When a seek is started this is reset to invalid.
-  SeekTarget mRequestedSeekTarget;
+  virtual void CallSeek(const SeekTarget& aTarget);
 
   MediaPromiseConsumerHolder<SeekPromise> mSeekRequest;
 
   // True when seeking or otherwise moving the play position around in
   // such a manner that progress event data is inaccurate. This is set
   // during seek and duration operations to prevent the progress indicator
   // from jumping around. Read/Write from any thread. Must have decode monitor
   // locked before accessing.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -211,16 +211,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mPlayDuration(0),
   mStartTime(-1),
   mEndTime(-1),
   mDurationSet(false),
   mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
              "MediaDecoderStateMachine::mPlayState (Mirror)"),
   mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED,
                  "MediaDecoderStateMachine::mNextPlayState (Mirror)"),
+  mLogicallySeeking(mTaskQueue, false,
+             "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"),
   mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"),
   mFragmentEndTime(-1),
   mReader(aReader),
   mCurrentFrameTime(0),
   mAudioStartTime(-1),
   mAudioEndTime(-1),
   mDecodedAudioEndTime(-1),
@@ -303,27 +305,29 @@ MediaDecoderStateMachine::~MediaDecoderS
 void
 MediaDecoderStateMachine::InitializationTask()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
   mPlayState.Connect(mDecoder->CanonicalPlayState());
   mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
+  mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(mDecoder->CanonicalVolume());
   mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
   mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
 
   // Initialize watchers.
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
+  mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio() {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
   // 1. We've not completed playback of audio, and
@@ -1671,16 +1675,23 @@ void MediaDecoderStateMachine::PlayState
   // when the state machine notices the decoder's state change to PLAYING.
   if (mState == DECODER_STATE_BUFFERING) {
     StartDecoding();
   }
 
   ScheduleStateMachine();
 }
 
+void MediaDecoderStateMachine::LogicallySeekingChanged()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  ScheduleStateMachine();
+}
+
 void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
                                                      uint32_t aLength,
                                                      int64_t aOffset)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
 
   // While playing an unseekable stream of unknown duration, mEndTime is
@@ -2540,16 +2551,17 @@ MediaDecoderStateMachine::FinishShutdown
 
   // Now that those threads are stopped, there's no possibility of
   // mPendingWakeDecoder being needed again. Revoke it.
   mPendingWakeDecoder = nullptr;
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
+  mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
   mNextFrameStatus.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
@@ -2660,16 +2672,17 @@ nsresult MediaDecoderStateMachine::RunSt
         StopPlayback();
       }
 
       // Start playback if necessary so that the clock can be properly queried.
       MaybeStartPlayback();
 
       AdvanceFrame();
       NS_ASSERTION(mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
+                   mLogicallySeeking ||
                    IsStateMachineScheduled() ||
                    mPlaybackRate == 0.0, "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_BUFFERING: {
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
@@ -2728,16 +2741,17 @@ nsresult MediaDecoderStateMachine::RunSt
       // once to ensure the current playback position is advanced to the
       // end of the media, and so that we update the readyState.
       if (VideoQueue().GetSize() > 0 ||
           (HasAudio() && !mAudioCompleted) ||
           (mAudioCaptured && !mDecoder->GetDecodedStream()->IsFinished()))
       {
         AdvanceFrame();
         NS_ASSERTION(mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
+                     mLogicallySeeking ||
                      mPlaybackRate == 0 || IsStateMachineScheduled(),
                      "Must have timer scheduled");
         return NS_OK;
       }
 
       // StopPlayback in order to reset the IsPlaying() state so audio
       // is restarted correctly.
       StopPlayback();
@@ -2929,17 +2943,17 @@ int64_t MediaDecoderStateMachine::GetClo
 
 void MediaDecoderStateMachine::AdvanceFrame()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(!HasAudio() || mAudioStartTime != -1,
                "Should know audio start time if we have audio.");
 
-  if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
+  if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING || mLogicallySeeking) {
     return;
   }
 
   // If playbackRate is 0.0, we should stop the progress, but not be in paused
   // state, per spec.
   if (mPlaybackRate == 0.0) {
     return;
   }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -575,16 +575,19 @@ protected:
 
   // Starts the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
   nsresult StartAudioThread();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
+  // Notification method invoked when mLogicallySeeking changes.
+  void LogicallySeekingChanged();
+
   // Sets internal state which causes playback of media to pause.
   // The decoder monitor must be held.
   void StopPlayback();
 
   // If the conditions are right, sets internal state which causes playback
   // of media to begin or resume.
   // Must be called with the decode monitor held.
   void MaybeStartPlayback();
@@ -886,16 +889,17 @@ public:
   // Will be set when SetDuration has been called with a value != -1
   // mDurationSet false doesn't indicate that we do not have a valid duration
   // as mStartTime and mEndTime could have been set separately.
   bool mDurationSet;
 
   // The current play state and next play state, mirrored from the main thread.
   Mirror<MediaDecoder::PlayState> mPlayState;
   Mirror<MediaDecoder::PlayState> mNextPlayState;
+  Mirror<bool> mLogicallySeeking;
 
   // Returns true if we're logically playing, that is, if the Play() has
   // been called and Pause() has not or we have not yet reached the end
   // of media. This is irrespective of the seeking state; if the owner
   // calls Play() and then Seek(), we still count as logically playing.
   // The decoder monitor must be held.
   bool IsLogicallyPlaying()
   {
--- a/dom/media/omx/AudioOffloadPlayer.cpp
+++ b/dom/media/omx/AudioOffloadPlayer.cpp
@@ -683,17 +683,17 @@ nsresult AudioOffloadPlayer::StopTimeUpd
   nsresult rv = mTimeUpdateTimer->Cancel();
   mTimeUpdateTimer = nullptr;
   return rv;
 }
 
 MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) {
+  if (mSeekTarget.IsValid()) {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
   } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
   } else {
     return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
   }
 }
 
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -79,27 +79,37 @@ MediaOmxCommonDecoder::FirstFrameLoaded(
   mAudioOffloadPlayer = new AudioOffloadPlayer(this);
 #endif
   if (!mAudioOffloadPlayer) {
     return;
   }
 
   mAudioOffloadPlayer->SetSource(mReader->GetAudioOffloadTrack());
   status_t err = mAudioOffloadPlayer->Start(false);
-  if (err == OK) {
-    PauseStateMachine();
-    // Call ChangeState() to run AudioOffloadPlayer since offload state enabled
-    ChangeState(mPlayState);
+  if (err != OK) {
+    mAudioOffloadPlayer = nullptr;
+    mFallbackToStateMachine = true;
+    DECODER_LOG(PR_LOG_DEBUG, ("In %s Unable to start offload audio %d."
+      "Switching to normal mode", __PRETTY_FUNCTION__, err));
     return;
   }
-
-  mAudioOffloadPlayer = nullptr;
-  mFallbackToStateMachine = true;
-  DECODER_LOG(PR_LOG_DEBUG, ("In %s Unable to start offload audio %d."
-      "Switching to normal mode", __PRETTY_FUNCTION__, err));
+  PauseStateMachine();
+  if (mLogicallySeeking) {
+    int64_t timeUsecs = 0;
+    SecondsToUsecs(mCurrentTime, timeUsecs);
+    SeekTarget target = SeekTarget(timeUsecs,
+                                   SeekTarget::Accurate,
+                                   MediaDecoderEventVisibility::Observable);
+    mSeekRequest.DisconnectIfExists();
+    mSeekRequest.Begin(mAudioOffloadPlayer->Seek(target)
+      ->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
+                    &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
+  }
+  // Call ChangeState() to run AudioOffloadPlayer since offload state enabled
+  ChangeState(mPlayState);
 }
 
 void
 MediaOmxCommonDecoder::PauseStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
   DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
@@ -135,27 +145,26 @@ MediaOmxCommonDecoder::ResumeStateMachin
   if (!GetStateMachine()) {
     return;
   }
 
   mFallbackToStateMachine = true;
   mAudioOffloadPlayer = nullptr;
   int64_t timeUsecs = 0;
   SecondsToUsecs(mCurrentTime, timeUsecs);
-  mRequestedSeekTarget = SeekTarget(timeUsecs,
-                                    SeekTarget::Accurate,
-                                    MediaDecoderEventVisibility::Suppressed);
+  SeekTarget target = SeekTarget(timeUsecs,
+                                 SeekTarget::Accurate,
+                                 MediaDecoderEventVisibility::Suppressed);
   // Call Seek of MediaDecoderStateMachine to suppress seek events.
   RefPtr<nsRunnable> event =
     NS_NewRunnableMethodWithArg<SeekTarget>(
       GetStateMachine(),
       &MediaDecoderStateMachine::Seek,
-      mRequestedSeekTarget);
+      target);
   GetStateMachine()->TaskQueue()->Dispatch(event);
-  mRequestedSeekTarget.Reset();
 
   mNextState = mPlayState;
   ChangeState(PLAY_STATE_LOADING);
   // exit dormant state
   event =
     NS_NewRunnableMethodWithArg<bool>(
       GetStateMachine(),
       &MediaDecoderStateMachine::SetDormant,
@@ -221,41 +230,30 @@ MediaOmxCommonDecoder::ChangeState(PlayS
     return;
   }
 
   status_t err = mAudioOffloadPlayer->ChangeState(aState);
   if (err != OK) {
     ResumeStateMachine();
     return;
   }
-
-  switch (mPlayState) {
-    case PLAY_STATE_SEEKING:
-      mSeekRequest.Begin(mAudioOffloadPlayer->Seek(mRequestedSeekTarget)
-        ->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
-                      &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
-      mRequestedSeekTarget.Reset();
-      break;
-    default: {
-      break;
-    }
-  }
 }
 
 void
-MediaOmxCommonDecoder::ApplyStateToStateMachine(PlayState aState)
+MediaOmxCommonDecoder::CallSeek(const SeekTarget& aTarget)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  // During offload playback, state machine should be in dormant state.
-  // ApplyStateToStateMachine() can change state machine state to
-  // something else or reset the seek time. So don't call this when audio is
-  // offloaded
   if (!mAudioOffloadPlayer) {
-    MediaDecoder::ApplyStateToStateMachine(aState);
+    MediaDecoder::CallSeek(aTarget);
+    return;
   }
+
+  mSeekRequest.DisconnectIfExists();
+  mSeekRequest.Begin(mAudioOffloadPlayer->Seek(aTarget)
+    ->RefableThen(AbstractThread::MainThread(), __func__, static_cast<MediaDecoder*>(this),
+                  &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
 }
 
 void
 MediaOmxCommonDecoder::PlaybackPositionChanged(MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mAudioOffloadPlayer) {
     MediaDecoder::PlaybackPositionChanged();
--- a/dom/media/omx/MediaOmxCommonDecoder.h
+++ b/dom/media/omx/MediaOmxCommonDecoder.h
@@ -19,33 +19,33 @@ class AudioOffloadPlayerBase;
 class MediaOmxCommonReader;
 
 class MediaOmxCommonDecoder : public MediaDecoder
 {
 public:
   MediaOmxCommonDecoder();
 
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
-                                MediaDecoderEventVisibility aEventVisibility);
-  virtual void ChangeState(PlayState aState);
-  virtual void ApplyStateToStateMachine(PlayState aState);
-  virtual void SetVolume(double aVolume);
+                                MediaDecoderEventVisibility aEventVisibility) override;
+  virtual void ChangeState(PlayState aState) override;
+  virtual void CallSeek(const SeekTarget& aTarget) override;
+  virtual void SetVolume(double aVolume) override;
   virtual void PlaybackPositionChanged(MediaDecoderEventVisibility aEventVisibility =
-                                         MediaDecoderEventVisibility::Observable);
+                                         MediaDecoderEventVisibility::Observable) override;
   virtual MediaDecoderOwner::NextFrameStatus NextFrameStatus() override;
-  virtual void SetElementVisibility(bool aIsVisible);
-  virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio);
-  virtual bool CheckDecoderCanOffloadAudio();
+  virtual void SetElementVisibility(bool aIsVisible) override;
+  virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) override;
+  virtual bool CheckDecoderCanOffloadAudio() override;
   virtual void AddOutputStream(ProcessedMediaStream* aStream,
-                               bool aFinishWhenEnded);
-  virtual void SetPlaybackRate(double aPlaybackRate);
+                               bool aFinishWhenEnded) override;
+  virtual void SetPlaybackRate(double aPlaybackRate) override;
 
   void AudioOffloadTearDown();
 
-  virtual MediaDecoderStateMachine* CreateStateMachine();
+  virtual MediaDecoderStateMachine* CreateStateMachine() override;
 
   virtual MediaOmxCommonReader* CreateReader() = 0;
   virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader) = 0;
 
 protected:
   virtual ~MediaOmxCommonDecoder();
   void PauseStateMachine();
   void ResumeStateMachine();
--- a/dom/media/omx/RtspMediaCodecDecoder.cpp
+++ b/dom/media/omx/RtspMediaCodecDecoder.cpp
@@ -27,25 +27,25 @@ RtspMediaCodecDecoder::CreateReader()
 MediaDecoderStateMachine*
 RtspMediaCodecDecoder::CreateStateMachineFromReader(MediaOmxCommonReader* aReader)
 {
   return new MediaDecoderStateMachine(this, aReader,
                                       mResource->IsRealTime());
 }
 
 void
-RtspMediaCodecDecoder::ApplyStateToStateMachine(PlayState aState)
+RtspMediaCodecDecoder::ChangeState(PlayState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  MediaDecoder::ApplyStateToStateMachine(aState);
+  MediaDecoder::ChangeState(aState);
 
   // Notify RTSP controller if the play state is ended.
   // This is necessary for RTSP controller to transit its own state.
-  if (aState == PLAY_STATE_ENDED) {
+  if (mPlayState == PLAY_STATE_ENDED) {
     nsRefPtr<RtspMediaResource> resource = mResource->GetRtspPointer();
     if (resource) {
       nsIStreamingProtocolController* controller =
         resource->GetMediaStreamController();
       if (controller) {
         controller->PlaybackEnded();
       }
     }
--- a/dom/media/omx/RtspMediaCodecDecoder.h
+++ b/dom/media/omx/RtspMediaCodecDecoder.h
@@ -15,14 +15,14 @@ class RtspMediaCodecDecoder final : publ
 {
 public:
   virtual MediaDecoder* Clone() override;
 
   virtual MediaOmxCommonReader* CreateReader() override;
 
   virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader) override;
 
-  virtual void ApplyStateToStateMachine(PlayState aState) override;
+  virtual void ChangeState(PlayState aState) override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/omx/RtspOmxDecoder.cpp
+++ b/dom/media/omx/RtspOmxDecoder.cpp
@@ -11,32 +11,34 @@
 
 namespace mozilla {
 
 MediaDecoder* RtspOmxDecoder::Clone()
 {
   return new RtspOmxDecoder();
 }
 
-MediaDecoderStateMachine* RtspOmxDecoder::CreateStateMachine()
+MediaDecoderStateMachine*
+RtspOmxDecoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this,
                                       new RtspOmxReader(this),
                                       mResource->IsRealTime());
 }
 
-void RtspOmxDecoder::ApplyStateToStateMachine(PlayState aState)
+void
+RtspOmxDecoder::ChangeState(PlayState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  MediaDecoder::ApplyStateToStateMachine(aState);
+  MediaDecoder::ChangeState(aState);
 
   // Notify RTSP controller if the play state is ended.
   // This is necessary for RTSP controller to transit its own state.
-  if (aState == PLAY_STATE_ENDED) {
+  if (mPlayState == PLAY_STATE_ENDED) {
     nsRefPtr<RtspMediaResource> resource = mResource->GetRtspPointer();
     if (resource) {
       nsIStreamingProtocolController* controller =
         resource->GetMediaStreamController();
       if (controller) {
         controller->PlaybackEnded();
       }
     }
--- a/dom/media/omx/RtspOmxDecoder.h
+++ b/dom/media/omx/RtspOmxDecoder.h
@@ -27,15 +27,14 @@ public:
   }
 
   ~RtspOmxDecoder() {
     MOZ_COUNT_DTOR(RtspOmxDecoder);
   }
 
   virtual MediaDecoder* Clone() override final;
   virtual MediaDecoderStateMachine* CreateStateMachine() override final;
-  virtual void ApplyStateToStateMachine(PlayState aState)
-                                        override final;
+  virtual void ChangeState(PlayState aState) override final;
 };
 
 } // namespace mozilla
 
 #endif