Bug 1139206 - Update AudioOffloadPlayer seek r=bholley,bwu
authorSotaro Ikeda <sikeda@mozilla.com>
Wed, 15 Apr 2015 14:00:41 -0700
changeset 258325 b49fbd3ad1b1d8c6611c12cfdd052558387716dd
parent 258324 e4d3753af8a2a3ae8d28469e4e191c27cf499343
child 258326 0761dba193080f2f7043dc8d465b5ad65dda57b8
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, bwu
bugs1139206
milestone40.0a1
Bug 1139206 - Update AudioOffloadPlayer seek r=bholley,bwu
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/omx/AudioOffloadPlayer.cpp
dom/media/omx/AudioOffloadPlayer.h
dom/media/omx/AudioOffloadPlayerBase.h
dom/media/omx/MediaOmxCommonDecoder.cpp
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1218,49 +1218,50 @@ void MediaDecoder::UpdateReadyStateForDa
   if (!mOwner || mShuttingDown || !mDecoderStateMachine) {
     return;
   }
   MediaDecoderOwner::NextFrameStatus frameStatus =
     mDecoderStateMachine->GetNextFrameStatus();
   mOwner->UpdateReadyStateForData(frameStatus);
 }
 
-void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
+void MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
 {
   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 = aAtEnd;
-      if (aAtEnd) {
+      fireEnded = aVal.mAtEnd;
+      if (aVal.mAtEnd) {
         ChangeState(PLAY_STATE_ENDED);
-      } else if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
-        ChangeState(aAtEnd ? PLAY_STATE_ENDED : mNextState);
+      } else if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
+        ChangeState(aVal.mAtEnd ? PLAY_STATE_ENDED : mNextState);
       }
     }
   }
 
-  PlaybackPositionChanged(aEventVisibility);
+  PlaybackPositionChanged(aVal.mEventVisibility);
 
   if (mOwner) {
     UpdateReadyStateForData();
-    if (!seekWasAborted && (aEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
+    if (!seekWasAborted && (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
       mOwner->SeekCompleted();
       if (fireEnded) {
         mOwner->PlaybackEnded();
       }
     }
   }
 }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -804,31 +804,17 @@ 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 OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility);
-
-  void OnSeekResolved(SeekResolveValue aVal)
-  {
-    mSeekRequest.Complete();
-    OnSeekResolvedInternal(aVal.mAtEnd, aVal.mEventVisibility);
-  }
-
-#ifdef MOZ_AUDIO_OFFLOAD
-  // Temporary hack - see bug 1139206.
-  void SimulateSeekResolvedForAudioOffload(MediaDecoderEventVisibility aEventVisibility)
-  {
-    OnSeekResolvedInternal(false, aEventVisibility);
-  }
-#endif
+  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.
   // This must be called on the main thread only.
--- a/dom/media/omx/AudioOffloadPlayer.cpp
+++ b/dom/media/omx/AudioOffloadPlayer.cpp
@@ -52,23 +52,20 @@ PRLogModuleInfo* gAudioOffloadPlayerLog;
 
 // maximum time in paused state when offloading audio decompression.
 // When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
 static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
 
 AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
   mStarted(false),
   mPlaying(false),
-  mSeeking(false),
   mReachedEOS(false),
-  mSeekDuringPause(false),
   mIsElementVisible(true),
   mSampleRate(0),
   mStartPosUs(0),
-  mSeekTimeUs(0),
   mPositionTimeMediaUs(-1),
   mInputBuffer(nullptr),
   mObserver(aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
 #ifdef PR_LOGGING
   if (!gAudioOffloadPlayerLog) {
@@ -194,23 +191,16 @@ status_t AudioOffloadPlayer::ChangeState
     case MediaDecoder::PLAY_STATE_PLAYING: {
       status_t err = Play();
       if (err != OK) {
         return err;
       }
       StartTimeUpdate();
     } break;
 
-    case MediaDecoder::PLAY_STATE_SEEKING: {
-      int64_t seekTimeUs
-          = mObserver->GetSeekTime();
-      SeekTo(seekTimeUs, true);
-      mObserver->ResetSeekTime();
-    } break;
-
     case MediaDecoder::PLAY_STATE_PAUSED:
     case MediaDecoder::PLAY_STATE_SHUTDOWN:
       // Just pause here during play state shutdown as well to stop playing
       // offload track immediately. Resources will be freed by
       // MediaOmxCommonDecoder
       Pause();
       break;
 
@@ -273,18 +263,22 @@ status_t AudioOffloadPlayer::Play()
 
   if (!mStarted) {
     // Last pause timed out and offloaded audio sink was reset. Start it again
     err = Start(false);
     if (err != OK) {
       return err;
     }
     // Seek to last play position only when there was no seek during last pause
-    if (!mSeeking) {
-      SeekTo(mPositionTimeMediaUs);
+    android::Mutex::Autolock autoLock(mLock);
+    if (!mSeekTarget.IsValid()) {
+      mSeekTarget = SeekTarget(mPositionTimeMediaUs,
+                               SeekTarget::Accurate,
+                               MediaDecoderEventVisibility::Suppressed);
+      DoSeek();
     }
   }
 
   if (!mPlaying) {
     CHECK(mAudioSink.get());
     err = mAudioSink->Start();
     if (err == OK) {
       mPlaying = true;
@@ -338,62 +332,64 @@ void AudioOffloadPlayer::Reset()
   mReachedEOS = false;
   mStarted = false;
   mPlaying = false;
   mStartPosUs = 0;
 
   WakeLockRelease();
 }
 
-status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
+nsRefPtr<MediaDecoder::SeekPromise> AudioOffloadPlayer::Seek(SeekTarget aTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  CHECK(mAudioSink.get());
-
   android::Mutex::Autolock autoLock(mLock);
 
-  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs));
+  mSeekPromise.RejectIfExists(true, __func__);
+  mSeekTarget = aTarget;
+  nsRefPtr<MediaDecoder::SeekPromise> p = mSeekPromise.Ensure(__func__);
+  DoSeek();
+  return p;
+}
 
-  mSeeking = true;
+status_t AudioOffloadPlayer::DoSeek()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mSeekTarget.IsValid());
+  CHECK(mAudioSink.get());
+
+  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("DoSeek ( %lld )", mSeekTarget.mTime));
+
   mReachedEOS = false;
   mPositionTimeMediaUs = -1;
-  mSeekTimeUs = aTimeUs;
-  mStartPosUs = aTimeUs;
-  mDispatchSeekEvents = aDispatchSeekEvents;
+  mStartPosUs = mSeekTarget.mTime;
 
-  if (mDispatchSeekEvents) {
+  if (!mSeekPromise.IsEmpty()) {
     nsCOMPtr<nsIRunnable> nsEvent =
       NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
         mObserver,
         &MediaDecoder::SeekingStarted,
-        MediaDecoderEventVisibility::Observable);
+        mSeekTarget.mEventVisibility);
     NS_DispatchToCurrentThread(nsEvent);
   }
 
   if (mPlaying) {
     mAudioSink->Pause();
     mAudioSink->Flush();
     mAudioSink->Start();
 
   } else {
-    mSeekDuringPause = true;
-
     if (mStarted) {
       mAudioSink->Flush();
     }
 
-    if (mDispatchSeekEvents) {
-      mDispatchSeekEvents = false;
+    if (!mSeekPromise.IsEmpty()) {
       AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause"));
-      nsCOMPtr<nsIRunnable> nsEvent =
-        NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
-          mObserver,
-          &MediaDecoder::SimulateSeekResolvedForAudioOffload,
-          MediaDecoderEventVisibility::Observable);
-      NS_DispatchToCurrentThread(nsEvent);
+      // We do not reset mSeekTarget here.
+      MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+      mSeekPromise.Resolve(val, __func__);
     }
   }
 
   return OK;
 }
 
 double AudioOffloadPlayer::GetMediaTimeSecs()
 {
@@ -402,18 +398,18 @@ double AudioOffloadPlayer::GetMediaTimeS
       static_cast<double>(USECS_PER_S));
 }
 
 int64_t AudioOffloadPlayer::GetMediaTimeUs()
 {
   android::Mutex::Autolock autoLock(mLock);
 
   int64_t playPosition = 0;
-  if (mSeeking) {
-    return mSeekTimeUs;
+  if (mSeekTarget.IsValid()) {
+    return mSeekTarget.mTime;
   }
   if (!mStarted) {
     return mPositionTimeMediaUs;
   }
 
   playPosition = GetOutputPlayPositionUs_l();
   if (!mReachedEOS) {
     mPositionTimeMediaUs = playPosition;
@@ -434,16 +430,22 @@ int64_t AudioOffloadPlayer::GetOutputPla
 
   // HAL position is relative to the first buffer we sent at mStartPosUs
   const int64_t renderedDuration = mStartPosUs + playedUs;
   return renderedDuration;
 }
 
 void AudioOffloadPlayer::NotifyAudioEOS()
 {
+  android::Mutex::Autolock autoLock(mLock);
+  // We do not reset mSeekTarget here.
+  if (!mSeekPromise.IsEmpty()) {
+    MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+    mSeekPromise.Resolve(val, __func__);
+  }
   nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
       &MediaDecoder::PlaybackEnded);
   NS_DispatchToMainThread(nsEvent);
 }
 
 void AudioOffloadPlayer::NotifyPositionChanged()
 {
   nsCOMPtr<nsIRunnable> nsEvent =
@@ -451,16 +453,25 @@ void AudioOffloadPlayer::NotifyPositionC
       mObserver,
       &MediaOmxCommonDecoder::PlaybackPositionChanged,
       MediaDecoderEventVisibility::Observable);
   NS_DispatchToMainThread(nsEvent);
 }
 
 void AudioOffloadPlayer::NotifyAudioTearDown()
 {
+  // Fallback to state machine.
+  // state machine's seeks will be done with
+  // MediaDecoderEventVisibility::Suppressed.
+  android::Mutex::Autolock autoLock(mLock);
+  // We do not reset mSeekTarget here.
+  if (!mSeekPromise.IsEmpty()) {
+    MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+    mSeekPromise.Resolve(val, __func__);
+  }
   nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
       &MediaOmxCommonDecoder::AudioOffloadTearDown);
   NS_DispatchToMainThread(nsEvent);
 }
 
 // static
 size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink,
                                              void* aBuffer,
@@ -501,45 +512,47 @@ size_t AudioOffloadPlayer::FillBuffer(vo
   CHECK(mAudioSink.get());
 
   if (mReachedEOS) {
     return 0;
   }
 
   size_t sizeDone = 0;
   size_t sizeRemaining = aSize;
+  int64_t seekTimeUs = -1;
   while (sizeRemaining > 0) {
     MediaSource::ReadOptions options;
     bool refreshSeekTime = false;
-
     {
       android::Mutex::Autolock autoLock(mLock);
 
-      if (mSeeking) {
-        options.setSeekTo(mSeekTimeUs);
+      if (mSeekTarget.IsValid()) {
+        seekTimeUs = mSeekTarget.mTime;
+        options.setSeekTo(seekTimeUs);
         refreshSeekTime = true;
 
         if (mInputBuffer) {
           mInputBuffer->release();
           mInputBuffer = nullptr;
         }
-        mSeeking = false;
       }
     }
 
     if (!mInputBuffer) {
-
       status_t err;
       err = mSource->read(&mInputBuffer, &options);
 
       CHECK((!err && mInputBuffer) || (err && !mInputBuffer));
 
       android::Mutex::Autolock autoLock(mLock);
 
       if (err != OK) {
+        if (mSeekTarget.IsValid()) {
+          mSeekTarget.Reset();
+        }
         AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d "
             "Ok to receive EOS error at end", err));
         if (!mReachedEOS) {
           // After seek there is a possible race condition if
           // OffloadThread is observing state_stopping_1 before
           // framesReady() > 0. Ensure sink stop is called
           // after last buffer is released. This ensures the
           // partial buffer is written to the driver before
@@ -559,50 +572,36 @@ size_t AudioOffloadPlayer::FillBuffer(vo
         break;
       }
 
       if(mInputBuffer->range_length() != 0) {
         CHECK(mInputBuffer->meta_data()->findInt64(
             kKeyTime, &mPositionTimeMediaUs));
       }
 
-      if (refreshSeekTime) {
-        if (mDispatchSeekEvents && !mSeekDuringPause) {
-          mDispatchSeekEvents = false;
+      if (mSeekTarget.IsValid() && seekTimeUs == mSeekTarget.mTime) {
+        MOZ_ASSERT(mSeekTarget.IsValid());
+        mSeekTarget.Reset();
+        if (!mSeekPromise.IsEmpty()) {
           AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE"));
-          nsCOMPtr<nsIRunnable> nsEvent =
-            NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
-              mObserver,
-              &MediaDecoder::SimulateSeekResolvedForAudioOffload,
-              MediaDecoderEventVisibility::Observable);
-          NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
+          MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+          mSeekPromise.Resolve(val, __func__);
+        }
+      } else if (mSeekTarget.IsValid()) {
+        AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("seek is updated during unlocking mLock"));
+      }
 
-        } else if (mSeekDuringPause) {
-          // Callback is already called for seek during pause. Just reset the
-          // flag
-          AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its"
-              " already faked"));
-          mSeekDuringPause = false;
-        }
-
+      if (refreshSeekTime) {
         NotifyPositionChanged();
 
         // need to adjust the mStartPosUs for offload decoding since parser
         // might not be able to get the exact seek time requested.
         mStartPosUs = mPositionTimeMediaUs;
         AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f",
             mStartPosUs / 1E6));
-
-        // clear seek time with mLock locked and once we have valid
-        // mPositionTimeMediaUs
-        // before clearing mSeekTimeUs check if a new seek request has been
-        // received while we were reading from the source with mLock released.
-        if (!mSeeking) {
-          mSeekTimeUs = 0;
-        }
       }
     }
 
     if (mInputBuffer->range_length() == 0) {
       mInputBuffer->release();
       mInputBuffer = nullptr;
       continue;
     }
--- a/dom/media/omx/AudioOffloadPlayer.h
+++ b/dom/media/omx/AudioOffloadPlayer.h
@@ -73,72 +73,58 @@ public:
     SEEK_COMPLETE
   };
 
   AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr);
 
   ~AudioOffloadPlayer();
 
   // Caller retains ownership of "aSource".
-  void SetSource(const android::sp<MediaSource> &aSource);
+  virtual void SetSource(const android::sp<MediaSource> &aSource) override;
 
   // Start the source if it's not already started and open the AudioSink to
   // create an offloaded audio track
-  status_t Start(bool aSourceAlreadyStarted = false);
+  virtual status_t Start(bool aSourceAlreadyStarted = false) override;
+
+  virtual status_t ChangeState(MediaDecoder::PlayState aState) override;
 
-  double GetMediaTimeSecs();
+  virtual void SetVolume(double aVolume) override;
+
+  virtual double GetMediaTimeSecs() override;
 
   // To update progress bar when the element is visible
-  void SetElementVisibility(bool aIsVisible);
-
-  status_t ChangeState(MediaDecoder::PlayState aState);
-
-  void SetVolume(double aVolume);
+  virtual void SetElementVisibility(bool aIsVisible) override;;
 
   // Update ready state based on current play state. Not checking data
   // availability since offloading is currently done only when whole compressed
   // data is available
-  MediaDecoderOwner::NextFrameStatus GetNextFrameStatus();
+  virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus() override;
+
+  virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) override;
 
   void TimeUpdate();
 
   // Close the audio sink, stop time updates, frees the input buffers
   void Reset();
 
 private:
   // Set when audio source is started and audioSink is initialized
   // Used only in main thread
   bool mStarted;
 
   // Set when audio sink is started. i.e. playback started
   // Used only in main thread
   bool mPlaying;
 
-  // Set when playstate is seeking and reset when FillBUffer() acknowledged
-  // seeking by seeking audio source. Used in main thread and offload
-  // callback thread, protected by Mutex mLock
-  bool mSeeking;
-
   // Once playback reached end of stream (last ~100ms), position provided by DSP
   // may be reset/corrupted. This bool is used to avoid that.
   // Used in main thread and offload callback thread, protected by Mutex
   // mLock
   bool mReachedEOS;
 
-  // Set when there is a seek request during pause.
-  // Used in main thread and offload callback thread, protected by Mutex
-  // mLock
-  bool mSeekDuringPause;
-
-  // Seek can be triggered internally or by MediaDecoder. This bool is to
-  // to track seek triggered by MediaDecoder so that we can send back
-  // SeekingStarted and SeekingStopped events.
-  // Used in main thread and offload callback thread, protected by Mutex mLock
-  bool mDispatchSeekEvents;
-
   // Set when the HTML Audio Element is visible to the user.
   // Used only in main thread
   bool mIsElementVisible;
 
   // Session id given by Android::AudioSystem and used while creating audio sink
   // Used only in main thread
   int mSessionId;
 
@@ -150,20 +136,25 @@ private:
   // different than given mSeekTimeUs, if audio source cannot find a frame at
   // that position. Store seeked position in mStartPosUs and provide
   // mStartPosUs + GetPosition() (i.e. absolute position) to
   // MediaOmxCommonDecoder
   // Used in main thread and offload callback thread, protected by Mutex
   // mLock
   int64_t mStartPosUs;
 
-  // Given seek time when there is a request to seek
+  // The target of current seek when there is a request to seek
   // Used in main thread and offload callback thread, protected by Mutex
   // mLock
-  int64_t mSeekTimeUs;
+  SeekTarget mSeekTarget;
+
+  // MediaPromise of current seek.
+  // Used in main thread and offload callback thread, protected by Mutex
+  // mLock
+  MediaPromiseHolder<MediaDecoder::SeekPromise> mSeekPromise;
 
   // Positions obtained from offlaoded tracks (DSP)
   // Used in main thread and offload callback thread, protected by Mutex
   // mLock
   int64_t mPositionTimeMediaUs;
 
   // State obtained from MediaOmxCommonDecoder. Used only in main thread
   MediaDecoder::PlayState mPlayState;
@@ -216,25 +207,25 @@ private:
   static size_t AudioSinkCallback(AudioSink *aAudioSink,
                                   void *aData,
                                   size_t aSize,
                                   void *aMe,
                                   AudioSink::cb_event_t aEvent);
 
   bool IsSeeking();
 
-  // Set mSeekTime to the given position and restart the sink. Actual seek
-  // happens in FillBuffer(). If aDispatchSeekEvents is true, send
+  // Set mSeekTarget to the given position and restart the sink. Actual seek
+  // happens in FillBuffer(). If mSeekPromise is not empty, send
   // SeekingStarted event always and SeekingStopped event when the play state is
   // paused to MediaDecoder.
   // When decoding and playing happens separately, if there is a seek during
   // pause, we can decode and keep data ready.
   // In case of offload player, no way to seek during pause. So just fake that
   // seek is done.
-  status_t SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents = false);
+  status_t DoSeek();
 
   // Start/Resume the audio sink so that callback will start being called to get
   // compressed data
   status_t Play();
 
   // Stop the audio sink if we need to play till we drain the current buffer.
   // or Pause the sink in case we should stop playing immediately
   void Pause(bool aPlayPendingSamples = false);
--- a/dom/media/omx/AudioOffloadPlayerBase.h
+++ b/dom/media/omx/AudioOffloadPlayerBase.h
@@ -61,13 +61,15 @@ public:
 
   // Update ready state based on current play state. Not checking data
   // availability since offloading is currently done only when whole compressed
   // data is available
   virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus()
   {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
   }
+
+  virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0;
 };
 
 } // namespace mozilla
 
 #endif // AUDIO_OFFLOAD_PLAYER_BASE_H_
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -121,18 +121,19 @@ MediaOmxCommonDecoder::ResumeStateMachin
   if (!mDecoderStateMachine) {
     return;
   }
 
   mFallbackToStateMachine = true;
   mAudioOffloadPlayer = nullptr;
   int64_t timeUsecs = 0;
   SecondsToUsecs(mCurrentTime, timeUsecs);
-  mRequestedSeekTarget = SeekTarget(timeUsecs, SeekTarget::Accurate);
-
+  mRequestedSeekTarget = SeekTarget(timeUsecs,
+                                    SeekTarget::Accurate,
+                                    MediaDecoderEventVisibility::Suppressed);
   mNextState = mPlayState;
   ChangeState(PLAY_STATE_LOADING);
   // exit dormant state
   RefPtr<nsRunnable> event =
     NS_NewRunnableMethodWithArg<bool>(
       mDecoderStateMachine,
       &MediaDecoderStateMachine::SetDormant,
       false);
@@ -188,20 +189,35 @@ void
 MediaOmxCommonDecoder::ChangeState(PlayState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Keep MediaDecoder state in sync with MediaElement irrespective of offload
   // playback so it will continue to work in normal mode when offloading fails
   // in between
   MediaDecoder::ChangeState(aState);
 
-  if (mAudioOffloadPlayer) {
-    status_t err = mAudioOffloadPlayer->ChangeState(aState);
-    if (err != OK) {
-      ResumeStateMachine();
+  if (!mAudioOffloadPlayer) {
+    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)
 {
   MOZ_ASSERT(NS_IsMainThread());