Bug 1266027 part 2 - make MDSM and SeekTask to adopt new MediaDecoderReaderWrapper API; r=jwwang draft
authorKaku Kuo <tkuo@mozilla.com>
Wed, 27 Apr 2016 14:50:39 +0800
changeset 357708 c1be6737905c366fd63938074ef93ec3fb602e44
parent 357707 ab63d785f670724b942468d31a6a6d97d8c0db70
child 519679 6e0ebe553ef3178df849ef294dece886eed6806b
push id16817
push usertkuo@mozilla.com
push dateFri, 29 Apr 2016 03:54:15 +0000
reviewersjwwang
bugs1266027
milestone49.0a1
Bug 1266027 part 2 - make MDSM and SeekTask to adopt new MediaDecoderReaderWrapper API; r=jwwang MozReview-Commit-ID: LqZ59A8vju1
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/SeekTask.cpp
dom/media/SeekTask.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -351,16 +351,19 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
+
+  // Configure MediaDecoderReaderWrapper.
+  SetMediaDecoderReaderWrapperCallback();
 }
 
 media::MediaSink*
 MediaDecoderStateMachine::CreateAudioSink()
 {
   RefPtr<MediaDecoderStateMachine> self = this;
   auto audioSinkCreator = [self] () {
     MOZ_ASSERT(self->OnTaskQueue());
@@ -559,17 +562,16 @@ MediaDecoderStateMachine::NeedToDecodeAu
 void
 MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   RefPtr<MediaData> audio(aAudioSample);
   MOZ_ASSERT(audio);
-  mAudioDataRequest.Complete();
 
   // audio->GetEndTime() is not always mono-increasing in chained ogg.
   mDecodedAudioEndTime = std::max(audio->GetEndTime(), mDecodedAudioEndTime);
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
              (audio ? audio->mTime : -1),
              (audio ? audio->GetEndTime() : -1),
              (audio ? audio->mDiscontinuity : 0));
@@ -673,21 +675,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   SAMPLE_LOG("OnNotDecoded (aType=%u, aReason=%u)", aType, aReason);
   bool isAudio = aType == MediaData::AUDIO_DATA;
   MOZ_ASSERT_IF(!isAudio, aType == MediaData::VIDEO_DATA);
 
-  if (isAudio) {
-    mAudioDataRequest.Complete();
-  } else {
-    mVideoDataRequest.Complete();
-  }
   if (IsShutdown()) {
     // Already shutdown;
     return;
   }
 
   // If this is a decode error, delegate to the generic error path.
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     DecodeError();
@@ -792,17 +789,16 @@ void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   RefPtr<MediaData> video(aVideoSample);
   MOZ_ASSERT(video);
-  mVideoDataRequest.Complete();
 
   // Handle abnormal or negative timestamps.
   mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, video->GetEndTime());
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
              (video ? video->GetEndTime() : -1),
              (video ? video->mDiscontinuity : 0));
@@ -926,16 +922,43 @@ nsresult MediaDecoderStateMachine::Init(
   NS_ENSURE_SUCCESS(rv, rv);
 
   r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ReadMetadata);
   OwnerThread()->Dispatch(r.forget());
 
   return NS_OK;
 }
 
+void
+MediaDecoderStateMachine::SetMediaDecoderReaderWrapperCallback()
+{
+  mAudioCallbackID =
+    mReader->SetAudioCallback(this,
+                              &MediaDecoderStateMachine::OnAudioDecoded,
+                              &MediaDecoderStateMachine::OnAudioNotDecoded);
+
+  mVideoCallbackID =
+    mReader->SetVideoCallback(this,
+                              &MediaDecoderStateMachine::OnVideoDecoded,
+                              &MediaDecoderStateMachine::OnVideoNotDecoded);
+
+  DECODER_LOG("MDSM set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
+  DECODER_LOG("MDSM set video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
+}
+
+void
+MediaDecoderStateMachine::CancelMediaDecoderReaderWrapperCallback()
+{
+    DECODER_LOG("MDSM cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
+    mReader->CancelAudioCallback(mAudioCallbackID);
+
+    DECODER_LOG("MDSM cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
+    mReader->CancelVideoCallback(mVideoCallbackID);
+}
+
 void MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("StopPlayback()");
 
   mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped);
 
   if (IsPlaying()) {
@@ -1148,20 +1171,17 @@ MediaDecoderStateMachine::SetDormant(boo
                                        SeekTarget::Accurate,
                                        MediaDecoderEventVisibility::Suppressed);
       // XXXbholley - Nobody is listening to this promise. Do we need to pass it
       // back to MediaDecoder when we come out of dormant?
       RefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
     }
 
     // Discard the current seek task.
-    if (mSeekTask) {
-      mSeekTask->Discard();
-      mSeekTask = nullptr;
-    }
+    DiscardSeekTaskIfExist();
 
     SetState(DECODER_STATE_DORMANT);
     if (IsPlaying()) {
       StopPlayback();
     }
 
     Reset();
 
@@ -1188,29 +1208,30 @@ MediaDecoderStateMachine::Shutdown()
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
   ScheduleStateMachine();
   SetState(DECODER_STATE_SHUTDOWN);
 
   mBufferedUpdateRequest.DisconnectIfExists();
 
   mQueuedSeek.RejectIfExists(__func__);
-  if (mSeekTask) {
-    mSeekTask->Discard();
-    mSeekTask = nullptr;
-  }
+
+  DiscardSeekTaskIfExist();
 
 #ifdef MOZ_EME
   mCDMProxyPromise.DisconnectIfExists();
 #endif
 
   if (IsPlaying()) {
     StopPlayback();
   }
 
+  // To break the cycle-reference between MediaDecoderReaderWrapper and MDSM.
+  CancelMediaDecoderReaderWrapperCallback();
+
   Reset();
 
   mMediaSink->Shutdown();
 
   DECODER_LOG("Shutdown started");
 
   // Put a task in the decode queue to shutdown the reader.
   // the queue to spin down.
@@ -1462,22 +1483,23 @@ MediaDecoderStateMachine::DispatchDecode
 }
 
 void
 MediaDecoderStateMachine::InitiateSeek(SeekJob aSeekJob)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Discard the existing seek task.
-  if (mSeekTask) {
-    mSeekTask->Discard();
-  }
+  DiscardSeekTaskIfExist();
 
   mSeekTaskRequest.DisconnectIfExists();
 
+  // SeekTask will register its callbacks to MediaDecoderReaderWrapper.
+  CancelMediaDecoderReaderWrapperCallback();
+
   // Create a new SeekTask instance for the incoming seek task.
   mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(),
                                        mReader.get(), Move(aSeekJob),
                                        mInfo, Duration(), GetMediaTime());
 
   // Stop playback now to ensure that while we're outside the monitor
   // dispatching SeekingStarted, playback doesn't advance and mess with
   // mCurrentPosition that we've setting to seekTime here.
@@ -1530,19 +1552,16 @@ MediaDecoderStateMachine::OnSeekTaskReso
     StopPrerollingAudio();
   }
 
   if (aValue.mNeedToStopPrerollingVideo) {
     StopPrerollingVideo();
   }
 
   SeekCompleted();
-
-  mSeekTask->Discard();
-  mSeekTask = nullptr;
 }
 
 void
 MediaDecoderStateMachine::OnSeekTaskRejected(SeekTaskRejectValue aValue)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
 
@@ -1563,18 +1582,29 @@ MediaDecoderStateMachine::OnSeekTaskReje
   }
 
   if (aValue.mNeedToStopPrerollingVideo) {
     StopPrerollingVideo();
   }
 
   DecodeError();
 
-  mSeekTask->Discard();
-  mSeekTask = nullptr;
+  DiscardSeekTaskIfExist();
+}
+
+void
+MediaDecoderStateMachine::DiscardSeekTaskIfExist()
+{
+  if (mSeekTask) {
+    mSeekTask->Discard();
+    mSeekTask = nullptr;
+
+    // Reset the MediaDecoderReaderWrapper's callbask.
+    SetMediaDecoderReaderWrapperCallback();
+  }
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (IsShutdown()) {
@@ -1597,17 +1627,17 @@ MediaDecoderStateMachine::EnsureAudioDec
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
               IsAudioDecoding(), AudioRequestStatus());
 
   if (mState != DECODER_STATE_DECODING &&
       mState != DECODER_STATE_BUFFERING) {
     return NS_OK;
   }
 
-  if (!IsAudioDecoding() || mAudioDataRequest.Exists() ||
+  if (!IsAudioDecoding() || mReader->IsRequestingAudioData() ||
       mAudioWaitRequest.Exists()) {
     return NS_OK;
   }
 
   RequestAudioData();
   return NS_OK;
 }
 
@@ -1615,21 +1645,17 @@ void
 MediaDecoderStateMachine::RequestAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
-  mAudioDataRequest.Begin(
-    mReader->RequestAudioData()
-    ->Then(OwnerThread(), __func__, this,
-           &MediaDecoderStateMachine::OnAudioDecoded,
-           &MediaDecoderStateMachine::OnAudioNotDecoded));
+  mReader->RequestAudioData();
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (IsShutdown()) {
@@ -1652,55 +1678,45 @@ MediaDecoderStateMachine::EnsureVideoDec
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
              IsVideoDecoding(), VideoRequestStatus());
 
   if (mState != DECODER_STATE_DECODING &&
       mState != DECODER_STATE_BUFFERING) {
     return NS_OK;
   }
 
-  if (!IsVideoDecoding() || mVideoDataRequest.Exists() ||
+  if (!IsVideoDecoding() || mReader->IsRequestingVidoeData() ||
       mVideoWaitRequest.Exists()) {
     return NS_OK;
   }
 
   RequestVideoData();
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::RequestVideoData()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
-  // Time the video decode, so that if it's slow, we can increase our low
-  // audio threshold to reduce the chance of an audio underrun while we're
-  // waiting for a video decode to complete.
-  TimeStamp videoDecodeStartTime = TimeStamp::Now();
-
   bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent &&
     NeedToSkipToNextKeyframe();
 
   media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(GetMediaTime());
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
              currentTime.ToMicroseconds());
 
-  RefPtr<MediaDecoderStateMachine> self = this;
-  mVideoDataRequest.Begin(
-    mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
-    ->Then(OwnerThread(), __func__,
-           [self, videoDecodeStartTime] (MediaData* aVideoSample) {
-             self->OnVideoDecoded(aVideoSample, videoDecodeStartTime);
-           },
-           [self] (MediaDecoderReader::NotDecodedReason aReason) {
-             self->OnVideoNotDecoded(aReason);
-           }));
+  // MediaDecoderReaderWrapper::RequestVideoData() records the decoding start
+  // time and sent it back to MDSM::OnVideoDecoded() so that if the decoding is
+  // slow, we can increase our low audio threshold to reduce the chance of an
+  // audio underrun while we're waiting for a video decode to complete.
+  mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
 }
 
 void
 MediaDecoderStateMachine::StartMediaSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMediaSink->IsStarted()) {
     mAudioCompleted = false;
@@ -2011,16 +2027,25 @@ MediaDecoderStateMachine::SeekCompleted(
     DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime);
     nextState = DECODER_STATE_DECODING;
   }
 
   // We want to resolve the seek request prior finishing the first frame
   // to ensure that the seeked event is fired prior loadeded.
   mSeekTask->GetSeekJob().Resolve(nextState == DECODER_STATE_COMPLETED, __func__);
 
+  // Discard and nullify the seek task.
+  // Reset the MediaDecoderReaderWrapper's callbask.
+  DiscardSeekTaskIfExist();
+
+  // NOTE: Discarding the mSeekTask must be done before here. The following code
+  // might ask the MediaDecoderReaderWrapper to request media data, however, the
+  // SeekTask::Discard() will ask MediaDecoderReaderWrapper to discard media
+  // data requests.
+
   if (mDecodingFirstFrame) {
     // We were resuming from dormant, or initiated a seek early.
     // We can fire loadeddata now.
     FinishDecodeFirstFrame();
   }
 
   if (nextState == DECODER_STATE_DECODING) {
     StartDecoding();
@@ -2165,18 +2190,18 @@ nsresult MediaDecoderStateMachine::RunSt
                       (mQuickBuffering ? "(quick exit)" : ""));
           ScheduleStateMachineIn(USECS_PER_S);
           return NS_OK;
         }
       } else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
         MOZ_ASSERT(mReader->IsWaitForDataSupported(),
                    "Don't yet have a strategy for non-heuristic + non-WaitForData");
         DispatchDecodeTasksIfNeeded();
-        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedAudio(), mAudioDataRequest.Exists() || mAudioWaitRequest.Exists());
-        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedVideo(), mVideoDataRequest.Exists() || mVideoWaitRequest.Exists());
+        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedAudio(), mReader->IsRequestingAudioData() || mAudioWaitRequest.Exists());
+        MOZ_ASSERT_IF(!mMinimizePreroll && OutOfDecodedVideo(), mReader->IsRequestingVidoeData() || mVideoWaitRequest.Exists());
         DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
                     "mAudioStatus: %s, outOfVideo: %d, mVideoStatus: %s",
                     OutOfDecodedAudio(), AudioRequestStatus(),
                     OutOfDecodedVideo(), VideoRequestStatus());
         return NS_OK;
       }
 
       DECODER_LOG("Changed state from BUFFERING to DECODING");
@@ -2261,19 +2286,17 @@ MediaDecoderStateMachine::Reset()
   mDecodedVideoEndTime = 0;
   mDecodedAudioEndTime = 0;
   mAudioCompleted = false;
   mVideoCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
 
   mMetadataRequest.DisconnectIfExists();
-  mAudioDataRequest.DisconnectIfExists();
   mAudioWaitRequest.DisconnectIfExists();
-  mVideoDataRequest.DisconnectIfExists();
   mVideoWaitRequest.DisconnectIfExists();
   mSeekTaskRequest.DisconnectIfExists();
 
   mPlaybackOffset = 0;
 
   mReader->ResetDecode();
 }
 
@@ -2732,16 +2755,42 @@ MediaDecoderStateMachine::CanonicalBuffe
 }
 
 MediaEventSource<void>&
 MediaDecoderStateMachine::OnMediaNotSeekable() const
 {
   return mReader->OnMediaNotSeekable();
 }
 
+const char*
+MediaDecoderStateMachine::AudioRequestStatus() const
+{
+  MOZ_ASSERT(OnTaskQueue());
+  if (mReader->IsRequestingAudioData()) {
+    MOZ_DIAGNOSTIC_ASSERT(!mAudioWaitRequest.Exists());
+    return "pending";
+  } else if (mAudioWaitRequest.Exists()) {
+    return "waiting";
+  }
+  return "idle";
+}
+
+const char*
+MediaDecoderStateMachine::VideoRequestStatus() const
+{
+  MOZ_ASSERT(OnTaskQueue());
+  if (mReader->IsRequestingVidoeData()) {
+    MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
+    return "pending";
+  } else if (mVideoWaitRequest.Exists()) {
+    return "waiting";
+  }
+  return "idle";
+}
+
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 #undef DECODER_WARN
 #undef DECODER_WARN_HELPER
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -82,16 +82,17 @@ hardware (via AudioStream).
 #if !defined(MediaDecoderStateMachine_h__)
 #define MediaDecoderStateMachine_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/StateMirroring.h"
 
 #include "nsThreadUtils.h"
+#include "MediaCallbackID.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaStatistics.h"
 #include "MediaTimer.h"
 #include "ImageContainer.h"
@@ -138,16 +139,19 @@ public:
   typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
   typedef mozilla::layers::ImageContainer::FrameID FrameID;
   MediaDecoderStateMachine(MediaDecoder* aDecoder,
                            MediaDecoderReader* aReader,
                            bool aRealTime = false);
 
   nsresult Init(MediaDecoder* aDecoder);
 
+  void SetMediaDecoderReaderWrapperCallback();
+  void CancelMediaDecoderReaderWrapperCallback();
+
   // Enumeration for the valid decoding states
   enum State {
     DECODER_STATE_DECODING_METADATA,
     DECODER_STATE_WAIT_FOR_CDM,
     DECODER_STATE_DORMANT,
     DECODER_STATE_DECODING,
     DECODER_STATE_SEEKING,
     DECODER_STATE_BUFFERING,
@@ -677,16 +681,20 @@ private:
 
   // mSeekTask is responsible for executing the current seek request.
   RefPtr<SeekTask> mSeekTask;
   MozPromiseRequestHolder<SeekTask::SeekTaskPromise> mSeekTaskRequest;
 
   void OnSeekTaskResolved(SeekTaskResolveValue aValue);
   void OnSeekTaskRejected(SeekTaskRejectValue aValue);
 
+  // This method discards the seek task and then get the ownership of
+  // MedaiDecoderReaderWarpper back via registering MDSM's callback into it.
+  void DiscardSeekTaskIfExist();
+
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
 
   const RefPtr<MediaDecoderReaderWrapper> mReader;
 
@@ -805,43 +813,23 @@ private:
   // playback. The flags below are true when the corresponding stream is
   // being "prerolled".
   bool mIsAudioPrerolling;
   bool mIsVideoPrerolling;
 
   // Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
   // should exist at any given moment.
 
-  MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mAudioDataRequest;
+  CallbackID mAudioCallbackID;
   MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
-  const char* AudioRequestStatus()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mAudioDataRequest.Exists()) {
-      MOZ_DIAGNOSTIC_ASSERT(!mAudioWaitRequest.Exists());
-      return "pending";
-    } else if (mAudioWaitRequest.Exists()) {
-      return "waiting";
-    }
-    return "idle";
-  }
+  const char* AudioRequestStatus() const;
 
+  CallbackID mVideoCallbackID;
   MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
-  MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mVideoDataRequest;
-  const char* VideoRequestStatus()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mVideoDataRequest.Exists()) {
-      MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
-      return "pending";
-    } else if (mVideoWaitRequest.Exists()) {
-      return "waiting";
-    }
-    return "idle";
-  }
+  const char* VideoRequestStatus() const;
 
   MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>& WaitRequestRef(MediaData::Type aType)
   {
     MOZ_ASSERT(OnTaskQueue());
     return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
   }
 
   // True if we shouldn't play our audio (but still write it to any capturing
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/SeekTask.cpp
@@ -81,16 +81,19 @@ SeekTask::SeekTask(const void* aDecoderI
   seekTime = std::min(seekTime, end);
   seekTime = std::max(int64_t(0), seekTime);
   NS_ASSERTION(seekTime >= 0 && seekTime <= end,
                "Can only seek in range [0,duration]");
   mSeekJob.mTarget.SetTime(media::TimeUnit::FromMicroseconds(seekTime));
 
   mDropAudioUntilNextDiscontinuity = HasAudio();
   mDropVideoUntilNextDiscontinuity = HasVideo();
+
+  // Configure MediaDecoderReaderWrapper.
+  SetMediaDecoderReaderWrapperCallback();
 }
 
 SeekTask::~SeekTask()
 {
   MOZ_ASSERT(mIsDiscarded);
 }
 
 void
@@ -147,22 +150,21 @@ void
 SeekTask::Discard()
 {
   // Disconnect MediaDecoder.
   mSeekJob.RejectIfExists(__func__);
 
   // Disconnect MDSM.
   RejectIfExist(__func__);
 
-  // Disconnect MediaDecoderReader.
+  // Disconnect MediaDecoderReaderWrapper.
   mSeekRequest.DisconnectIfExists();
-  mAudioDataRequest.DisconnectIfExists();
-  mVideoDataRequest.DisconnectIfExists();
   mAudioWaitRequest.DisconnectIfExists();
   mVideoWaitRequest.DisconnectIfExists();
+  CancelMediaDecoderReaderWrapperCallback();
 
   mIsDiscarded = true;
 }
 
 bool
 SeekTask::NeedToResetMDSM() const
 {
   return true;
@@ -211,17 +213,17 @@ nsresult
 SeekTask::EnsureAudioDecodeTaskQueued()
 {
   AssertOwnerThread();
 
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
               IsAudioDecoding(), AudioRequestStatus());
 
   if (!IsAudioDecoding() ||
-      mAudioDataRequest.Exists() ||
+      mReader->IsRequestingAudioData() ||
       mAudioWaitRequest.Exists() ||
       mSeekRequest.Exists()) {
     return NS_OK;
   }
 
   RequestAudioData();
   return NS_OK;
 }
@@ -230,81 +232,76 @@ nsresult
 SeekTask::EnsureVideoDecodeTaskQueued()
 {
   AssertOwnerThread();
 
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
              IsVideoDecoding(), VideoRequestStatus());
 
   if (!IsVideoDecoding() ||
-      mVideoDataRequest.Exists() ||
+      mReader->IsRequestingVidoeData() ||
       mVideoWaitRequest.Exists() ||
       mSeekRequest.Exists()) {
     return NS_OK;
   }
 
   RequestVideoData();
   return NS_OK;
 }
 
 const char*
 SeekTask::AudioRequestStatus()
 {
   AssertOwnerThread();
-  if (mAudioDataRequest.Exists()) {
+  if (mReader->IsRequestingAudioData()) {
     MOZ_DIAGNOSTIC_ASSERT(!mAudioWaitRequest.Exists());
     return "pending";
   } else if (mAudioWaitRequest.Exists()) {
     return "waiting";
   }
   return "idle";
 }
 
 const char*
 SeekTask::VideoRequestStatus()
 {
   AssertOwnerThread();
-  if (mVideoDataRequest.Exists()) {
+  if (mReader->IsRequestingVidoeData()) {
     MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
     return "pending";
   } else if (mVideoWaitRequest.Exists()) {
     return "waiting";
   }
   return "idle";
 }
 
 void
 SeekTask::RequestAudioData()
 {
   AssertOwnerThread();
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              !!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
 
-  mAudioDataRequest.Begin(mReader->RequestAudioData()
-    ->Then(OwnerThread(), __func__, this,
-           &SeekTask::OnAudioDecoded, &SeekTask::OnAudioNotDecoded));
+  mReader->RequestAudioData();
 }
 
 void
 SeekTask::RequestVideoData()
 {
   AssertOwnerThread();
   //These two variables are not used in the SEEKING state.
   const bool skipToNextKeyFrame = false;
   const media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(0);
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
                !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
                currentTime.ToMicroseconds());
 
-  mVideoDataRequest.Begin(
-    mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
-    ->Then(OwnerThread(), __func__, this,
-           &SeekTask::OnVideoDecoded, &SeekTask::OnVideoNotDecoded));
+  mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
 }
 
 nsresult
 SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
 {
   AssertOwnerThread();
   RefPtr<AudioData> audio(aSample->As<AudioData>());
   MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
@@ -495,17 +492,16 @@ SeekTask::OnSeekRejected(nsresult aResul
 }
 
 void
 SeekTask::OnAudioDecoded(MediaData* aAudioSample)
 {
   AssertOwnerThread();
   RefPtr<MediaData> audio(aAudioSample);
   MOZ_ASSERT(audio);
-  mAudioDataRequest.Complete();
 
   // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
   // resolved.
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
              (audio ? audio->mTime : -1),
              (audio ? audio->GetEndTime() : -1),
              (audio ? audio->mDiscontinuity : 0));
@@ -548,17 +544,16 @@ SeekTask::OnAudioDecoded(MediaData* aAud
   CheckIfSeekComplete();
 }
 
 void
 SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
 {
   AssertOwnerThread();
   SAMPLE_LOG("OnAduioNotDecoded (aReason=%u)", aReason);
-  mAudioDataRequest.Complete();
 
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     // If this is a decode error, delegate to the generic error path.
     RejectIfExist(__func__);
     return;
   }
 
   // If the decoder is waiting for data, we tell it to call us back when the
@@ -597,17 +592,16 @@ SeekTask::OnAudioNotDecoded(MediaDecoder
 }
 
 void
 SeekTask::OnVideoDecoded(MediaData* aVideoSample)
 {
   AssertOwnerThread();
   RefPtr<MediaData> video(aVideoSample);
   MOZ_ASSERT(video);
-  mVideoDataRequest.Complete();
 
   // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
   // resolved.
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
              (video ? video->GetEndTime() : -1),
              (video ? video->mDiscontinuity : 0));
@@ -652,17 +646,16 @@ SeekTask::OnVideoDecoded(MediaData* aVid
   CheckIfSeekComplete();
 }
 
 void
 SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
 {
   AssertOwnerThread();
   SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
-  mVideoDataRequest.Complete();
 
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     // If this is a decode error, delegate to the generic error path.
     RejectIfExist(__func__);
     return;
   }
 
   // If the decoder is waiting for data, we tell it to call us back when the
@@ -705,9 +698,33 @@ SeekTask::OnVideoNotDecoded(MediaDecoder
     }
 
     mIsVideoQueueFinished = true;
     mDropVideoUntilNextDiscontinuity = false; // To make IsVideoSeekComplete() return TRUE.
     CheckIfSeekComplete();
   }
 }
 
+void
+SeekTask::SetMediaDecoderReaderWrapperCallback()
+{
+  mAudioCallbackID =
+    mReader->SetAudioCallback(this, &SeekTask::OnAudioDecoded,
+                                    &SeekTask::OnAudioNotDecoded);
+
+  mVideoCallbackID =
+    mReader->SetVideoCallback(this, &SeekTask::OnVideoDecoded,
+                                    &SeekTask::OnVideoNotDecoded);
+
+  DECODER_LOG("SeekTask set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
+  DECODER_LOG("SeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
+}
+
+void
+SeekTask::CancelMediaDecoderReaderWrapperCallback()
+{
+    DECODER_LOG("SeekTask cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
+    mReader->CancelAudioCallback(mAudioCallbackID);
+
+    DECODER_LOG("SeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
+    mReader->CancelVideoCallback(mVideoCallbackID);
+}
 } // namespace mozilla
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -3,16 +3,17 @@
 /* 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/. */
 
 #ifndef SEEK_TASK_H
 #define SEEK_TASK_H
 
 #include "mozilla/MozPromise.h"
+#include "MediaCallbackID.h"
 #include "MediaDecoderReader.h"
 #include "SeekJob.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaData;
 class MediaDecoderReaderWrapper;
@@ -122,16 +123,20 @@ protected:
   virtual void OnAudioDecoded(MediaData* aAudioSample);
 
   virtual void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
 
   virtual void OnVideoDecoded(MediaData* aVideoSample);
 
   virtual void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
 
+  void SetMediaDecoderReaderWrapperCallback();
+
+  void CancelMediaDecoderReaderWrapperCallback();
+
   /*
    * Data shared with MDSM.
    */
   const void* mDecoderID; // For logging.
   const RefPtr<AbstractThread> mOwnerThread;
   const RefPtr<MediaDecoderReaderWrapper> mReader;
 
   /*
@@ -152,18 +157,18 @@ protected:
   // the seek target, we will still have a frame that we can display as the
   // last frame in the media.
   RefPtr<MediaData> mFirstVideoFrameAfterSeek;
 
   /*
    * Track the current seek promise made by the reader.
    */
   MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
-  MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mAudioDataRequest;
-  MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mVideoDataRequest;
+  CallbackID mAudioCallbackID;
+  CallbackID mVideoCallbackID;
   MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
   MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
 
   /*
    * Information which are going to be returned to MDSM.
    */
   RefPtr<MediaData> mSeekedAudioData;
   RefPtr<MediaData> mSeekedVideoData;