Bug 1319992: P3. Remove no longer used seeking argument. r=jwwang
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 30 Nov 2016 01:42:50 +1100
changeset 326482 909eee913f30f61712f54f3a8ff7eca770c506e1
parent 326481 7df4d36392e7519bb24968330aef458737f11605
child 326483 4ea0f7d805d9aeb490a0c8553fb2b29cd453949c
push id31105
push userkwierso@gmail.com
push dateTue, 20 Dec 2016 19:35:18 +0000
treeherdermozilla-central@0f3603e365f0 [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.ref(), mMaster->Duration())
+    mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget.ref())
       ->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
@@ -2265,17 +2265,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);