Bug 1319992: P3. Remove no longer used seeking argument. r=jwwang
☠☠ backed out by 6726d2d57b40 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 30 Nov 2016 01:42:50 +1100
changeset 326390 3cfdc09bf851edee680f63c44880bb70337ff06a
parent 326389 45afcd8cf9ef44508e33637b669a4d8f236b4f61
child 326391 2ae0864c76bcd89b3196a0086a9422886a5f5742
push id31101
push usercbook@mozilla.com
push dateMon, 19 Dec 2016 15:03:45 +0000
treeherdermozilla-central@d4b3146a5567 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1319992
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1319992: P3. Remove no longer used seeking argument. r=jwwang MozReview-Commit-ID: 9Oc0SHX0Rw2
dom/media/MediaDecoderReader.h
dom/media/MediaDecoderReaderWrapper.cpp
dom/media/MediaDecoderReaderWrapper.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/NextFrameSeekTask.cpp
dom/media/NextFrameSeekTask.h
dom/media/SeekTask.h
dom/media/android/AndroidMediaReader.cpp
dom/media/android/AndroidMediaReader.h
dom/media/directshow/DirectShowReader.cpp
dom/media/directshow/DirectShowReader.h
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -146,20 +146,18 @@ public:
   // synchronous ReadMetadata() calls. Implementations may also
   // override AsyncReadMetadata to create a more proper async implementation.
   virtual RefPtr<MetadataPromise> AsyncReadMetadata();
 
   // Fills aInfo with the latest cached data required to present the media,
   // ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
   virtual void ReadUpdatedMetadata(MediaInfo* aInfo) {}
 
-  // Moves the decode head to aTime microseconds. aEndTime denotes the end
-  // time of the media in usecs. This is only needed for OggReader, and should
-  // probably be removed somehow.
-  virtual RefPtr<SeekPromise> Seek(const SeekTarget& aTarget, int64_t aEndTime) = 0;
+  // Moves the decode head to aTime microseconds.
+  virtual RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) = 0;
 
   virtual void SetCDMProxy(CDMProxy* aProxy) {}
 
   // Tell the reader that the data decoded are not for direct playback, so it
   // can accept more files, in particular those which have more channels than
   // available in the audio output.
   void SetIgnoreAudioOutputFormat()
   {
--- a/dom/media/MediaDecoderReaderWrapper.cpp
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -116,26 +116,25 @@ MediaDecoderReaderWrapper::IsWaitingAudi
 bool
 MediaDecoderReaderWrapper::IsWaitingVideoData() const
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   return mVideoWaitRequest.Exists();
 }
 
 RefPtr<MediaDecoderReader::SeekPromise>
-MediaDecoderReaderWrapper::Seek(const SeekTarget& aTarget,
-                                const media::TimeUnit& aEndTime)
+MediaDecoderReaderWrapper::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   SeekTarget adjustedTarget = aTarget;
   adjustedTarget.SetTime(adjustedTarget.GetTime() + StartTime());
-  return InvokeAsync<SeekTarget&&, int64_t>(
+  return InvokeAsync<SeekTarget&&>(
            mReader->OwnerThread(), mReader.get(), __func__,
            &MediaDecoderReader::Seek,
-           Move(adjustedTarget), aEndTime.ToMicroseconds());
+           Move(adjustedTarget));
 }
 
 void
 MediaDecoderReaderWrapper::WaitForData(MediaData::Type aType)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
   auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -62,18 +62,17 @@ public:
   // NOTE: please set callbacks before invoking WaitForData()!
   void WaitForData(MediaData::Type aType);
 
   bool IsRequestingAudioData() const;
   bool IsRequestingVideoData() const;
   bool IsWaitingAudioData() const;
   bool IsWaitingVideoData() const;
 
-  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget,
-                           const media::TimeUnit& aEndTime);
+  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget);
   RefPtr<ShutdownPromise> Shutdown();
 
   void ReleaseResources();
   void ResetDecode(TrackSet aTracks);
 
   nsresult Init() { return mReader->Init(); }
   bool IsWaitForDataSupported() const { return mReader->IsWaitForDataSupported(); }
   bool IsAsync() const { return mReader->IsAsync(); }
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1008,17 +1008,17 @@ private:
     } else {
       mMaster->Reset();
     }
   }
 
   void DoSeek() override
   {
     // Request the demuxer to perform seek.
-    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget, mMaster->Duration())
+    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget)
       ->Then(OwnerThread(), __func__,
              [this] (media::TimeUnit aUnit) {
                OnSeekResolved(aUnit);
              },
              [this] (const MediaResult& aError) {
                OnSeekRejected(aError);
              }));
   }
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -2264,17 +2264,17 @@ MediaFormatReader::OnVideoSkipFailed(Med
       break;
     default:
       NotifyError(TrackType::kVideoTrack, aFailure.mFailure);
       break;
   }
 }
 
 RefPtr<MediaDecoderReader::SeekPromise>
-MediaFormatReader::Seek(const SeekTarget& aTarget, int64_t aUnused)
+MediaFormatReader::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   LOG("aTarget=(%lld)", aTarget.GetTime().ToMicroseconds());
 
   MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty());
   MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise());
   MOZ_DIAGNOSTIC_ASSERT(aTarget.IsVideoOnly() || !mAudio.HasPromise());
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -41,18 +41,17 @@ public:
   RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) override;
 
   RefPtr<MediaDataPromise> RequestAudioData() override;
 
   RefPtr<MetadataPromise> AsyncReadMetadata() override;
 
   void ReadUpdatedMetadata(MediaInfo* aInfo) override;
 
-  RefPtr<SeekPromise>
-  Seek(const SeekTarget& aTarget, int64_t aUnused) override;
+  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) override;
 
 protected:
   void NotifyDataArrived() override;
   void UpdateBuffered() override;
 
 public:
   bool ForceZeroStartTime() const override;
 
new file mode 100644
--- /dev/null
+++ b/dom/media/NextFrameSeekTask.cpp
@@ -0,0 +1,327 @@
+/* -*- 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 "NextFrameSeekTask.h"
+#include "MediaDecoderReaderWrapper.h"
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Assertions.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+
+extern LazyLogModule gMediaSampleLog;
+
+#define SAMPLE_LOG(x, ...) MOZ_LOG(gMediaSampleLog, LogLevel::Debug, \
+  ("[NextFrameSeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
+
+namespace media {
+
+NextFrameSeekTask::NextFrameSeekTask(const void* aDecoderID,
+                                     AbstractThread* aThread,
+                                     MediaDecoderReaderWrapper* aReader,
+                                     const SeekTarget& aTarget,
+                                     const MediaInfo& aInfo,
+                                     const media::TimeUnit& aDuration,
+                                     int64_t aCurrentTime,
+                                     MediaQueue<MediaData>& aAudioQueue,
+                                     MediaQueue<MediaData>& aVideoQueue)
+  : SeekTask(aDecoderID, aThread, aReader, aTarget)
+  , mAudioQueue(aAudioQueue)
+  , mVideoQueue(aVideoQueue)
+  , mCurrentTime(aCurrentTime)
+  , mDuration(aDuration)
+{
+  AssertOwnerThread();
+  MOZ_ASSERT(aInfo.HasVideo());
+}
+
+NextFrameSeekTask::~NextFrameSeekTask()
+{
+  AssertOwnerThread();
+  MOZ_ASSERT(mIsDiscarded);
+}
+
+void
+NextFrameSeekTask::Discard()
+{
+  AssertOwnerThread();
+
+  // Disconnect MDSM.
+  RejectIfExist(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+
+  mIsDiscarded = true;
+}
+
+int64_t
+NextFrameSeekTask::CalculateNewCurrentTime() const
+{
+  AssertOwnerThread();
+
+  // The HTMLMediaElement.currentTime should be updated to the seek target
+  // which has been updated to the next frame's time.
+  return mTarget.GetTime().ToMicroseconds();
+}
+
+void
+NextFrameSeekTask::HandleAudioDecoded(MediaData* aAudio)
+{
+  AssertOwnerThread();
+  MOZ_ASSERT(aAudio);
+  MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
+
+  // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
+  // resolved.
+
+  SAMPLE_LOG("OnAudioDecoded [%lld,%lld]", aAudio->mTime, aAudio->GetEndTime());
+
+  // We accept any audio data here.
+  mSeekedAudioData = aAudio;
+
+  MaybeFinishSeek();
+}
+
+void
+NextFrameSeekTask::HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart)
+{
+  AssertOwnerThread();
+  MOZ_ASSERT(aVideo);
+  MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
+
+  // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
+  // resolved.
+
+  SAMPLE_LOG("OnVideoDecoded [%lld,%lld]", aVideo->mTime, aVideo->GetEndTime());
+
+  if (aVideo->mTime > mCurrentTime) {
+    mSeekedVideoData = aVideo;
+  }
+
+  if (NeedMoreVideo()) {
+    RequestVideoData();
+    return;
+  }
+
+  MaybeFinishSeek();
+}
+
+void
+NextFrameSeekTask::HandleNotDecoded(MediaData::Type aType, const MediaResult& aError)
+{
+  AssertOwnerThread();
+  switch (aType) {
+  case MediaData::AUDIO_DATA:
+  {
+    MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
+
+    SAMPLE_LOG("OnAudioNotDecoded (aError=%u)", aError.Code());
+
+    // We don't really handle audio deocde error here. Let MDSM to trigger further
+    // audio decoding tasks if it needs to play audio, and MDSM will then receive
+    // the decoding state from MediaDecoderReader.
+
+    MaybeFinishSeek();
+    break;
+  }
+  case MediaData::VIDEO_DATA:
+  {
+    MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
+
+    SAMPLE_LOG("OnVideoNotDecoded (aError=%u)", aError.Code());
+
+    if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
+      mIsVideoQueueFinished = true;
+    }
+
+    // Video seek not finished.
+    if (NeedMoreVideo()) {
+      switch (aError.Code()) {
+        case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
+          mReader->WaitForData(MediaData::VIDEO_DATA);
+          break;
+        case NS_ERROR_DOM_MEDIA_CANCELED:
+          RequestVideoData();
+          break;
+        case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
+          MOZ_ASSERT(false, "Shouldn't want more data for ended video.");
+          break;
+        default:
+          // Reject the promise since we can't finish video seek anyway.
+          RejectIfExist(aError, __func__);
+          break;
+      }
+      return;
+    }
+
+    MaybeFinishSeek();
+    break;
+  }
+  default:
+    MOZ_ASSERT_UNREACHABLE("We cannot handle RAW_DATA or NULL_DATA here.");
+  }
+}
+
+void
+NextFrameSeekTask::HandleAudioWaited(MediaData::Type aType)
+{
+  AssertOwnerThread();
+
+  // We don't make an audio decode request here, instead, let MDSM to
+  // trigger further audio decode tasks if MDSM itself needs to play audio.
+  MaybeFinishSeek();
+}
+
+void
+NextFrameSeekTask::HandleVideoWaited(MediaData::Type aType)
+{
+  AssertOwnerThread();
+
+  if (NeedMoreVideo()) {
+    RequestVideoData();
+    return;
+  }
+  MaybeFinishSeek();
+}
+
+void
+NextFrameSeekTask::HandleNotWaited(const WaitForDataRejectValue& aRejection)
+{
+  AssertOwnerThread();
+
+  switch(aRejection.mType) {
+  case MediaData::AUDIO_DATA:
+  {
+    // We don't make an audio decode request here, instead, let MDSM to
+    // trigger further audio decode tasks if MDSM itself needs to play audio.
+    MaybeFinishSeek();
+    break;
+  }
+  case MediaData::VIDEO_DATA:
+  {
+    if (NeedMoreVideo()) {
+      // Reject if we can't finish video seeking.
+      RejectIfExist(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+      return;
+    }
+    MaybeFinishSeek();
+    break;
+  }
+  default:
+    MOZ_ASSERT_UNREACHABLE("We cannot handle RAW_DATA or NULL_DATA here.");
+  }
+}
+
+/*
+ * Remove samples from the queue until aCompare() returns false.
+ * aCompare A function object with the signature bool(int64_t) which returns
+ *          true for samples that should be removed.
+ */
+template <typename Function> static void
+DiscardFrames(MediaQueue<MediaData>& aQueue, const Function& aCompare)
+{
+  while(aQueue.GetSize() > 0) {
+    if (aCompare(aQueue.PeekFront()->mTime)) {
+      RefPtr<MediaData> releaseMe = aQueue.PopFront();
+      continue;
+    }
+    break;
+  }
+}
+
+RefPtr<NextFrameSeekTask::SeekTaskPromise>
+NextFrameSeekTask::Seek()
+{
+  AssertOwnerThread();
+
+  auto currentTime = mCurrentTime;
+  DiscardFrames(mVideoQueue, [currentTime] (int64_t aSampleTime) {
+    return aSampleTime <= currentTime;
+  });
+
+  RefPtr<SeekTaskPromise> promise = mSeekTaskPromise.Ensure(__func__);
+  if (!IsVideoRequestPending() && NeedMoreVideo()) {
+    RequestVideoData();
+  }
+  MaybeFinishSeek(); // Might resolve mSeekTaskPromise and modify audio queue.
+  return promise;
+}
+
+void
+NextFrameSeekTask::RequestVideoData()
+{
+  AssertOwnerThread();
+  mReader->RequestVideoData(false, media::TimeUnit());
+}
+
+bool
+NextFrameSeekTask::NeedMoreVideo() const
+{
+  AssertOwnerThread();
+  // Need to request video when we have none and video queue is not finished.
+  return mVideoQueue.GetSize() == 0 &&
+         !mSeekedVideoData &&
+         !mVideoQueue.IsFinished() &&
+         !mIsVideoQueueFinished;
+}
+
+bool
+NextFrameSeekTask::IsVideoRequestPending() const
+{
+  AssertOwnerThread();
+  return mReader->IsRequestingVideoData() || mReader->IsWaitingVideoData();
+}
+
+bool
+NextFrameSeekTask::IsAudioSeekComplete() const
+{
+  AssertOwnerThread();
+  // Don't finish seek until there are no pending requests. Otherwise, we might
+  // lose audio samples for the promise is resolved asynchronously.
+  return !mReader->IsRequestingAudioData() && !mReader->IsWaitingAudioData();
+}
+
+bool
+NextFrameSeekTask::IsVideoSeekComplete() const
+{
+  AssertOwnerThread();
+  // Don't finish seek until there are no pending requests. Otherwise, we might
+  // lose video samples for the promise is resolved asynchronously.
+  return !IsVideoRequestPending() && !NeedMoreVideo();
+}
+
+void
+NextFrameSeekTask::MaybeFinishSeek()
+{
+  AssertOwnerThread();
+  if (IsAudioSeekComplete() && IsVideoSeekComplete()) {
+    UpdateSeekTargetTime();
+
+    auto time = mTarget.GetTime().ToMicroseconds();
+    DiscardFrames(mAudioQueue, [time] (int64_t aSampleTime) {
+      return aSampleTime < time;
+    });
+
+    Resolve(__func__); // Call to MDSM::SeekCompleted();
+  }
+}
+
+void
+NextFrameSeekTask::UpdateSeekTargetTime()
+{
+  AssertOwnerThread();
+
+  RefPtr<MediaData> data = mVideoQueue.PeekFront();
+  if (data) {
+    mTarget.SetTime(TimeUnit::FromMicroseconds(data->mTime));
+  } else if (mSeekedVideoData) {
+    mTarget.SetTime(TimeUnit::FromMicroseconds(mSeekedVideoData->mTime));
+  } else if (mIsVideoQueueFinished || mVideoQueue.AtEndOfStream()) {
+    mTarget.SetTime(mDuration);
+  } else {
+    MOZ_ASSERT(false, "No data!");
+  }
+}
+} // namespace media
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/NextFrameSeekTask.h
@@ -0,0 +1,89 @@
+/* -*- 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 NEXTFRAME_SEEK_TASK_H
+#define NEXTFRAME_SEEK_TASK_H
+
+#include "SeekTask.h"
+#include "MediaDecoderReader.h"
+
+namespace mozilla {
+namespace media {
+
+/*
+ * While invoking a NextFrameSeekTask, we don't know the seek target time, what
+ * we know is the media's currant position. We use the media's currant position
+ * to find out what the next frame is, by traversing through the video queue or
+ * asking the decoder to decode more video frames. Once we confirm the next
+ * frame, we then know the target time of the NextFrameSeekTask and we update it
+ * so that the MDSM will be able to update the media element's position.
+ */
+
+class NextFrameSeekTask final : public SeekTask {
+public:
+  NextFrameSeekTask(const void* aDecoderID,
+                   AbstractThread* aThread,
+                   MediaDecoderReaderWrapper* aReader,
+                   const SeekTarget& aTarget,
+                   const MediaInfo& aInfo,
+                   const media::TimeUnit& aDuration,
+                   int64_t aCurrentTime,
+                   MediaQueue<MediaData>& aAudioQueue,
+                   MediaQueue<MediaData>& aVideoQueue);
+
+  void Discard() override;
+
+  RefPtr<SeekTaskPromise> Seek() override;
+
+  int64_t CalculateNewCurrentTime() const override;
+
+  void HandleAudioDecoded(MediaData* aAudio) override;
+
+  void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override;
+
+  void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override;
+
+  void HandleAudioWaited(MediaData::Type aType) override;
+
+  void HandleVideoWaited(MediaData::Type aType) override;
+
+  void HandleNotWaited(const WaitForDataRejectValue& aRejection) override;
+
+  ~NextFrameSeekTask();
+
+  void RequestVideoData();
+
+  bool NeedMoreVideo() const;
+
+  bool IsVideoRequestPending() const;
+
+  bool IsAudioSeekComplete() const;
+
+  bool IsVideoSeekComplete() const;
+
+  void MaybeFinishSeek();
+
+  // Update the seek target's time before resolving this seek task, the updated
+  // time will be used in the MDSM::SeekCompleted() to update the MDSM's position.
+  void UpdateSeekTargetTime();
+
+  /*
+   * Data shared with MDSM.
+   */
+  MediaQueue<MediaData>& mAudioQueue;
+  MediaQueue<MediaData>& mVideoQueue;
+
+  /*
+   * Internal state.
+   */
+  const int64_t mCurrentTime;
+  media::TimeUnit mDuration;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif /* NEXTFRAME_SEEK_TASK_H */
new file mode 100644
--- /dev/null
+++ b/dom/media/SeekTask.h
@@ -0,0 +1,117 @@
+/* -*- 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
+
+#include "mozilla/MozPromise.h"
+#include "MediaData.h"          // For MediaData::Type.
+#include "MediaDecoderReader.h" // For WaitForDataRejectValue.
+#include "MediaResult.h"
+#include "SeekTarget.h"
+
+namespace mozilla {
+
+class AbstractThread;
+class MediaData;
+class MediaDecoderReaderWrapper;
+
+namespace media {
+class TimeUnit;
+}
+
+struct SeekTaskResolveValue
+{
+  RefPtr<MediaData> mSeekedAudioData;
+  RefPtr<MediaData> mSeekedVideoData;
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+};
+
+struct SeekTaskRejectValue
+{
+  SeekTaskRejectValue()
+    : mIsAudioQueueFinished(false)
+    , mIsVideoQueueFinished(false)
+    , mError(NS_ERROR_DOM_MEDIA_FATAL_ERR)
+  {
+  }
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+  MediaResult mError;
+};
+
+class SeekTask {
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
+
+public:
+  static const bool IsExclusive = true;
+
+  using SeekTaskPromise =
+    MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
+
+  virtual void Discard() = 0;
+
+  virtual RefPtr<SeekTaskPromise> Seek() = 0;
+
+  virtual int64_t CalculateNewCurrentTime() const = 0;
+
+  virtual void HandleAudioDecoded(MediaData* aAudio) = 0;
+
+  virtual void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) = 0;
+
+  virtual void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) = 0;
+
+  virtual void HandleAudioWaited(MediaData::Type aType) = 0;
+
+  virtual void HandleVideoWaited(MediaData::Type aType) = 0;
+
+  virtual void HandleNotWaited(const WaitForDataRejectValue& aRejection) = 0;
+
+protected:
+  SeekTask(const void* aDecoderID,
+           AbstractThread* aThread,
+           MediaDecoderReaderWrapper* aReader,
+           const SeekTarget& aTarget);
+
+  virtual ~SeekTask();
+
+public:
+  void Resolve(const char* aCallSite);
+
+  void RejectIfExist(const MediaResult& aError, const char* aCallSite);
+
+  void AssertOwnerThread() const;
+
+  AbstractThread* OwnerThread() const;
+
+  /*
+   * Data shared with MDSM.
+   */
+  const void* mDecoderID; // For logging.
+  const RefPtr<AbstractThread> mOwnerThread;
+  const RefPtr<MediaDecoderReaderWrapper> mReader;
+
+  /*
+   * Internal state.
+   */
+  SeekTarget mTarget;
+  MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
+  bool mIsDiscarded;
+
+  /*
+   * Information which are going to be returned to MDSM.
+   */
+  RefPtr<MediaData> mSeekedAudioData;
+  RefPtr<MediaData> mSeekedVideoData;
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+};
+
+} // namespace mozilla
+
+#endif /* SEEK_TASK_H */
--- a/dom/media/android/AndroidMediaReader.cpp
+++ b/dom/media/android/AndroidMediaReader.cpp
@@ -308,17 +308,17 @@ bool AndroidMediaReader::DecodeAudioData
                               frames,
                               source.mAudioChannels,
                               MPCopy(static_cast<uint8_t *>(source.mData),
                                      source.mSize,
                                      source.mAudioChannels));
 }
 
 RefPtr<MediaDecoderReader::SeekPromise>
-AndroidMediaReader::Seek(const SeekTarget& aTarget, int64_t aEndTime)
+AndroidMediaReader::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
   if (mHasAudio && mHasVideo) {
     // The decoder seeks/demuxes audio and video streams separately. So if
     // we seek both audio and video to aTarget, the audio stream can typically
     // seek closer to the seek target, since typically every audio block is
--- a/dom/media/android/AndroidMediaReader.h
+++ b/dom/media/android/AndroidMediaReader.h
@@ -43,17 +43,17 @@ public:
 
   nsresult ResetDecode(TrackSet aTracks = TrackSet(TrackInfo::kAudioTrack,
                                                    TrackInfo::kVideoTrack)) override;
 
   bool DecodeAudioData() override;
   bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override;
 
   nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
-  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget, int64_t aEndTime) override;
+  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) override;
 
   RefPtr<ShutdownPromise> Shutdown() override;
 
   class ImageBufferCallback : public MPAPI::BufferCallback {
     typedef mozilla::layers::Image Image;
 
   public:
     ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -315,17 +315,17 @@ bool
 DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip,
                                    int64_t aTimeThreshold)
 {
   MOZ_ASSERT(OnTaskQueue());
   return false;
 }
 
 RefPtr<MediaDecoderReader::SeekPromise>
-DirectShowReader::Seek(const SeekTarget& aTarget, int64_t aEndTime)
+DirectShowReader::Seek(const SeekTarget& aTarget)
 {
   nsresult res = SeekInternal(aTarget.GetTime().ToMicroseconds());
   if (NS_FAILED(res)) {
     return SeekPromise::CreateAndReject(res, __func__);
   } else {
     return SeekPromise::CreateAndResolve(aTarget.GetTime(), __func__);
   }
 }
--- a/dom/media/directshow/DirectShowReader.h
+++ b/dom/media/directshow/DirectShowReader.h
@@ -52,18 +52,17 @@ public:
 
   bool DecodeAudioData() override;
   bool DecodeVideoFrame(bool &aKeyframeSkip,
                         int64_t aTimeThreshold) override;
 
   nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) override;
 
-  RefPtr<SeekPromise>
-  Seek(const SeekTarget& aTarget, int64_t aEndTime) override;
+  RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) override;
 
   static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER;
 
 private:
   // Notifies the filter graph that playback is complete. aStatus is
   // the code to send to the filter graph. Always returns false, so
   // that we can just "return Finish()" from DecodeAudioData().
   bool Finish(HRESULT aStatus);