Bug 1235301 part 1 - abstract the SeekTask class; r=jwwang draft
authorKaku Kuo <tkuo@mozilla.com>
Tue, 24 May 2016 11:02:57 +0800
changeset 372166 74fe1a49abb17a9b8129330bcb70181520146140
parent 372165 7c49031aec07f8a04a83783091d1e93cd0767075
child 372167 1a205db83a22c9d91f7fac0ddf0d184d0591cba7
push id19455
push usertkuo@mozilla.com
push dateFri, 27 May 2016 16:28:45 +0000
reviewersjwwang
bugs1235301
milestone49.0a1
Bug 1235301 part 1 - abstract the SeekTask class; r=jwwang MozReview-Commit-ID: F2oXfR4F28x
dom/media/AccurateSeekTask.cpp
dom/media/AccurateSeekTask.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/SeekTask.cpp
dom/media/SeekTask.h
dom/media/moz.build
copy from dom/media/SeekTask.cpp
copy to dom/media/AccurateSeekTask.cpp
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -1,84 +1,62 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "SeekTask.h"
+#include "AccurateSeekTask.h"
 #include "MediaDecoderReaderWrapper.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Assertions.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 extern LazyLogModule gMediaSampleLog;
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 
 #define LOG(m, l, x, ...) \
-  MOZ_LOG(m, l, ("[SeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
+  MOZ_LOG(m, l, ("[AccurateSeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
 #define DECODER_LOG(x, ...) \
   LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
 #define VERBOSE_LOG(x, ...) \
   LOG(gMediaDecoderLog, LogLevel::Verbose, x, ##__VA_ARGS__)
 #define SAMPLE_LOG(x, ...) \
   LOG(gMediaSampleLog, LogLevel::Debug, x, ##__VA_ARGS__)
 
 // Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
 // when __VA_ARGS__ expands to nothing. This is a workaround for it.
 #define DECODER_WARN_HELPER(a, b) NS_WARNING b
 #define DECODER_WARN(x, ...) \
   DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
 
-/*static*/ already_AddRefed<SeekTask>
-SeekTask::CreateSeekTask(const void* aDecoderID,
-                         AbstractThread* aThread,
-                         MediaDecoderReaderWrapper* aReader,
-                         SeekJob&& aSeekJob,
-                         const MediaInfo& aInfo,
-                         const media::TimeUnit& aDuration,
-                         int64_t aCurrentMediaTime)
-{
-  RefPtr<SeekTask> task(new SeekTask(aDecoderID, aThread,
-                                     aReader, Move(aSeekJob), aInfo,
-                                     aDuration, aCurrentMediaTime));
-
-  return task.forget();
-}
-
-SeekTask::SeekTask(const void* aDecoderID,
-                   AbstractThread* aThread,
-                   MediaDecoderReaderWrapper* aReader,
-                   SeekJob&& aSeekJob,
-                   const MediaInfo& aInfo,
-                   const media::TimeUnit& aDuration,
-                   int64_t aCurrentMediaTime)
-  : mDecoderID(aDecoderID)
-  , mOwnerThread(aThread)
-  , mReader(aReader)
-  , mSeekJob(Move(aSeekJob))
+AccurateSeekTask::AccurateSeekTask(const void* aDecoderID,
+                                   AbstractThread* aThread,
+                                   MediaDecoderReaderWrapper* aReader,
+                                   SeekJob&& aSeekJob,
+                                   const MediaInfo& aInfo,
+                                   const media::TimeUnit& aDuration,
+                                   int64_t aCurrentMediaTime)
+  : SeekTask(aDecoderID, aThread, aReader, Move(aSeekJob))
   , mCurrentTimeBeforeSeek(aCurrentMediaTime)
   , mAudioRate(aInfo.mAudio.mRate)
   , mHasAudio(aInfo.HasAudio())
   , mHasVideo(aInfo.HasVideo())
   , mDropAudioUntilNextDiscontinuity(false)
   , mDropVideoUntilNextDiscontinuity(false)
-  , mIsDiscarded(false)
-  , mIsAudioQueueFinished(false)
-  , mIsVideoQueueFinished(false)
-  , mNeedToStopPrerollingAudio(false)
-  , mNeedToStopPrerollingVideo(false)
 {
+  AssertOwnerThread();
+
   // Bound the seek time to be inside the media range.
   int64_t end = aDuration.ToMicroseconds();
   NS_ASSERTION(end != -1, "Should know end time by now");
   int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
   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]");
@@ -86,229 +64,178 @@ SeekTask::SeekTask(const void* aDecoderI
 
   mDropAudioUntilNextDiscontinuity = HasAudio();
   mDropVideoUntilNextDiscontinuity = HasVideo();
 
   // Configure MediaDecoderReaderWrapper.
   SetMediaDecoderReaderWrapperCallback();
 }
 
-SeekTask::~SeekTask()
+AccurateSeekTask::~AccurateSeekTask()
 {
+  AssertOwnerThread();
   MOZ_ASSERT(mIsDiscarded);
 }
 
-void
-SeekTask::Resolve(const char* aCallSite)
-{
-  SeekTaskResolveValue val;
-  val.mSeekedAudioData = mSeekedAudioData;
-  val.mSeekedVideoData = mSeekedVideoData;
-  val.mIsAudioQueueFinished = mIsAudioQueueFinished;
-  val.mIsVideoQueueFinished = mIsVideoQueueFinished;
-  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
-  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
-
-  mSeekTaskPromise.Resolve(val, aCallSite);
-}
-
-void
-SeekTask::RejectIfExist(const char* aCallSite)
+bool
+AccurateSeekTask::HasAudio() const
 {
-  SeekTaskRejectValue val;
-  val.mIsAudioQueueFinished = mIsAudioQueueFinished;
-  val.mIsVideoQueueFinished = mIsVideoQueueFinished;
-  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
-  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
-
-  mSeekTaskPromise.RejectIfExists(val, aCallSite);
-}
-
-void
-SeekTask::AssertOwnerThread() const
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-}
-
-bool
-SeekTask::HasAudio() const
-{
+  AssertOwnerThread();
   return mHasAudio;
 }
 
 bool
-SeekTask::HasVideo() const
+AccurateSeekTask::HasVideo() const
 {
+  AssertOwnerThread();
   return mHasVideo;
 }
 
-AbstractThread*
-SeekTask::OwnerThread() const
+void
+AccurateSeekTask::Discard()
 {
-  return mOwnerThread;
-}
+  AssertOwnerThread();
 
-void
-SeekTask::Discard()
-{
   // Disconnect MediaDecoder.
   mSeekJob.RejectIfExists(__func__);
 
   // Disconnect MDSM.
   RejectIfExist(__func__);
 
   // Disconnect MediaDecoderReaderWrapper.
   mSeekRequest.DisconnectIfExists();
   CancelMediaDecoderReaderWrapperCallback();
 
   mIsDiscarded = true;
 }
 
 bool
-SeekTask::NeedToResetMDSM() const
+AccurateSeekTask::NeedToResetMDSM() const
 {
+  AssertOwnerThread();
   return true;
 }
 
-SeekJob&
-SeekTask::GetSeekJob()
-{
-  return mSeekJob;
-}
-
-bool
-SeekTask::Exists() const
-{
-  // mSeekTaskPromise communicates SeekTask and MDSM;
-  // mSeekJob communicates MDSM and MediaDecoder;
-  // Either one exists means the current seek task has yet finished.
-  return !mSeekTaskPromise.IsEmpty() || mSeekJob.Exists();
-}
-
-RefPtr<SeekTask::SeekTaskPromise>
-SeekTask::Seek(const media::TimeUnit& aDuration)
+RefPtr<AccurateSeekTask::SeekTaskPromise>
+AccurateSeekTask::Seek(const media::TimeUnit& aDuration)
 {
   AssertOwnerThread();
 
   // Do the seek.
   mSeekRequest.Begin(mReader->Seek(mSeekJob.mTarget, aDuration)
     ->Then(OwnerThread(), __func__, this,
-           &SeekTask::OnSeekResolved, &SeekTask::OnSeekRejected));
+           &AccurateSeekTask::OnSeekResolved, &AccurateSeekTask::OnSeekRejected));
 
   return mSeekTaskPromise.Ensure(__func__);
 }
 
 bool
-SeekTask::IsVideoDecoding() const
+AccurateSeekTask::IsVideoDecoding() const
 {
   AssertOwnerThread();
   return HasVideo() && !mIsVideoQueueFinished;
 }
 
 bool
-SeekTask::IsAudioDecoding() const
+AccurateSeekTask::IsAudioDecoding() const
 {
   AssertOwnerThread();
   return HasAudio() && !mIsAudioQueueFinished;
 }
 
 nsresult
-SeekTask::EnsureAudioDecodeTaskQueued()
+AccurateSeekTask::EnsureAudioDecodeTaskQueued()
 {
   AssertOwnerThread();
-
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
               IsAudioDecoding(), AudioRequestStatus());
 
   if (!IsAudioDecoding() ||
       mReader->IsRequestingAudioData() ||
       mReader->IsWaitingAudioData() ||
       mSeekRequest.Exists()) {
     return NS_OK;
   }
 
   RequestAudioData();
   return NS_OK;
 }
 
 nsresult
-SeekTask::EnsureVideoDecodeTaskQueued()
+AccurateSeekTask::EnsureVideoDecodeTaskQueued()
 {
   AssertOwnerThread();
-
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
              IsVideoDecoding(), VideoRequestStatus());
 
   if (!IsVideoDecoding() ||
       mReader->IsRequestingVideoData() ||
       mReader->IsWaitingVideoData() ||
       mSeekRequest.Exists()) {
     return NS_OK;
   }
 
   RequestVideoData();
   return NS_OK;
 }
 
 const char*
-SeekTask::AudioRequestStatus()
+AccurateSeekTask::AudioRequestStatus()
 {
   AssertOwnerThread();
+
   if (mReader->IsRequestingAudioData()) {
     MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingAudioData());
     return "pending";
   } else if (mReader->IsWaitingAudioData()) {
     return "waiting";
   }
   return "idle";
 }
 
 const char*
-SeekTask::VideoRequestStatus()
+AccurateSeekTask::VideoRequestStatus()
 {
   AssertOwnerThread();
+
   if (mReader->IsRequestingVideoData()) {
     MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingVideoData());
     return "pending";
   } else if (mReader->IsWaitingVideoData()) {
     return "waiting";
   }
   return "idle";
 }
 
 void
-SeekTask::RequestAudioData()
+AccurateSeekTask::RequestAudioData()
 {
   AssertOwnerThread();
-
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              !!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
 
   mReader->RequestAudioData();
 }
 
 void
-SeekTask::RequestVideoData()
+AccurateSeekTask::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(), false,
+               media::TimeUnit().ToMicroseconds());
 
-  SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
-               !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
-               currentTime.ToMicroseconds());
-
-  mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
+  mReader->RequestVideoData(false, media::TimeUnit());
 }
 
 nsresult
-SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
+AccurateSeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
 {
   AssertOwnerThread();
+
   RefPtr<AudioData> audio(aSample->As<AudioData>());
   MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
 
   CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
   if (!sampleDuration.isValid()) {
     return NS_ERROR_FAILURE;
   }
 
@@ -373,19 +300,20 @@ SeekTask::DropAudioUpToSeekTarget(MediaD
                                        audio->mRate));
   MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
   mSeekedAudioData = data;
 
   return NS_OK;
 }
 
 nsresult
-SeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
+AccurateSeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
 {
   AssertOwnerThread();
+
   RefPtr<VideoData> video(aSample->As<VideoData>());
   MOZ_ASSERT(video);
   DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
               video->mTime, video->GetEndTime());
   MOZ_ASSERT(mSeekJob.Exists());
   const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
 
   // If the frame end time is less than the seek target, we won't want
@@ -410,45 +338,43 @@ SeekTask::DropVideoUpToSeekTarget(MediaD
     MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
     mSeekedVideoData = video;
   }
 
   return NS_OK;
 }
 
 bool
-SeekTask::IsAudioSeekComplete()
+AccurateSeekTask::IsAudioSeekComplete()
 {
   AssertOwnerThread();
-
   SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
       mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
   return
     !HasAudio() ||
     mSeekJob.mTarget.IsVideoOnly() ||
     (Exists() && !mDropAudioUntilNextDiscontinuity &&
      (mIsAudioQueueFinished || mSeekedAudioData));
 }
 
 bool
-SeekTask::IsVideoSeekComplete()
+AccurateSeekTask::IsVideoSeekComplete()
 {
   AssertOwnerThread();
-
   SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
       mSeekJob.Exists(), mDropVideoUntilNextDiscontinuity, mIsVideoQueueFinished, !!mSeekedVideoData);
 
   return
     !HasVideo() ||
     (Exists() && !mDropVideoUntilNextDiscontinuity &&
      (mIsVideoQueueFinished || mSeekedVideoData));
 }
 
 void
-SeekTask::CheckIfSeekComplete()
+AccurateSeekTask::CheckIfSeekComplete()
 {
   AssertOwnerThread();
 
   const bool videoSeekComplete = IsVideoSeekComplete();
   if (HasVideo() && !videoSeekComplete) {
     // We haven't reached the target. Ensure we have requested another sample.
     if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
       DECODER_WARN("Failed to request video during seek");
@@ -469,41 +395,44 @@ SeekTask::CheckIfSeekComplete()
              audioSeekComplete, videoSeekComplete);
 
   if (audioSeekComplete && videoSeekComplete) {
     Resolve(__func__); // Call to MDSM::SeekCompleted();
   }
 }
 
 void
-SeekTask::OnSeekResolved(media::TimeUnit)
+AccurateSeekTask::OnSeekResolved(media::TimeUnit)
 {
   AssertOwnerThread();
+
   mSeekRequest.Complete();
   // We must decode the first samples of active streams, so we can determine
   // the new stream time. So dispatch tasks to do that.
   EnsureVideoDecodeTaskQueued();
   if (!mSeekJob.mTarget.IsVideoOnly()) {
     EnsureAudioDecodeTaskQueued();
   }
 }
 
 void
-SeekTask::OnSeekRejected(nsresult aResult)
+AccurateSeekTask::OnSeekRejected(nsresult aResult)
 {
   AssertOwnerThread();
+
   mSeekRequest.Complete();
   MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
   RejectIfExist(__func__);
 }
 
 void
-SeekTask::OnAudioDecoded(MediaData* aAudioSample)
+AccurateSeekTask::OnAudioDecoded(MediaData* aAudioSample)
 {
   AssertOwnerThread();
+
   RefPtr<MediaData> audio(aAudioSample);
   MOZ_ASSERT(audio);
 
   // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
   // resolved.
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
              (audio ? audio->mTime : -1),
@@ -544,17 +473,17 @@ SeekTask::OnAudioDecoded(MediaData* aAud
         return;
       }
     }
   }
   CheckIfSeekComplete();
 }
 
 void
-SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+AccurateSeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
 {
   AssertOwnerThread();
   SAMPLE_LOG("OnAduioNotDecoded (aReason=%u)", aReason);
 
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     // If this is a decode error, delegate to the generic error path.
     RejectIfExist(__func__);
     return;
@@ -582,19 +511,20 @@ SeekTask::OnAudioNotDecoded(MediaDecoder
   if (aReason == MediaDecoderReader::END_OF_STREAM) {
     mIsAudioQueueFinished = true;
     mDropAudioUntilNextDiscontinuity = false; // To make IsAudioSeekComplete() return TRUE.
     CheckIfSeekComplete();
   }
 }
 
 void
-SeekTask::OnVideoDecoded(MediaData* aVideoSample)
+AccurateSeekTask::OnVideoDecoded(MediaData* aVideoSample)
 {
   AssertOwnerThread();
+
   RefPtr<MediaData> video(aVideoSample);
   MOZ_ASSERT(video);
 
   // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
   // resolved.
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
@@ -637,17 +567,17 @@ SeekTask::OnVideoDecoded(MediaData* aVid
         return;
       }
     }
   }
   CheckIfSeekComplete();
 }
 
 void
-SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+AccurateSeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
 {
   AssertOwnerThread();
   SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
 
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     // If this is a decode error, delegate to the generic error path.
     RejectIfExist(__func__);
     return;
@@ -685,55 +615,65 @@ SeekTask::OnVideoNotDecoded(MediaDecoder
 
     mIsVideoQueueFinished = true;
     mDropVideoUntilNextDiscontinuity = false; // To make IsVideoSeekComplete() return TRUE.
     CheckIfSeekComplete();
   }
 }
 
 void
-SeekTask::SetMediaDecoderReaderWrapperCallback()
+AccurateSeekTask::SetMediaDecoderReaderWrapperCallback()
 {
+  AssertOwnerThread();
+
   mAudioCallbackID =
-    mReader->SetAudioCallback(this, &SeekTask::OnAudioDecoded,
-                                    &SeekTask::OnAudioNotDecoded);
+    mReader->SetAudioCallback(this, &AccurateSeekTask::OnAudioDecoded,
+                                    &AccurateSeekTask::OnAudioNotDecoded);
 
   mVideoCallbackID =
-    mReader->SetVideoCallback(this, &SeekTask::OnVideoDecoded,
-                                    &SeekTask::OnVideoNotDecoded);
+    mReader->SetVideoCallback(this, &AccurateSeekTask::OnVideoDecoded,
+                                    &AccurateSeekTask::OnVideoNotDecoded);
 
-  RefPtr<SeekTask> self = this;
+  RefPtr<AccurateSeekTask> self = this;
   mWaitAudioCallbackID =
     mReader->SetWaitAudioCallback(
       [self] (MediaData::Type aType) -> void {
+        self->AssertOwnerThread();
         self->EnsureAudioDecodeTaskQueued();
       },
-      [self] (WaitForDataRejectValue aRejection) -> void {});
+      [self] (WaitForDataRejectValue aRejection) -> void {
+        self->AssertOwnerThread();
+      });
 
   mWaitVideoCallbackID =
     mReader->SetWaitVideoCallback(
       [self] (MediaData::Type aType) -> void {
+        self->AssertOwnerThread();
         self->EnsureVideoDecodeTaskQueued();
       },
-      [self] (WaitForDataRejectValue aRejection) -> void {});
+      [self] (WaitForDataRejectValue aRejection) -> void {
+        self->AssertOwnerThread();
+      });
 
   DECODER_LOG("SeekTask set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
   DECODER_LOG("SeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
   DECODER_LOG("SeekTask set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
   DECODER_LOG("SeekTask set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
 }
 
 void
-SeekTask::CancelMediaDecoderReaderWrapperCallback()
+AccurateSeekTask::CancelMediaDecoderReaderWrapperCallback()
 {
-    DECODER_LOG("SeekTask cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
-    mReader->CancelAudioCallback(mAudioCallbackID);
+  AssertOwnerThread();
+
+  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);
+  DECODER_LOG("SeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
+  mReader->CancelVideoCallback(mVideoCallbackID);
 
-    DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-    mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
+  DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
+  mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
 
-    DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-    mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
+  DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
+  mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
 }
 } // namespace mozilla
copy from dom/media/SeekTask.h
copy to dom/media/AccurateSeekTask.h
--- a/dom/media/SeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -1,100 +1,47 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef SEEK_TASK_H
-#define SEEK_TASK_H
+#ifndef ACCURATE_SEEK_TASK_H
+#define ACCURATE_SEEK_TASK_H
 
-#include "mozilla/MozPromise.h"
+#include "SeekTask.h"
 #include "MediaCallbackID.h"
 #include "MediaDecoderReader.h"
 #include "SeekJob.h"
 
 namespace mozilla {
 
-class AbstractThread;
-class MediaData;
-class MediaDecoderReaderWrapper;
-
-struct SeekTaskResolveValue
-{
-  RefPtr<MediaData> mSeekedAudioData;
-  RefPtr<MediaData> mSeekedVideoData;
-  bool mIsAudioQueueFinished;
-  bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
-};
-
-struct SeekTaskRejectValue
-{
-  bool mIsAudioQueueFinished;
-  bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
-};
-
-class SeekTask {
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
-
+class AccurateSeekTask final : public SeekTask {
 public:
-  static const bool IsExclusive = true;
-
-  using SeekTaskPromise =
-    MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
+  AccurateSeekTask(const void* aDecoderID,
+                   AbstractThread* aThread,
+                   MediaDecoderReaderWrapper* aReader,
+                   SeekJob&& aSeekJob,
+                   const MediaInfo& aInfo,
+                   const media::TimeUnit& aDuration,
+                   int64_t aCurrentMediaTime);
 
-  static already_AddRefed<SeekTask>
-  CreateSeekTask(const void* aDecoderID,
-                 AbstractThread* aThread,
-                 MediaDecoderReaderWrapper* aReader,
-                 SeekJob&& aSeekJob,
-                 const MediaInfo& aInfo,
-                 const media::TimeUnit& aDuration,
-                 int64_t aCurrentMediaTime);
+  void Discard() override;
 
-  virtual void Discard();
-
-  virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
-
-  virtual bool NeedToResetMDSM() const;
-
-  SeekJob& GetSeekJob();
+  RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
 
-  bool Exists() const;
+  bool NeedToResetMDSM() const override;
 
-protected:
-  SeekTask(const void* aDecoderID,
-           AbstractThread* aThread,
-           MediaDecoderReaderWrapper* aReader,
-           SeekJob&& aSeekJob,
-           const MediaInfo& aInfo,
-           const media::TimeUnit& aDuration,
-           int64_t aCurrentMediaTime);
-
-  virtual ~SeekTask();
-
-  void Resolve(const char* aCallSite);
-
-  void RejectIfExist(const char* aCallSite);
-
-  void AssertOwnerThread() const;
+private:
+  ~AccurateSeekTask();
 
   bool HasAudio() const;
 
   bool HasVideo() const;
 
-  TaskQueue* DecodeTaskQueue() const;
-
-  AbstractThread* OwnerThread() const;
-
   bool IsVideoDecoding() const;
 
   bool IsAudioDecoding() const;
 
   nsresult EnsureVideoDecodeTaskQueued();
 
   nsresult EnsureAudioDecodeTaskQueued();
 
@@ -111,73 +58,53 @@ protected:
   nsresult DropVideoUpToSeekTarget(MediaData* aSample);
 
   bool IsAudioSeekComplete();
 
   bool IsVideoSeekComplete();
 
   void CheckIfSeekComplete();
 
-  virtual void OnSeekResolved(media::TimeUnit);
+  void OnSeekResolved(media::TimeUnit);
 
-  virtual void OnSeekRejected(nsresult aResult);
+  void OnSeekRejected(nsresult aResult);
 
-  virtual void OnAudioDecoded(MediaData* aAudioSample);
+  void OnAudioDecoded(MediaData* aAudioSample);
 
-  virtual void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+  void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
 
-  virtual void OnVideoDecoded(MediaData* aVideoSample);
+  void OnVideoDecoded(MediaData* aVideoSample);
 
-  virtual void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+  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;
-
-  /*
    * Internal state.
    */
-  SeekJob mSeekJob;
-  MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
-  int64_t mCurrentTimeBeforeSeek;
+  const int64_t mCurrentTimeBeforeSeek;
   const uint32_t mAudioRate;  // Audio sample rate.
   const bool mHasAudio;
   const bool mHasVideo;
   bool mDropAudioUntilNextDiscontinuity;
   bool mDropVideoUntilNextDiscontinuity;
-  bool mIsDiscarded;
 
   // This temporarily stores the first frame we decode after we seek.
   // This is so that if we hit end of stream while we're decoding to reach
   // 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;
   CallbackID mAudioCallbackID;
   CallbackID mVideoCallbackID;
   CallbackID mWaitAudioCallbackID;
   CallbackID mWaitVideoCallbackID;
-
-  /*
-   * Information which are going to be returned to MDSM.
-   */
-  RefPtr<MediaData> mSeekedAudioData;
-  RefPtr<MediaData> mSeekedVideoData;
-  bool mIsAudioQueueFinished;
-  bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
 };
 
 } // namespace mozilla
 
-#endif /* SEEK_TASK_H */
+#endif /* ACCURATE_SEEK_TASK_H */
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -32,16 +32,17 @@
 #include "nsContentUtils.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsDeque.h"
 #include "prenv.h"
 
+#include "AccurateSeekTask.h"
 #include "AudioSegment.h"
 #include "DOMMediaStream.h"
 #include "ImageContainer.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderReaderWrapper.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaShutdownManager.h"
@@ -1389,19 +1390,19 @@ void MediaDecoderStateMachine::InitiateV
   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(seekJob),
-                                       mInfo, Duration(), GetMediaTime());
+  mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
+                                   mReader.get(), Move(seekJob),
+                                   mInfo, Duration(), GetMediaTime());
 
   mOnSeekingStart.Notify(MediaDecoderEventVisibility::Suppressed);
 
   // Reset our state machine and decoding pipeline before seeking.
   if (mSeekTask->NeedToResetMDSM()) {
     Reset(MediaDecoderReader::VIDEO_ONLY);
   }
 
@@ -1574,19 +1575,19 @@ MediaDecoderStateMachine::InitiateSeek(S
   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());
+  mSeekTask = new AccurateSeekTask(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.
   StopPlayback();
   UpdatePlaybackPositionInternal(mSeekTask->GetSeekJob().mTarget.GetTime().ToMicroseconds());
 
   mOnSeekingStart.Notify(mSeekTask->GetSeekJob().mTarget.mEventVisibility);
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/SeekTask.cpp
@@ -2,738 +2,96 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SeekTask.h"
 #include "MediaDecoderReaderWrapper.h"
 #include "mozilla/AbstractThread.h"
-#include "mozilla/Assertions.h"
-#include "nsPrintfCString.h"
 
 namespace mozilla {
 
-extern LazyLogModule gMediaDecoderLog;
-extern LazyLogModule gMediaSampleLog;
-
-// avoid redefined macro in unified build
-#undef LOG
-#undef DECODER_LOG
-#undef VERBOSE_LOG
-
-#define LOG(m, l, x, ...) \
-  MOZ_LOG(m, l, ("[SeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
-#define DECODER_LOG(x, ...) \
-  LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
-#define VERBOSE_LOG(x, ...) \
-  LOG(gMediaDecoderLog, LogLevel::Verbose, x, ##__VA_ARGS__)
-#define SAMPLE_LOG(x, ...) \
-  LOG(gMediaSampleLog, LogLevel::Debug, x, ##__VA_ARGS__)
-
-// Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
-// when __VA_ARGS__ expands to nothing. This is a workaround for it.
-#define DECODER_WARN_HELPER(a, b) NS_WARNING b
-#define DECODER_WARN(x, ...) \
-  DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
-
-/*static*/ already_AddRefed<SeekTask>
-SeekTask::CreateSeekTask(const void* aDecoderID,
-                         AbstractThread* aThread,
-                         MediaDecoderReaderWrapper* aReader,
-                         SeekJob&& aSeekJob,
-                         const MediaInfo& aInfo,
-                         const media::TimeUnit& aDuration,
-                         int64_t aCurrentMediaTime)
-{
-  RefPtr<SeekTask> task(new SeekTask(aDecoderID, aThread,
-                                     aReader, Move(aSeekJob), aInfo,
-                                     aDuration, aCurrentMediaTime));
-
-  return task.forget();
-}
-
 SeekTask::SeekTask(const void* aDecoderID,
                    AbstractThread* aThread,
                    MediaDecoderReaderWrapper* aReader,
-                   SeekJob&& aSeekJob,
-                   const MediaInfo& aInfo,
-                   const media::TimeUnit& aDuration,
-                   int64_t aCurrentMediaTime)
+                   SeekJob&& aSeekJob)
   : mDecoderID(aDecoderID)
   , mOwnerThread(aThread)
   , mReader(aReader)
   , mSeekJob(Move(aSeekJob))
-  , mCurrentTimeBeforeSeek(aCurrentMediaTime)
-  , mAudioRate(aInfo.mAudio.mRate)
-  , mHasAudio(aInfo.HasAudio())
-  , mHasVideo(aInfo.HasVideo())
-  , mDropAudioUntilNextDiscontinuity(false)
-  , mDropVideoUntilNextDiscontinuity(false)
   , mIsDiscarded(false)
   , mIsAudioQueueFinished(false)
   , mIsVideoQueueFinished(false)
   , mNeedToStopPrerollingAudio(false)
   , mNeedToStopPrerollingVideo(false)
 {
-  // Bound the seek time to be inside the media range.
-  int64_t end = aDuration.ToMicroseconds();
-  NS_ASSERTION(end != -1, "Should know end time by now");
-  int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
-  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();
+  AssertOwnerThread();
 }
 
 SeekTask::~SeekTask()
 {
+  AssertOwnerThread();
   MOZ_ASSERT(mIsDiscarded);
 }
 
 void
 SeekTask::Resolve(const char* aCallSite)
 {
+  AssertOwnerThread();
+
   SeekTaskResolveValue val;
   val.mSeekedAudioData = mSeekedAudioData;
   val.mSeekedVideoData = mSeekedVideoData;
   val.mIsAudioQueueFinished = mIsAudioQueueFinished;
   val.mIsVideoQueueFinished = mIsVideoQueueFinished;
   val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
   val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
 
   mSeekTaskPromise.Resolve(val, aCallSite);
 }
 
 void
 SeekTask::RejectIfExist(const char* aCallSite)
 {
+  AssertOwnerThread();
+
   SeekTaskRejectValue val;
   val.mIsAudioQueueFinished = mIsAudioQueueFinished;
   val.mIsVideoQueueFinished = mIsVideoQueueFinished;
   val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
   val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
 
   mSeekTaskPromise.RejectIfExists(val, aCallSite);
 }
 
 void
 SeekTask::AssertOwnerThread() const
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 }
 
-bool
-SeekTask::HasAudio() const
-{
-  return mHasAudio;
-}
-
-bool
-SeekTask::HasVideo() const
-{
-  return mHasVideo;
-}
-
 AbstractThread*
 SeekTask::OwnerThread() const
 {
+  AssertOwnerThread();
   return mOwnerThread;
 }
 
-void
-SeekTask::Discard()
-{
-  // Disconnect MediaDecoder.
-  mSeekJob.RejectIfExists(__func__);
-
-  // Disconnect MDSM.
-  RejectIfExist(__func__);
-
-  // Disconnect MediaDecoderReaderWrapper.
-  mSeekRequest.DisconnectIfExists();
-  CancelMediaDecoderReaderWrapperCallback();
-
-  mIsDiscarded = true;
-}
-
-bool
-SeekTask::NeedToResetMDSM() const
-{
-  return true;
-}
-
 SeekJob&
 SeekTask::GetSeekJob()
 {
+  AssertOwnerThread();
   return mSeekJob;
 }
 
 bool
 SeekTask::Exists() const
 {
+  AssertOwnerThread();
+
   // mSeekTaskPromise communicates SeekTask and MDSM;
   // mSeekJob communicates MDSM and MediaDecoder;
   // Either one exists means the current seek task has yet finished.
   return !mSeekTaskPromise.IsEmpty() || mSeekJob.Exists();
 }
 
-RefPtr<SeekTask::SeekTaskPromise>
-SeekTask::Seek(const media::TimeUnit& aDuration)
-{
-  AssertOwnerThread();
-
-  // Do the seek.
-  mSeekRequest.Begin(mReader->Seek(mSeekJob.mTarget, aDuration)
-    ->Then(OwnerThread(), __func__, this,
-           &SeekTask::OnSeekResolved, &SeekTask::OnSeekRejected));
-
-  return mSeekTaskPromise.Ensure(__func__);
-}
-
-bool
-SeekTask::IsVideoDecoding() const
-{
-  AssertOwnerThread();
-  return HasVideo() && !mIsVideoQueueFinished;
-}
-
-bool
-SeekTask::IsAudioDecoding() const
-{
-  AssertOwnerThread();
-  return HasAudio() && !mIsAudioQueueFinished;
-}
-
-nsresult
-SeekTask::EnsureAudioDecodeTaskQueued()
-{
-  AssertOwnerThread();
-
-  SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
-              IsAudioDecoding(), AudioRequestStatus());
-
-  if (!IsAudioDecoding() ||
-      mReader->IsRequestingAudioData() ||
-      mReader->IsWaitingAudioData() ||
-      mSeekRequest.Exists()) {
-    return NS_OK;
-  }
-
-  RequestAudioData();
-  return NS_OK;
-}
-
-nsresult
-SeekTask::EnsureVideoDecodeTaskQueued()
-{
-  AssertOwnerThread();
-
-  SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
-             IsVideoDecoding(), VideoRequestStatus());
-
-  if (!IsVideoDecoding() ||
-      mReader->IsRequestingVideoData() ||
-      mReader->IsWaitingVideoData() ||
-      mSeekRequest.Exists()) {
-    return NS_OK;
-  }
-
-  RequestVideoData();
-  return NS_OK;
-}
-
-const char*
-SeekTask::AudioRequestStatus()
-{
-  AssertOwnerThread();
-  if (mReader->IsRequestingAudioData()) {
-    MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingAudioData());
-    return "pending";
-  } else if (mReader->IsWaitingAudioData()) {
-    return "waiting";
-  }
-  return "idle";
-}
-
-const char*
-SeekTask::VideoRequestStatus()
-{
-  AssertOwnerThread();
-  if (mReader->IsRequestingVideoData()) {
-    MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingVideoData());
-    return "pending";
-  } else if (mReader->IsWaitingVideoData()) {
-    return "waiting";
-  }
-  return "idle";
-}
-
-void
-SeekTask::RequestAudioData()
-{
-  AssertOwnerThread();
-
-  SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
-             !!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
-
-  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());
-
-  mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
-}
-
-nsresult
-SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
-{
-  AssertOwnerThread();
-  RefPtr<AudioData> audio(aSample->As<AudioData>());
-  MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
-
-  CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
-  if (!sampleDuration.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (audio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
-    // Our seek target lies after the frames in this AudioData. Don't
-    // push it onto the audio queue, and keep decoding forwards.
-    return NS_OK;
-  }
-
-  if (audio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
-    // The seek target doesn't lie in the audio block just after the last
-    // audio frames we've seen which were before the seek target. This
-    // could have been the first audio data we've seen after seek, i.e. the
-    // seek terminated after the seek target in the audio stream. Just
-    // abort the audio decode-to-target, the state machine will play
-    // silence to cover the gap. Typically this happens in poorly muxed
-    // files.
-    DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
-    mSeekedAudioData = audio;
-    return NS_OK;
-  }
-
-  // The seek target lies somewhere in this AudioData's frames, strip off
-  // any frames which lie before the seek target, so we'll begin playback
-  // exactly at the seek target.
-  NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
-               "Target must at or be after data start.");
-  NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
-               "Data must end after target.");
-
-  CheckedInt64 framesToPrune =
-    UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - audio->mTime, mAudioRate);
-  if (!framesToPrune.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-  if (framesToPrune.value() > audio->mFrames) {
-    // We've messed up somehow. Don't try to trim frames, the |frames|
-    // variable below will overflow.
-    DECODER_WARN("Can't prune more frames that we have!");
-    return NS_ERROR_FAILURE;
-  }
-  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
-  uint32_t channels = audio->mChannels;
-  AlignedAudioBuffer audioData(frames * channels);
-  if (!audioData) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  memcpy(audioData.get(),
-         audio->mAudioData.get() + (framesToPrune.value() * channels),
-         frames * channels * sizeof(AudioDataValue));
-  CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
-  if (!duration.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-  RefPtr<AudioData> data(new AudioData(audio->mOffset,
-                                       mSeekJob.mTarget.GetTime().ToMicroseconds(),
-                                       duration.value(),
-                                       frames,
-                                       Move(audioData),
-                                       channels,
-                                       audio->mRate));
-  MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
-  mSeekedAudioData = data;
-
-  return NS_OK;
-}
-
-nsresult
-SeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
-{
-  AssertOwnerThread();
-  RefPtr<VideoData> video(aSample->As<VideoData>());
-  MOZ_ASSERT(video);
-  DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
-              video->mTime, video->GetEndTime());
-  MOZ_ASSERT(mSeekJob.Exists());
-  const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
-
-  // If the frame end time is less than the seek target, we won't want
-  // to display this frame after the seek, so discard it.
-  if (target >= video->GetEndTime()) {
-    DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
-                video->mTime, video->GetEndTime(), target);
-    mFirstVideoFrameAfterSeek = video;
-  } else {
-    if (target >= video->mTime && video->GetEndTime() >= target) {
-      // The seek target lies inside this frame's time slice. Adjust the frame's
-      // start time to match the seek target. We do this by replacing the
-      // first frame with a shallow copy which has the new timestamp.
-      RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
-      video = temp;
-    }
-    mFirstVideoFrameAfterSeek = nullptr;
-
-    DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
-                video->mTime, video->GetEndTime(), target);
-
-    MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
-    mSeekedVideoData = video;
-  }
-
-  return NS_OK;
-}
-
-bool
-SeekTask::IsAudioSeekComplete()
-{
-  AssertOwnerThread();
-
-  SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
-      mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
-  return
-    !HasAudio() ||
-    mSeekJob.mTarget.IsVideoOnly() ||
-    (Exists() && !mDropAudioUntilNextDiscontinuity &&
-     (mIsAudioQueueFinished || mSeekedAudioData));
-}
-
-bool
-SeekTask::IsVideoSeekComplete()
-{
-  AssertOwnerThread();
-
-  SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
-      mSeekJob.Exists(), mDropVideoUntilNextDiscontinuity, mIsVideoQueueFinished, !!mSeekedVideoData);
-
-  return
-    !HasVideo() ||
-    (Exists() && !mDropVideoUntilNextDiscontinuity &&
-     (mIsVideoQueueFinished || mSeekedVideoData));
-}
-
-void
-SeekTask::CheckIfSeekComplete()
-{
-  AssertOwnerThread();
-
-  const bool videoSeekComplete = IsVideoSeekComplete();
-  if (HasVideo() && !videoSeekComplete) {
-    // We haven't reached the target. Ensure we have requested another sample.
-    if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
-      DECODER_WARN("Failed to request video during seek");
-      RejectIfExist(__func__);
-    }
-  }
-
-  const bool audioSeekComplete = IsAudioSeekComplete();
-  if (HasAudio() && !audioSeekComplete) {
-    // We haven't reached the target. Ensure we have requested another sample.
-    if (NS_FAILED(EnsureAudioDecodeTaskQueued())) {
-      DECODER_WARN("Failed to request audio during seek");
-      RejectIfExist(__func__);
-    }
-  }
-
-  SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
-             audioSeekComplete, videoSeekComplete);
-
-  if (audioSeekComplete && videoSeekComplete) {
-    Resolve(__func__); // Call to MDSM::SeekCompleted();
-  }
-}
-
-void
-SeekTask::OnSeekResolved(media::TimeUnit)
-{
-  AssertOwnerThread();
-  mSeekRequest.Complete();
-  // We must decode the first samples of active streams, so we can determine
-  // the new stream time. So dispatch tasks to do that.
-  EnsureVideoDecodeTaskQueued();
-  if (!mSeekJob.mTarget.IsVideoOnly()) {
-    EnsureAudioDecodeTaskQueued();
-  }
-}
-
-void
-SeekTask::OnSeekRejected(nsresult aResult)
-{
-  AssertOwnerThread();
-  mSeekRequest.Complete();
-  MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
-  RejectIfExist(__func__);
-}
-
-void
-SeekTask::OnAudioDecoded(MediaData* aAudioSample)
-{
-  AssertOwnerThread();
-  RefPtr<MediaData> audio(aAudioSample);
-  MOZ_ASSERT(audio);
-
-  // 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));
-
-  if (!Exists()) {
-    // We've received a sample from a previous decode. Discard it.
-    return;
-  }
-
-  if (audio->mDiscontinuity) {
-    mDropAudioUntilNextDiscontinuity = false;
-  }
-
-  if (!mDropAudioUntilNextDiscontinuity) {
-    // We must be after the discontinuity; we're receiving samples
-    // at or after the seek target.
-    if (mSeekJob.mTarget.IsFast() &&
-        mSeekJob.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
-        audio->mTime < mCurrentTimeBeforeSeek) {
-      // We are doing a fastSeek, but we ended up *before* the previous
-      // playback position. This is surprising UX, so switch to an accurate
-      // seek and decode to the seek target. This is not conformant to the
-      // spec, fastSeek should always be fast, but until we get the time to
-      // change all Readers to seek to the keyframe after the currentTime
-      // in this case, we'll just decode forward. Bug 1026330.
-      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
-    }
-    if (mSeekJob.mTarget.IsFast()) {
-      // Non-precise seek; we can stop the seek at the first sample.
-      mSeekedAudioData = audio;
-    } else {
-      // We're doing an accurate seek. We must discard
-      // MediaData up to the one containing exact seek target.
-      if (NS_FAILED(DropAudioUpToSeekTarget(audio.get()))) {
-        RejectIfExist(__func__);
-        return;
-      }
-    }
-  }
-  CheckIfSeekComplete();
-}
-
-void
-SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-{
-  AssertOwnerThread();
-  SAMPLE_LOG("OnAduioNotDecoded (aReason=%u)", aReason);
-
-  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
-  // data arrives.
-  if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
-    MOZ_ASSERT(mReader->IsWaitForDataSupported(),
-               "Readers that send WAITING_FOR_DATA need to implement WaitForData");
-    mReader->WaitForData(MediaData::AUDIO_DATA);
-
-    // We are out of data to decode and will enter buffering mode soon.
-    // We want to play the frames we have already decoded, so we stop pre-rolling
-    // and ensure that loadeddata is fired as required.
-    mNeedToStopPrerollingAudio = true;
-    return;
-  }
-
-  if (aReason == MediaDecoderReader::CANCELED) {
-    EnsureAudioDecodeTaskQueued();
-    return;
-  }
-
-  if (aReason == MediaDecoderReader::END_OF_STREAM) {
-    mIsAudioQueueFinished = true;
-    mDropAudioUntilNextDiscontinuity = false; // To make IsAudioSeekComplete() return TRUE.
-    CheckIfSeekComplete();
-  }
-}
-
-void
-SeekTask::OnVideoDecoded(MediaData* aVideoSample)
-{
-  AssertOwnerThread();
-  RefPtr<MediaData> video(aVideoSample);
-  MOZ_ASSERT(video);
-
-  // 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));
-
-  if (!Exists()) {
-    // We've received a sample from a previous decode. Discard it.
-    return;
-  }
-
-  if (mDropVideoUntilNextDiscontinuity) {
-    if (video->mDiscontinuity) {
-      mDropVideoUntilNextDiscontinuity = false;
-    }
-  }
-
-  if (!mDropVideoUntilNextDiscontinuity) {
-    // We must be after the discontinuity; we're receiving samples
-    // at or after the seek target.
-    if (mSeekJob.mTarget.IsFast() &&
-        mSeekJob.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
-        video->mTime < mCurrentTimeBeforeSeek) {
-      // We are doing a fastSeek, but we ended up *before* the previous
-      // playback position. This is surprising UX, so switch to an accurate
-      // seek and decode to the seek target. This is not conformant to the
-      // spec, fastSeek should always be fast, but until we get the time to
-      // change all Readers to seek to the keyframe after the currentTime
-      // in this case, we'll just decode forward. Bug 1026330.
-      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
-    }
-    if (mSeekJob.mTarget.IsFast()) {
-      // Non-precise seek. We can stop the seek at the first sample.
-      mSeekedVideoData = video;
-    } else {
-      // We're doing an accurate seek. We still need to discard
-      // MediaData up to the one containing exact seek target.
-      if (NS_FAILED(DropVideoUpToSeekTarget(video.get()))) {
-        RejectIfExist(__func__);
-        return;
-      }
-    }
-  }
-  CheckIfSeekComplete();
-}
-
-void
-SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-{
-  AssertOwnerThread();
-  SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
-
-  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
-  // data arrives.
-  if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
-    MOZ_ASSERT(mReader->IsWaitForDataSupported(),
-               "Readers that send WAITING_FOR_DATA need to implement WaitForData");
-    mReader->WaitForData(MediaData::VIDEO_DATA);
-
-    // We are out of data to decode and will enter buffering mode soon.
-    // We want to play the frames we have already decoded, so we stop pre-rolling
-    // and ensure that loadeddata is fired as required.
-    mNeedToStopPrerollingVideo = true;
-    return;
-  }
-
-  if (aReason == MediaDecoderReader::CANCELED) {
-    EnsureVideoDecodeTaskQueued();
-    return;
-  }
-
-  if (aReason == MediaDecoderReader::END_OF_STREAM) {
-
-    if (Exists() && mFirstVideoFrameAfterSeek) {
-      // Null sample. Hit end of stream. If we have decoded a frame,
-      // insert it into the queue so that we have something to display.
-      // We make sure to do this before invoking VideoQueue().Finish()
-      // below.
-      mSeekedVideoData = mFirstVideoFrameAfterSeek;
-      mFirstVideoFrameAfterSeek = nullptr;
-    }
-
-    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);
-
-  RefPtr<SeekTask> self = this;
-  mWaitAudioCallbackID =
-    mReader->SetWaitAudioCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->EnsureAudioDecodeTaskQueued();
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {});
-
-  mWaitVideoCallbackID =
-    mReader->SetWaitVideoCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->EnsureVideoDecodeTaskQueued();
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {});
-
-  DECODER_LOG("SeekTask set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
-  DECODER_LOG("SeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
-  DECODER_LOG("SeekTask set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-  DECODER_LOG("SeekTask set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-}
-
-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);
-
-    DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-    mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
-
-    DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-    mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
-}
 } // namespace mozilla
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -4,24 +4,28 @@
  * 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;
+class MediaInfo;
+
+namespace media {
+class TimeUnit;
+}
 
 struct SeekTaskResolveValue
 {
   RefPtr<MediaData> mSeekedAudioData;
   RefPtr<MediaData> mSeekedVideoData;
   bool mIsAudioQueueFinished;
   bool mIsVideoQueueFinished;
   bool mNeedToStopPrerollingAudio;
@@ -41,137 +45,56 @@ class SeekTask {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
 
 public:
   static const bool IsExclusive = true;
 
   using SeekTaskPromise =
     MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
 
-  static already_AddRefed<SeekTask>
-  CreateSeekTask(const void* aDecoderID,
-                 AbstractThread* aThread,
-                 MediaDecoderReaderWrapper* aReader,
-                 SeekJob&& aSeekJob,
-                 const MediaInfo& aInfo,
-                 const media::TimeUnit& aDuration,
-                 int64_t aCurrentMediaTime);
+  virtual void Discard() = 0;
 
-  virtual void Discard();
+  virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) = 0;
 
-  virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
-
-  virtual bool NeedToResetMDSM() const;
+  virtual bool NeedToResetMDSM() const = 0;
 
   SeekJob& GetSeekJob();
 
   bool Exists() const;
 
 protected:
   SeekTask(const void* aDecoderID,
            AbstractThread* aThread,
            MediaDecoderReaderWrapper* aReader,
-           SeekJob&& aSeekJob,
-           const MediaInfo& aInfo,
-           const media::TimeUnit& aDuration,
-           int64_t aCurrentMediaTime);
+           SeekJob&& aSeekJob);
 
   virtual ~SeekTask();
 
   void Resolve(const char* aCallSite);
 
   void RejectIfExist(const char* aCallSite);
 
   void AssertOwnerThread() const;
 
-  bool HasAudio() const;
-
-  bool HasVideo() const;
-
-  TaskQueue* DecodeTaskQueue() const;
-
   AbstractThread* OwnerThread() const;
 
-  bool IsVideoDecoding() const;
-
-  bool IsAudioDecoding() const;
-
-  nsresult EnsureVideoDecodeTaskQueued();
-
-  nsresult EnsureAudioDecodeTaskQueued();
-
-  const char* AudioRequestStatus();
-
-  const char* VideoRequestStatus();
-
-  void RequestVideoData();
-
-  void RequestAudioData();
-
-  nsresult DropAudioUpToSeekTarget(MediaData* aSample);
-
-  nsresult DropVideoUpToSeekTarget(MediaData* aSample);
-
-  bool IsAudioSeekComplete();
-
-  bool IsVideoSeekComplete();
-
-  void CheckIfSeekComplete();
-
-  virtual void OnSeekResolved(media::TimeUnit);
-
-  virtual void OnSeekRejected(nsresult aResult);
-
-  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;
 
   /*
    * Internal state.
    */
   SeekJob mSeekJob;
   MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
-  int64_t mCurrentTimeBeforeSeek;
-  const uint32_t mAudioRate;  // Audio sample rate.
-  const bool mHasAudio;
-  const bool mHasVideo;
-  bool mDropAudioUntilNextDiscontinuity;
-  bool mDropVideoUntilNextDiscontinuity;
   bool mIsDiscarded;
 
-  // This temporarily stores the first frame we decode after we seek.
-  // This is so that if we hit end of stream while we're decoding to reach
-  // 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;
-  CallbackID mAudioCallbackID;
-  CallbackID mVideoCallbackID;
-  CallbackID mWaitAudioCallbackID;
-  CallbackID mWaitVideoCallbackID;
-
   /*
    * Information which are going to be returned to MDSM.
    */
   RefPtr<MediaData> mSeekedAudioData;
   RefPtr<MediaData> mSeekedVideoData;
   bool mIsAudioQueueFinished;
   bool mIsVideoQueueFinished;
   bool mNeedToStopPrerollingAudio;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -79,16 +79,17 @@ XPIDL_SOURCES += [
     'nsIDOMNavigatorUserMedia.idl',
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
+    'AccurateSeekTask.h',
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioConverter.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
@@ -192,16 +193,17 @@ EXPORTS.mozilla.dom += [
     'TextTrackRegion.h',
     'VideoPlaybackQuality.h',
     'VideoStreamTrack.h',
     'VideoTrack.h',
     'VideoTrackList.h',
 ]
 
 UNIFIED_SOURCES += [
+    'AccurateSeekTask.cpp',
     'ADTSDecoder.cpp',
     'ADTSDemuxer.cpp',
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
     'AudioConverter.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',