Bug 1281090. Part 3 - replace MediaDecoderReaderWrapper::Set{Audio,Video}Callback with MediaCallback. r=kaku. draft
authorJW Wang <jwwang@mozilla.com>
Tue, 14 Jun 2016 11:22:45 +0800
changeset 380741 d4ef0e44106f939ed0695e5eef6df181e9bd0ec9
parent 380740 045d7e18650aa4a17c05f35f33fc4fa80e9a030c
child 380742 05c147239fa97d754439ce1154d34d29c22542df
push id21310
push userjwwang@mozilla.com
push dateThu, 23 Jun 2016 02:27:34 +0000
reviewerskaku
bugs1281090
milestone50.0a1
Bug 1281090. Part 3 - replace MediaDecoderReaderWrapper::Set{Audio,Video}Callback with MediaCallback. r=kaku. MozReview-Commit-ID: DleK3s5F3sD
dom/media/AccurateSeekTask.cpp
dom/media/AccurateSeekTask.h
dom/media/MediaDecoderReaderWrapper.cpp
dom/media/MediaDecoderReaderWrapper.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/NextFrameSeekTask.cpp
dom/media/NextFrameSeekTask.h
--- a/dom/media/AccurateSeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -619,61 +619,52 @@ AccurateSeekTask::OnVideoNotDecoded(Medi
   }
 }
 
 void
 AccurateSeekTask::SetMediaDecoderReaderWrapperCallback()
 {
   AssertOwnerThread();
 
-  mAudioCallbackID =
-    mReader->SetAudioCallback(this, &AccurateSeekTask::OnAudioDecoded,
-                                    &AccurateSeekTask::OnAudioNotDecoded);
-
-  mVideoCallbackID =
-    mReader->SetVideoCallback(this, &AccurateSeekTask::OnVideoDecoded,
-                                    &AccurateSeekTask::OnVideoNotDecoded);
+  mAudioCallback = mReader->AudioCallback().Connect(
+    OwnerThread(), [this] (AudioCallbackData aData) {
+    if (aData.is<MediaData*>()) {
+      OnAudioDecoded(aData.as<MediaData*>());
+    } else {
+      OnAudioNotDecoded(aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
 
-  RefPtr<AccurateSeekTask> self = this;
-  mWaitAudioCallbackID =
-    mReader->SetWaitAudioCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->AssertOwnerThread();
-        self->EnsureAudioDecodeTaskQueued();
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {
-        self->AssertOwnerThread();
-      });
+  mVideoCallback = mReader->VideoCallback().Connect(
+    OwnerThread(), [this] (VideoCallbackData aData) {
+    typedef Tuple<MediaData*, TimeStamp> Type;
+    if (aData.is<Type>()) {
+      OnVideoDecoded(Get<0>(aData.as<Type>()));
+    } else {
+      OnVideoNotDecoded(aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
 
-  mWaitVideoCallbackID =
-    mReader->SetWaitVideoCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->AssertOwnerThread();
-        self->EnsureVideoDecodeTaskQueued();
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {
-        self->AssertOwnerThread();
-      });
+  mAudioWaitCallback = mReader->AudioWaitCallback().Connect(
+    OwnerThread(), [this] (WaitCallbackData aData) {
+    if (aData.is<MediaData::Type>()) {
+      EnsureAudioDecodeTaskQueued();
+    }
+  });
 
-  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);
+  mVideoWaitCallback = mReader->VideoWaitCallback().Connect(
+    OwnerThread(), [this] (WaitCallbackData aData) {
+    if (aData.is<MediaData::Type>()) {
+      EnsureVideoDecodeTaskQueued();
+    }
+  });
 }
 
 void
 AccurateSeekTask::CancelMediaDecoderReaderWrapperCallback()
 {
   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 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);
+  mAudioCallback.Disconnect();
+  mVideoCallback.Disconnect();
+  mAudioWaitCallback.Disconnect();
+  mVideoWaitCallback.Disconnect();
 }
 } // namespace mozilla
--- a/dom/media/AccurateSeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -94,17 +94,18 @@ private:
   // 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;
+
+  MediaEventListener mAudioCallback;
+  MediaEventListener mVideoCallback;
+  MediaEventListener mAudioWaitCallback;
+  MediaEventListener mVideoWaitCallback;
 };
 
 } // namespace mozilla
 
 #endif /* ACCURATE_SEEK_TASK_H */
--- a/dom/media/MediaDecoderReaderWrapper.cpp
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -139,20 +139,16 @@ private:
 };
 
 MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(bool aIsRealTime,
                                                      AbstractThread* aOwnerThread,
                                                      MediaDecoderReader* aReader)
   : mForceZeroStartTime(aIsRealTime || aReader->ForceZeroStartTime())
   , mOwnerThread(aOwnerThread)
   , mReader(aReader)
-  , mAudioCallbackID("AudioCallbackID")
-  , mVideoCallbackID("VideoCallbackID")
-  , mWaitAudioCallbackID("WaitAudioCallbackID")
-  , mWaitVideoCallbackID("WaitVideoCallbackID")
 {}
 
 MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper()
 {}
 
 media::TimeUnit
 MediaDecoderReaderWrapper::StartTime() const
 {
@@ -178,89 +174,50 @@ RefPtr<HaveStartTimePromise>
 MediaDecoderReaderWrapper::AwaitStartTime()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
   return mStartTimeRendezvous->AwaitStartTime();
 }
 
 void
-MediaDecoderReaderWrapper::CancelAudioCallback(CallbackID aID)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(aID == mAudioCallbackID);
-  ++mAudioCallbackID;
-  mRequestAudioDataCB = nullptr;
-}
-
-void
-MediaDecoderReaderWrapper::CancelVideoCallback(CallbackID aID)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(aID == mVideoCallbackID);
-  ++mVideoCallbackID;
-  mRequestVideoDataCB = nullptr;
-}
-
-void
-MediaDecoderReaderWrapper::CancelWaitAudioCallback(CallbackID aID)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(aID == mWaitAudioCallbackID);
-  ++mWaitAudioCallbackID;
-  mWaitAudioDataCB = nullptr;
-}
-
-void
-MediaDecoderReaderWrapper::CancelWaitVideoCallback(CallbackID aID)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(aID == mWaitVideoCallbackID);
-  ++mWaitVideoCallbackID;
-  mWaitVideoDataCB = nullptr;
-}
-
-void
 MediaDecoderReaderWrapper::RequestAudioData()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
-  MOZ_ASSERT(mRequestAudioDataCB, "Request audio data without callback!");
 
   auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
                        &MediaDecoderReader::RequestAudioData);
 
   if (!mStartTimeRendezvous->HaveStartTime()) {
     p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
                 &StartTimeRendezvous::ProcessFirstSample<MediaData::AUDIO_DATA>,
                 &StartTimeRendezvous::FirstSampleRejected<MediaData::AUDIO_DATA>)
          ->CompletionPromise();
   }
 
   RefPtr<MediaDecoderReaderWrapper> self = this;
   mAudioDataRequest.Begin(p->Then(mOwnerThread, __func__,
     [self] (MediaData* aAudioSample) {
-      MOZ_ASSERT(self->mRequestAudioDataCB);
       self->mAudioDataRequest.Complete();
-      self->OnSampleDecoded(self->mRequestAudioDataCB.get(), aAudioSample, TimeStamp());
+      aAudioSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
+      self->mAudioCallback.Notify(AsVariant(aAudioSample));
     },
     [self] (MediaDecoderReader::NotDecodedReason aReason) {
-      MOZ_ASSERT(self->mRequestAudioDataCB);
       self->mAudioDataRequest.Complete();
-      self->OnNotDecoded(self->mRequestAudioDataCB.get(), aReason);
+      self->mAudioCallback.Notify(AsVariant(aReason));
     }));
 }
 
 void
 MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
                                             media::TimeUnit aTimeThreshold)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
-  MOZ_ASSERT(mRequestVideoDataCB, "Request video data without callback!");
 
   // Time the video decode and send this value back to callbacks who accept
   // a TimeStamp as its second parameter.
   TimeStamp videoDecodeStartTime = TimeStamp::Now();
 
   if (aTimeThreshold.ToMicroseconds() > 0 &&
       mStartTimeRendezvous->HaveStartTime()) {
     aTimeThreshold += StartTime();
@@ -275,24 +232,23 @@ MediaDecoderReaderWrapper::RequestVideoD
                 &StartTimeRendezvous::ProcessFirstSample<MediaData::VIDEO_DATA>,
                 &StartTimeRendezvous::FirstSampleRejected<MediaData::VIDEO_DATA>)
          ->CompletionPromise();
   }
 
   RefPtr<MediaDecoderReaderWrapper> self = this;
   mVideoDataRequest.Begin(p->Then(mOwnerThread, __func__,
     [self, videoDecodeStartTime] (MediaData* aVideoSample) {
-      MOZ_ASSERT(self->mRequestVideoDataCB);
       self->mVideoDataRequest.Complete();
-      self->OnSampleDecoded(self->mRequestVideoDataCB.get(), aVideoSample, videoDecodeStartTime);
+      aVideoSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
+      self->mVideoCallback.Notify(AsVariant(MakeTuple(aVideoSample, videoDecodeStartTime)));
     },
     [self] (MediaDecoderReader::NotDecodedReason aReason) {
-      MOZ_ASSERT(self->mRequestVideoDataCB);
       self->mVideoDataRequest.Complete();
-      self->OnNotDecoded(self->mRequestVideoDataCB.get(), aReason);
+      self->mVideoCallback.Notify(AsVariant(aReason));
     }));
 }
 
 bool
 MediaDecoderReaderWrapper::IsRequestingAudioData() const
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   return mAudioDataRequest.Exists();
@@ -328,40 +284,37 @@ MediaDecoderReaderWrapper::Seek(SeekTarg
                      &MediaDecoderReader::Seek, aTarget,
                      aEndTime.ToMicroseconds());
 }
 
 void
 MediaDecoderReaderWrapper::WaitForData(MediaData::Type aType)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(WaitCallbackRef(aType));
 
   auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
                        &MediaDecoderReader::WaitForData, aType);
 
   RefPtr<MediaDecoderReaderWrapper> self = this;
   WaitRequestRef(aType).Begin(p->Then(mOwnerThread, __func__,
     [self] (MediaData::Type aType) {
-      MOZ_ASSERT(self->WaitCallbackRef(aType));
       self->WaitRequestRef(aType).Complete();
-      self->WaitCallbackRef(aType)->OnResolved(aType);
+      self->WaitCallbackRef(aType).Notify(AsVariant(aType));
     },
     [self, aType] (WaitForDataRejectValue aRejection) {
-      MOZ_ASSERT(self->WaitCallbackRef(aType));
       self->WaitRequestRef(aType).Complete();
-      self->WaitCallbackRef(aType)->OnRejected(aRejection);
+      self->WaitCallbackRef(aType).Notify(AsVariant(aRejection));
     }));
 }
 
-UniquePtr<MediaDecoderReaderWrapper::WaitForDataCallbackBase>&
+MediaCallbackExc<WaitCallbackData>&
 MediaDecoderReaderWrapper::WaitCallbackRef(MediaData::Type aType)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  return aType == MediaData::AUDIO_DATA ? mWaitAudioDataCB : mWaitVideoDataCB;
+  return aType == MediaData::AUDIO_DATA ? mAudioWaitCallback : mVideoWaitCallback;
 }
 
 MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>&
 MediaDecoderReaderWrapper::WaitRequestRef(MediaData::Type aType)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
 }
@@ -413,18 +366,16 @@ MediaDecoderReaderWrapper::ResetDecode(T
                                 aTracks);
   mReader->OwnerThread()->Dispatch(r.forget());
 }
 
 RefPtr<ShutdownPromise>
 MediaDecoderReaderWrapper::Shutdown()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(!mRequestAudioDataCB);
-  MOZ_ASSERT(!mRequestVideoDataCB);
   MOZ_ASSERT(!mAudioDataRequest.Exists());
   MOZ_ASSERT(!mVideoDataRequest.Exists());
 
   mShutdown = true;
   if (mStartTimeRendezvous) {
     mStartTimeRendezvous->Destroy();
     mStartTimeRendezvous = nullptr;
   }
@@ -454,31 +405,9 @@ MediaDecoderReaderWrapper::OnMetadataRea
         self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds());
       },
       [] () {
         NS_WARNING("Setting start time on reader failed");
       });
   }
 }
 
-void
-MediaDecoderReaderWrapper::OnSampleDecoded(CallbackBase* aCallback,
-                                           MediaData* aSample,
-                                           TimeStamp aDecodeStartTime)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(!mShutdown);
-
-  aSample->AdjustForStartTime(StartTime().ToMicroseconds());
-  aCallback->OnResolved(aSample, aDecodeStartTime);
-}
-
-void
-MediaDecoderReaderWrapper::OnNotDecoded(CallbackBase* aCallback,
-                                        MediaDecoderReader::NotDecodedReason aReason)
-{
-  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-  MOZ_ASSERT(!mShutdown);
-
-  aCallback->OnRejected(aReason);
-}
-
 } // namespace mozilla
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -7,365 +7,62 @@
 #ifndef MediaDecoderReaderWrapper_h_
 #define MediaDecoderReaderWrapper_h_
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 
 #include "MediaDecoderReader.h"
-#include "MediaCallbackID.h"
+#include "MediaEventSource.h"
 
 namespace mozilla {
 
 class StartTimeRendezvous;
 
 typedef MozPromise<bool, bool, /* isExclusive = */ false> HaveStartTimePromise;
 
+typedef Variant<MediaData*, MediaDecoderReader::NotDecodedReason> AudioCallbackData;
+typedef Variant<Tuple<MediaData*, TimeStamp>, MediaDecoderReader::NotDecodedReason> VideoCallbackData;
+typedef Variant<MediaData::Type, WaitForDataRejectValue> WaitCallbackData;
+
 /**
  * A wrapper around MediaDecoderReader to offset the timestamps of Audio/Video
  * samples by the start time to ensure MDSM can always assume zero start time.
  * It also adjusts the seek target passed to Seek() to ensure correct seek time
  * is passed to the underlying reader.
  */
 class MediaDecoderReaderWrapper {
   typedef MediaDecoderReader::MetadataPromise MetadataPromise;
   typedef MediaDecoderReader::MediaDataPromise MediaDataPromise;
   typedef MediaDecoderReader::SeekPromise SeekPromise;
   typedef MediaDecoderReader::WaitForDataPromise WaitForDataPromise;
   typedef MediaDecoderReader::BufferedUpdatePromise BufferedUpdatePromise;
   typedef MediaDecoderReader::TrackSet TrackSet;
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper);
 
-  /*
-   * Type 1: void(MediaData*)
-   *         void(RefPtr<MediaData>)
-   */
-  template <typename T>
-  class ArgType1CheckHelper {
-    template<typename C, typename... Ts>
-    static TrueType
-    test(void(C::*aMethod)(Ts...),
-         decltype((DeclVal<C>().*aMethod)(DeclVal<MediaData*>()), 0));
-
-    template <typename F>
-    static TrueType
-    test(F&&, decltype(DeclVal<F>()(DeclVal<MediaData*>()), 0));
-
-    static FalseType test(...);
-  public:
-    typedef decltype(test(DeclVal<T>(), 0)) Type;
-  };
-
-  template <typename T>
-  struct ArgType1Check : public ArgType1CheckHelper<T>::Type {};
-
-  /*
-   * Type 2: void(MediaData*, TimeStamp)
-   *         void(RefPtr<MediaData>, TimeStamp)
-   *         void(MediaData*, TimeStamp&)
-   *         void(RefPtr<MediaData>, const TimeStamp&&)
-   */
-  template <typename T>
-  class ArgType2CheckHelper {
-
-    template<typename C, typename... Ts>
-    static TrueType
-    test(void(C::*aMethod)(Ts...),
-         decltype((DeclVal<C>().*aMethod)(DeclVal<MediaData*>(), DeclVal<TimeStamp>()), 0));
-
-    template <typename F>
-    static TrueType
-    test(F&&, decltype(DeclVal<F>()(DeclVal<MediaData*>(), DeclVal<TimeStamp>()), 0));
-
-    static FalseType test(...);
-  public:
-    typedef decltype(test(DeclVal<T>(), 0)) Type;
-  };
-
-  template <typename T>
-  struct ArgType2Check : public ArgType2CheckHelper<T>::Type {};
-
-  struct CallbackBase
-  {
-    virtual ~CallbackBase() {}
-    virtual void OnResolved(MediaData*, TimeStamp) = 0;
-    virtual void OnRejected(MediaDecoderReader::NotDecodedReason) = 0;
-  };
-
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  struct MethodCallback : public CallbackBase
-  {
-    MethodCallback(ThisType* aThis, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
-      : mThis(aThis), mResolveMethod(aResolveMethod), mRejectMethod(aRejectMethod)
-    {
-    }
-
-    template<typename F>
-    typename EnableIf<ArgType1Check<F>::value, void>::Type
-    CallHelper(MediaData* aSample, TimeStamp)
-    {
-      (mThis->*mResolveMethod)(aSample);
-    }
-
-    template<typename F>
-    typename EnableIf<ArgType2Check<F>::value, void>::Type
-    CallHelper(MediaData* aSample, TimeStamp aDecodeStartTime)
-    {
-      (mThis->*mResolveMethod)(aSample, aDecodeStartTime);
-    }
-
-    void OnResolved(MediaData* aSample, TimeStamp aDecodeStartTime) override
-    {
-      CallHelper<ResolveMethodType>(aSample, aDecodeStartTime);
-    }
-
-    void OnRejected(MediaDecoderReader::NotDecodedReason aReason) override
-    {
-      (mThis->*mRejectMethod)(aReason);
-    }
-
-    RefPtr<ThisType> mThis;
-    ResolveMethodType mResolveMethod;
-    RejectMethodType mRejectMethod;
-  };
-
-  template<typename ResolveFunctionType, typename RejectFunctionType>
-  struct FunctionCallback : public CallbackBase
-  {
-    FunctionCallback(ResolveFunctionType&& aResolveFuntion, RejectFunctionType&& aRejectFunction)
-      : mResolveFuntion(Move(aResolveFuntion)), mRejectFunction(Move(aRejectFunction))
-    {
-    }
-
-    template<typename F>
-    typename EnableIf<ArgType1Check<F>::value, void>::Type
-    CallHelper(MediaData* aSample, TimeStamp)
-    {
-      mResolveFuntion(aSample);
-    }
-
-    template<typename F>
-    typename EnableIf<ArgType2Check<F>::value, void>::Type
-    CallHelper(MediaData* aSample, TimeStamp aDecodeStartTime)
-    {
-      mResolveFuntion(aSample, aDecodeStartTime);
-    }
-
-    void OnResolved(MediaData* aSample, TimeStamp aDecodeStartTime) override
-    {
-      CallHelper<ResolveFunctionType>(aSample, aDecodeStartTime);
-    }
-
-    void OnRejected(MediaDecoderReader::NotDecodedReason aReason) override
-    {
-      mRejectFunction(aReason);
-    }
-
-    ResolveFunctionType mResolveFuntion;
-    RejectFunctionType mRejectFunction;
-  };
-
-  struct WaitForDataCallbackBase
-  {
-    virtual ~WaitForDataCallbackBase() {}
-    virtual void OnResolved(MediaData::Type aType) = 0;
-    virtual void OnRejected(WaitForDataRejectValue aRejection) = 0;
-  };
-
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  struct WaitForDataMethodCallback : public WaitForDataCallbackBase
-  {
-    WaitForDataMethodCallback(ThisType* aThis, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
-      : mThis(aThis), mResolveMethod(aResolveMethod), mRejectMethod(aRejectMethod)
-    {
-    }
-
-    void OnResolved(MediaData::Type aType) override
-    {
-      (mThis->*mResolveMethod)(aType);
-    }
-
-    void OnRejected(WaitForDataRejectValue aRejection) override
-    {
-      (mThis->*mRejectMethod)(aRejection);
-    }
-
-    RefPtr<ThisType> mThis;
-    ResolveMethodType mResolveMethod;
-    RejectMethodType mRejectMethod;
-  };
-
-  template<typename ResolveFunctionType, typename RejectFunctionType>
-  struct WaitForDataFunctionCallback : public WaitForDataCallbackBase
-  {
-    WaitForDataFunctionCallback(ResolveFunctionType&& aResolveFuntion, RejectFunctionType&& aRejectFunction)
-      : mResolveFuntion(Move(aResolveFuntion)), mRejectFunction(Move(aRejectFunction))
-    {
-    }
-
-    void OnResolved(MediaData::Type aType) override
-    {
-      mResolveFuntion(aType);
-    }
-
-    void OnRejected(WaitForDataRejectValue aRejection) override
-    {
-      mRejectFunction(aRejection);
-    }
-
-    ResolveFunctionType mResolveFuntion;
-    RejectFunctionType mRejectFunction;
-  };
+private:
+  MediaCallbackExc<AudioCallbackData> mAudioCallback;
+  MediaCallbackExc<VideoCallbackData> mVideoCallback;
+  MediaCallbackExc<WaitCallbackData> mAudioWaitCallback;
+  MediaCallbackExc<WaitCallbackData> mVideoWaitCallback;
 
 public:
   MediaDecoderReaderWrapper(bool aIsRealTime,
                             AbstractThread* aOwnerThread,
                             MediaDecoderReader* aReader);
 
   media::TimeUnit StartTime() const;
   RefPtr<MetadataPromise> ReadMetadata();
   RefPtr<HaveStartTimePromise> AwaitStartTime();
 
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  CallbackID
-  SetAudioCallback(ThisType* aThisVal,
-                   ResolveMethodType aResolveMethod,
-                   RejectMethodType aRejectMethod)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mRequestAudioDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mRequestAudioDataCB.reset(
-      new MethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
-            aThisVal, aResolveMethod, aRejectMethod));
-
-    return mAudioCallbackID;
-  }
-
-  template<typename ResolveFunction, typename RejectFunction>
-  CallbackID
-  SetAudioCallback(ResolveFunction&& aResolveFunction,
-                   RejectFunction&& aRejectFunction)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mRequestAudioDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mRequestAudioDataCB.reset(
-      new FunctionCallback<ResolveFunction, RejectFunction>(
-            Move(aResolveFunction), Move(aRejectFunction)));
-
-    return mAudioCallbackID;
-  }
-
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  CallbackID
-  SetVideoCallback(ThisType* aThisVal,
-                   ResolveMethodType aResolveMethod,
-                   RejectMethodType aRejectMethod)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mRequestVideoDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mRequestVideoDataCB.reset(
-      new MethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
-            aThisVal, aResolveMethod, aRejectMethod));
-
-    return mVideoCallbackID;
-  }
-
-  template<typename ResolveFunction, typename RejectFunction>
-  CallbackID
-  SetVideoCallback(ResolveFunction&& aResolveFunction,
-                   RejectFunction&& aRejectFunction)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mRequestVideoDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mRequestVideoDataCB.reset(
-      new FunctionCallback<ResolveFunction, RejectFunction>(
-            Move(aResolveFunction), Move(aRejectFunction)));
-
-    return mVideoCallbackID;
-  }
-
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  CallbackID
-  SetWaitAudioCallback(ThisType* aThisVal,
-                       ResolveMethodType aResolveMethod,
-                       RejectMethodType aRejectMethod)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mWaitAudioDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mWaitAudioDataCB.reset(
-      new WaitForDataMethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
-            aThisVal, aResolveMethod, aRejectMethod));
-
-    return mWaitAudioCallbackID;
-  }
-
-  template<typename ResolveFunction, typename RejectFunction>
-  CallbackID
-  SetWaitAudioCallback(ResolveFunction&& aResolveFunction,
-                       RejectFunction&& aRejectFunction)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mWaitAudioDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mWaitAudioDataCB.reset(
-      new WaitForDataFunctionCallback<ResolveFunction, RejectFunction>(
-            Move(aResolveFunction), Move(aRejectFunction)));
-
-    return mWaitAudioCallbackID;
-  }
-
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  CallbackID
-  SetWaitVideoCallback(ThisType* aThisVal,
-                       ResolveMethodType aResolveMethod,
-                       RejectMethodType aRejectMethod)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mWaitVideoDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mWaitVideoDataCB.reset(
-      new WaitForDataMethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
-            aThisVal, aResolveMethod, aRejectMethod));
-
-    return mWaitVideoCallbackID;
-  }
-
-  template<typename ResolveFunction, typename RejectFunction>
-  CallbackID
-  SetWaitVideoCallback(ResolveFunction&& aResolveFunction,
-                       RejectFunction&& aRejectFunction)
-  {
-    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-    MOZ_ASSERT(!mWaitVideoDataCB,
-               "Please cancel the original callback before setting a new one.");
-
-    mWaitVideoDataCB.reset(
-      new WaitForDataFunctionCallback<ResolveFunction, RejectFunction>(
-            Move(aResolveFunction), Move(aRejectFunction)));
-
-    return mWaitVideoCallbackID;
-  }
-
-  void CancelAudioCallback(CallbackID aID);
-  void CancelVideoCallback(CallbackID aID);
-  void CancelWaitAudioCallback(CallbackID aID);
-  void CancelWaitVideoCallback(CallbackID aID);
+  decltype(mAudioCallback)& AudioCallback() { return mAudioCallback; }
+  decltype(mVideoCallback)& VideoCallback() { return mVideoCallback; }
+  decltype(mAudioWaitCallback)& AudioWaitCallback() { return mAudioWaitCallback; }
+  decltype(mVideoWaitCallback)& VideoWaitCallback() { return mVideoWaitCallback; }
 
   // NOTE: please set callbacks before requesting audio/video data!
   void RequestAudioData();
   void RequestVideoData(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
 
   // NOTE: please set callbacks before invoking WaitForData()!
   void WaitForData(MediaData::Type aType);
 
@@ -423,44 +120,27 @@ public:
   void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
 #endif
 
 private:
   ~MediaDecoderReaderWrapper();
 
   void OnMetadataRead(MetadataHolder* aMetadata);
   void OnMetadataNotRead() {}
-  void OnSampleDecoded(CallbackBase* aCallback, MediaData* aSample,
-                       TimeStamp aVideoDecodeStartTime);
-  void OnNotDecoded(CallbackBase* aCallback,
-                    MediaDecoderReader::NotDecodedReason aReason);
-
-  UniquePtr<WaitForDataCallbackBase>& WaitCallbackRef(MediaData::Type aType);
+  MediaCallbackExc<WaitCallbackData>& WaitCallbackRef(MediaData::Type aType);
   MozPromiseRequestHolder<WaitForDataPromise>& WaitRequestRef(MediaData::Type aType);
 
   const bool mForceZeroStartTime;
   const RefPtr<AbstractThread> mOwnerThread;
   const RefPtr<MediaDecoderReader> mReader;
 
   bool mShutdown = false;
   RefPtr<StartTimeRendezvous> mStartTimeRendezvous;
 
-  UniquePtr<CallbackBase> mRequestAudioDataCB;
-  UniquePtr<CallbackBase> mRequestVideoDataCB;
-  UniquePtr<WaitForDataCallbackBase> mWaitAudioDataCB;
-  UniquePtr<WaitForDataCallbackBase> mWaitVideoDataCB;
   MozPromiseRequestHolder<MediaDataPromise> mAudioDataRequest;
   MozPromiseRequestHolder<MediaDataPromise> mVideoDataRequest;
   MozPromiseRequestHolder<WaitForDataPromise> mAudioWaitRequest;
   MozPromiseRequestHolder<WaitForDataPromise> mVideoWaitRequest;
-
-  /*
-   * These callback ids are used to prevent mis-canceling callback.
-   */
-  CallbackID mAudioCallbackID;
-  CallbackID mVideoCallbackID;
-  CallbackID mWaitAudioCallbackID;
-  CallbackID mWaitVideoCallbackID;
 };
 
 } // namespace mozilla
 
 #endif // MediaDecoderReaderWrapper_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -913,61 +913,61 @@ nsresult MediaDecoderStateMachine::Init(
   OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaDecoderStateMachine::ReadMetadata));
 
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::SetMediaDecoderReaderWrapperCallback()
 {
-  mAudioCallbackID =
-    mReader->SetAudioCallback(this,
-                              &MediaDecoderStateMachine::OnAudioDecoded,
-                              &MediaDecoderStateMachine::OnAudioNotDecoded);
-
-  mVideoCallbackID =
-    mReader->SetVideoCallback(this,
-                              &MediaDecoderStateMachine::OnVideoDecoded,
-                              &MediaDecoderStateMachine::OnVideoNotDecoded);
-
-  RefPtr<MediaDecoderStateMachine> 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("MDSM set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
-  DECODER_LOG("MDSM set video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
-  DECODER_LOG("MDSM set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-  DECODER_LOG("MDSM set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
+  MOZ_ASSERT(OnTaskQueue());
+
+  mAudioCallback = mReader->AudioCallback().Connect(
+    mTaskQueue, [this] (AudioCallbackData aData) {
+    if (aData.is<MediaData*>()) {
+      OnAudioDecoded(aData.as<MediaData*>());
+    } else {
+      OnNotDecoded(MediaData::AUDIO_DATA, aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
+
+  mVideoCallback = mReader->VideoCallback().Connect(
+    mTaskQueue, [this] (VideoCallbackData aData) {
+    typedef Tuple<MediaData*, TimeStamp> Type;
+    if (aData.is<Type>()) {
+      auto&& v = aData.as<Type>();
+      OnVideoDecoded(Get<0>(v), Get<1>(v));
+    } else {
+      OnNotDecoded(MediaData::VIDEO_DATA, aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
+
+  mAudioWaitCallback = mReader->AudioWaitCallback().Connect(
+    mTaskQueue, [this] (WaitCallbackData aData) {
+    if (aData.is<MediaData::Type>()) {
+      EnsureAudioDecodeTaskQueued();
+    }
+  });
+
+  mVideoWaitCallback = mReader->VideoWaitCallback().Connect(
+    mTaskQueue, [this] (WaitCallbackData aData) {
+    if (aData.is<MediaData::Type>()) {
+      EnsureVideoDecodeTaskQueued();
+    }
+  });
 }
 
 void
 MediaDecoderStateMachine::CancelMediaDecoderReaderWrapperCallback()
 {
-    DECODER_LOG("MDSM cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
-    mReader->CancelAudioCallback(mAudioCallbackID);
-
-    DECODER_LOG("MDSM cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
-    mReader->CancelVideoCallback(mVideoCallbackID);
-
-    DECODER_LOG("MDSM cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-    mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
-
-    DECODER_LOG("MDSM cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-    mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
+  MOZ_ASSERT(OnTaskQueue());
+  mAudioCallback.Disconnect();
+  mVideoCallback.Disconnect();
+  mAudioWaitCallback.Disconnect();
+  mVideoWaitCallback.Disconnect();
 }
 
 void MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("StopPlayback()");
 
   mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped);
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -354,26 +354,16 @@ private:
   // be held.
   bool IsPlaying() const;
 
   // TODO: Those callback function may receive demuxed-only data.
   // Need to figure out a suitable API name for this case.
   void OnAudioDecoded(MediaData* aAudioSample);
   void OnVideoDecoded(MediaData* aVideoSample, TimeStamp aDecodeStartTime);
   void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
-  void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    OnNotDecoded(MediaData::AUDIO_DATA, aReason);
-  }
-  void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    OnNotDecoded(MediaData::VIDEO_DATA, aReason);
-  }
 
   // Resets all state related to decoding and playback, emptying all buffers
   // and aborting all pending operations on the decode task queue.
   void Reset(TrackSet aTracks = TrackSet(TrackInfo::kAudioTrack,
                                          TrackInfo::kVideoTrack));
 
 protected:
   virtual ~MediaDecoderStateMachine();
@@ -832,22 +822,22 @@ private:
   // playback. The flags below are true when the corresponding stream is
   // being "prerolled".
   bool mIsAudioPrerolling;
   bool mIsVideoPrerolling;
 
   // Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
   // should exist at any given moment.
 
-  CallbackID mAudioCallbackID;
-  CallbackID mWaitAudioCallbackID;
+  MediaEventListener mAudioCallback;
+  MediaEventListener mVideoCallback;
+  MediaEventListener mAudioWaitCallback;
+  MediaEventListener mVideoWaitCallback;
+
   const char* AudioRequestStatus() const;
-
-  CallbackID mVideoCallbackID;
-  CallbackID mWaitVideoCallbackID;
   const char* VideoRequestStatus() const;
 
   void OnSuspendTimerResolved();
   void OnSuspendTimerRejected();
 
   // True if we shouldn't play our audio (but still write it to any capturing
   // streams). When this is true, the audio thread will never start again after
   // it has stopped.
--- a/dom/media/NextFrameSeekTask.cpp
+++ b/dom/media/NextFrameSeekTask.cpp
@@ -412,68 +412,57 @@ NextFrameSeekTask::OnVideoNotDecoded(Med
 
 void
 NextFrameSeekTask::SetMediaDecoderReaderWrapperCallback()
 {
   AssertOwnerThread();
 
   // Register dummy callbcak for audio decoding since we don't need to handle
   // the decoded audio samples.
-  mAudioCallbackID =
-    mReader->SetAudioCallback(this, &NextFrameSeekTask::OnAudioDecoded,
-                                    &NextFrameSeekTask::OnAudioNotDecoded);
-
-  mVideoCallbackID =
-    mReader->SetVideoCallback(this, &NextFrameSeekTask::OnVideoDecoded,
-                                    &NextFrameSeekTask::OnVideoNotDecoded);
+  mAudioCallback = mReader->AudioCallback().Connect(
+    OwnerThread(), [this] (AudioCallbackData aData) {
+    if (aData.is<MediaData*>()) {
+      OnAudioDecoded(aData.as<MediaData*>());
+    } else {
+      OnAudioNotDecoded(aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
 
-  RefPtr<NextFrameSeekTask> self = this;
-  mWaitAudioCallbackID =
-    mReader->SetWaitAudioCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->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.
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {
-        self->AssertOwnerThread();
-      });
+  mVideoCallback = mReader->VideoCallback().Connect(
+    OwnerThread(), [this] (VideoCallbackData aData) {
+    typedef Tuple<MediaData*, TimeStamp> Type;
+    if (aData.is<Type>()) {
+      OnVideoDecoded(Get<0>(aData.as<Type>()));
+    } else {
+      OnVideoNotDecoded(aData.as<MediaDecoderReader::NotDecodedReason>());
+    }
+  });
 
-  mWaitVideoCallbackID =
-    mReader->SetWaitVideoCallback(
-      [self] (MediaData::Type aType) -> void {
-        self->AssertOwnerThread();
-        self->EnsureVideoDecodeTaskQueued();
-      },
-      [self] (WaitForDataRejectValue aRejection) -> void {
-        self->AssertOwnerThread();
-      });
+  mAudioWaitCallback = mReader->AudioWaitCallback().Connect(
+    OwnerThread(), [this] (WaitCallbackData aData) {
+    // 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.
+  });
 
-  DECODER_LOG("NextFrameSeekTask set audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
-  DECODER_LOG("NextFrameSeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
-  DECODER_LOG("NextFrameSeekTask set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-  DECODER_LOG("NextFrameSeekTask set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
+  mVideoWaitCallback = mReader->VideoWaitCallback().Connect(
+    OwnerThread(), [this] (WaitCallbackData aData) {
+    if (aData.is<MediaData::Type>()) {
+      EnsureVideoDecodeTaskQueued();
+    }
+  });
 }
 
 void
 NextFrameSeekTask::CancelMediaDecoderReaderWrapperCallback()
 {
   AssertOwnerThread();
-
-  DECODER_LOG("NextFrameSeekTask cancel audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
-  mReader->CancelAudioCallback(mAudioCallbackID);
-
-  DECODER_LOG("NextFrameSeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
-  mReader->CancelVideoCallback(mVideoCallbackID);
-
-  DECODER_LOG("NextFrameSeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
-  mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
-
-  DECODER_LOG("NextFrameSeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-  mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
+  mAudioCallback.Disconnect();
+  mVideoCallback.Disconnect();
+  mAudioWaitCallback.Disconnect();
+  mVideoWaitCallback.Disconnect();
 }
 
 void
 NextFrameSeekTask::UpdateSeekTargetTime()
 {
   AssertOwnerThread();
 
   RefPtr<MediaData> data = mVideoQueue.PeekFront();
--- a/dom/media/NextFrameSeekTask.h
+++ b/dom/media/NextFrameSeekTask.h
@@ -86,21 +86,18 @@ private:
   /*
    * Internal state.
    */
   const int64_t mCurrentTimeBeforeSeek;
   const bool mHasAudio;
   const bool mHasVideo;
   media::TimeUnit mDuration;
 
-  /*
-   * Track the current seek promise made by the reader.
-   */
-  CallbackID mAudioCallbackID;
-  CallbackID mVideoCallbackID;
-  CallbackID mWaitAudioCallbackID;
-  CallbackID mWaitVideoCallbackID;
+  MediaEventListener mAudioCallback;
+  MediaEventListener mVideoCallback;
+  MediaEventListener mAudioWaitCallback;
+  MediaEventListener mVideoWaitCallback;
 };
 
 } // namespace media
 } // namespace mozilla
 
 #endif /* NEXTFRAME_SEEK_TASK_H */