Bug 1339748: P2. Let the MediaDataDecoder tells the format it wants. r?cpearce draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 17 Feb 2017 22:19:55 +0100
changeset 486566 23a85bd06bcbf3bb9c036d16996ad1f8cef0e809
parent 486552 129561d536911969ad0ecb0f5b8030bc1024f8f9
child 546283 e8b72bbb2bc30ddafbd568097da5acf72b489215
push id46023
push userbmo:jyavenard@mozilla.com
push dateSat, 18 Feb 2017 17:46:41 +0000
reviewerscpearce
bugs1339748
milestone54.0a1
Bug 1339748: P2. Let the MediaDataDecoder tells the format it wants. r?cpearce MozReview-Commit-ID: 22GEUnwEacd
dom/media/ipc/PVideoDecoder.ipdl
dom/media/ipc/RemoteVideoDecoder.cpp
dom/media/ipc/RemoteVideoDecoder.h
dom/media/ipc/VideoDecoderChild.cpp
dom/media/ipc/VideoDecoderChild.h
dom/media/ipc/VideoDecoderParent.cpp
dom/media/platforms/PlatformDecoderModule.h
dom/media/platforms/agnostic/AgnosticDecoderModule.h
dom/media/platforms/agnostic/BlankDecoderModule.cpp
dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
dom/media/platforms/agnostic/eme/EMEDecoderModule.h
dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
dom/media/platforms/android/AndroidDecoderModule.cpp
dom/media/platforms/android/AndroidDecoderModule.h
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/apple/AppleDecoderModule.cpp
dom/media/platforms/apple/AppleDecoderModule.h
dom/media/platforms/apple/AppleVTDecoder.h
dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
dom/media/platforms/omx/OmxDataDecoder.h
dom/media/platforms/omx/OmxDecoderModule.cpp
dom/media/platforms/omx/OmxDecoderModule.h
dom/media/platforms/wmf/WMFDecoderModule.cpp
dom/media/platforms/wmf/WMFDecoderModule.h
dom/media/platforms/wmf/WMFMediaDataDecoder.h
dom/media/platforms/wmf/WMFVideoMFTManager.h
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp
dom/media/platforms/wrappers/MediaDataDecoderProxy.h
--- a/dom/media/ipc/PVideoDecoder.ipdl
+++ b/dom/media/ipc/PVideoDecoder.ipdl
@@ -53,18 +53,17 @@ parent:
   async Drain();
   async Shutdown();
 
   async SetSeekThreshold(int64_t time);
 
   async __delete__();
 
 child:
-
-  async InitComplete(bool hardware, nsCString hardwareReason);
+  async InitComplete(bool hardware, nsCString hardwareReason, uint32_t conversion);
   async InitFailed(nsresult reason);
 
   async FlushComplete();
 
   // Each output includes a SurfaceDescriptorGPUVideo that represents the decoded
   // frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
   // must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
   // on the manager protocol.
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -104,16 +104,22 @@ RemoteVideoDecoder::SetSeekThreshold(con
   RefPtr<RemoteVideoDecoder> self = this;
   media::TimeUnit time = aTime;
   VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([=]() {
     MOZ_ASSERT(self->mActor);
     self->mActor->SetSeekThreshold(time);
   }), NS_DISPATCH_NORMAL);
 }
 
+MediaDataDecoder::ConversionRequired
+RemoteVideoDecoder::NeedsConversion() const
+{
+  return mActor->NeedsConversion();
+}
+
 nsresult
 RemoteDecoderModule::Startup()
 {
   if (!VideoDecoderManagerChild::GetManagerThread()) {
     return NS_ERROR_FAILURE;
   }
   return mWrapped->Startup();
 }
@@ -127,22 +133,16 @@ RemoteDecoderModule::SupportsMimeType(co
 
 bool
 RemoteDecoderModule::Supports(const TrackInfo& aTrackInfo,
                               DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return mWrapped->Supports(aTrackInfo, aDiagnostics);
 }
 
-PlatformDecoderModule::ConversionRequired
-RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  return mWrapped->DecoderNeedsConversion(aConfig);
-}
-
 already_AddRefed<MediaDataDecoder>
 RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
   if (!MediaPrefs::PDMUseGPUDecoder() ||
       !aParams.mKnowsCompositor ||
       aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) {
     return mWrapped->CreateVideoDecoder(aParams);
   }
--- a/dom/media/ipc/RemoteVideoDecoder.h
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -31,16 +31,17 @@ public:
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
   void SetSeekThreshold(const media::TimeUnit& aTime) override;
 
   const char* GetDescriptionName() const override { return "RemoteVideoDecoder"; }
+  ConversionRequired NeedsConversion() const override;
 
 private:
   RemoteVideoDecoder();
   ~RemoteVideoDecoder();
 
   // Only ever written to from the reader task queue (during the constructor and
   // destructor when we can guarantee no other threads are accessing it). Only
   // read from the manager thread.
@@ -60,19 +61,16 @@ public:
 
   nsresult Startup() override;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
   bool Supports(const TrackInfo& aTrackInfo,
                 DecoderDoctorDiagnostics* aDiagnostics) const override;
 
-  ConversionRequired DecoderNeedsConversion(
-    const TrackInfo& aConfig) const override;
-
   already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
     const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
     const CreateDecoderParams& aParams) override
   {
     return nullptr;
   }
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -19,16 +19,17 @@ using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
 VideoDecoderChild::VideoDecoderChild()
   : mThread(VideoDecoderManagerChild::GetManagerThread())
   , mCanSend(false)
   , mInitialized(false)
   , mIsHardwareAccelerated(false)
+  , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
   , mNeedNewDecoder(false)
 {
 }
 
 VideoDecoderChild::~VideoDecoderChild()
 {
   AssertOnManagerThread();
   mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
@@ -83,23 +84,25 @@ VideoDecoderChild::RecvError(const nsres
   mDecodePromise.RejectIfExists(aError, __func__);
   mDrainPromise.RejectIfExists(aError, __func__);
   mFlushPromise.RejectIfExists(aError, __func__);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VideoDecoderChild::RecvInitComplete(const bool& aHardware,
-                                    const nsCString& aHardwareReason)
+                                    const nsCString& aHardwareReason,
+                                    const uint32_t& aConversion)
 {
   AssertOnManagerThread();
   mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
   mInitialized = true;
   mIsHardwareAccelerated = aHardware;
   mHardwareAcceleratedReason = aHardwareReason;
+  mConversion = static_cast<MediaDataDecoder::ConversionRequired>(aConversion);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VideoDecoderChild::RecvInitFailed(const nsresult& aReason)
 {
   AssertOnManagerThread();
   mInitPromise.RejectIfExists(aReason, __func__);
@@ -289,18 +292,24 @@ void
 VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime)
 {
   AssertOnManagerThread();
   if (mCanSend) {
     SendSetSeekThreshold(aTime.ToMicroseconds());
   }
 }
 
+MediaDataDecoder::ConversionRequired
+VideoDecoderChild::NeedsConversion() const
+{
+  return mConversion;
+}
+
 void
-VideoDecoderChild::AssertOnManagerThread()
+VideoDecoderChild::AssertOnManagerThread() const
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
 
 VideoDecoderManagerChild*
 VideoDecoderChild::GetManager()
 {
   if (!mCanSend) {
--- a/dom/media/ipc/VideoDecoderChild.h
+++ b/dom/media/ipc/VideoDecoderChild.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* 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 include_dom_ipc_VideoDecoderChild_h
 #define include_dom_ipc_VideoDecoderChild_h
 
 #include "PlatformDecoderModule.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/dom/PVideoDecoderChild.h"
 
 namespace mozilla {
 namespace dom {
 
 class RemoteVideoDecoder;
 class RemoteDecoderModule;
 class VideoDecoderManagerChild;
@@ -23,57 +24,62 @@ public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderChild)
 
   // PVideoDecoderChild
   mozilla::ipc::IPCResult RecvOutput(const VideoDataIPDL& aData) override;
   mozilla::ipc::IPCResult RecvInputExhausted() override;
   mozilla::ipc::IPCResult RecvDrainComplete() override;
   mozilla::ipc::IPCResult RecvError(const nsresult& aError) override;
-  mozilla::ipc::IPCResult RecvInitComplete(const bool& aHardware, const nsCString& aHardwareReason) override;
+  mozilla::ipc::IPCResult RecvInitComplete(const bool& aHardware,
+                                           const nsCString& aHardwareReason,
+                                           const uint32_t& aConversion) override;
   mozilla::ipc::IPCResult RecvInitFailed(const nsresult& aReason) override;
   mozilla::ipc::IPCResult RecvFlushComplete() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   RefPtr<MediaDataDecoder::InitPromise> Init();
   RefPtr<MediaDataDecoder::DecodePromise> Decode(MediaRawData* aSample);
   RefPtr<MediaDataDecoder::DecodePromise> Drain();
   RefPtr<MediaDataDecoder::FlushPromise> Flush();
   void Shutdown();
   bool IsHardwareAccelerated(nsACString& aFailureReason) const;
   void SetSeekThreshold(const media::TimeUnit& aTime);
+  MediaDataDecoder::ConversionRequired NeedsConversion() const;
 
   MOZ_IS_CLASS_INIT
   bool InitIPDL(const VideoInfo& aVideoInfo,
                 const layers::TextureFactoryIdentifier& aIdentifier);
   void DestroyIPDL();
 
   // Called from IPDL when our actor has been destroyed
   void IPDLActorDestroyed();
 
   VideoDecoderManagerChild* GetManager();
 
 private:
   ~VideoDecoderChild();
 
-  void AssertOnManagerThread();
+  void AssertOnManagerThread() const;
 
   RefPtr<VideoDecoderChild> mIPDLSelfRef;
   RefPtr<nsIThread> mThread;
 
   MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDrainPromise;
   MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
 
   nsCString mHardwareAcceleratedReason;
   bool mCanSend;
   bool mInitialized;
-  bool mIsHardwareAccelerated;
+  Atomic<bool> mIsHardwareAccelerated;
+  Atomic<MediaDataDecoder::ConversionRequired> mConversion;
+
   // Set to true if the actor got destroyed and we haven't yet notified the
   // caller.
   bool mNeedNewDecoder;
   MediaDataDecoder::DecodedData mDecodedData;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderParent.cpp
+++ b/dom/media/ipc/VideoDecoderParent.cpp
@@ -104,17 +104,20 @@ VideoDecoderParent::RecvInit()
   MOZ_ASSERT(OnManagerThread());
   RefPtr<VideoDecoderParent> self = this;
   mDecoder->Init()->Then(mManagerTaskQueue, __func__,
     [self] (TrackInfo::TrackType aTrack) {
       if (self->mDecoder) {
         nsCString hardwareReason;
         bool hardwareAccelerated =
           self->mDecoder->IsHardwareAccelerated(hardwareReason);
-        Unused << self->SendInitComplete(hardwareAccelerated, hardwareReason);
+        uint32_t conversion =
+          static_cast<uint32_t>(self->mDecoder->NeedsConversion());
+        Unused << self->SendInitComplete(
+          hardwareAccelerated, hardwareReason, conversion);
       }
     },
     [self] (MediaResult aReason) {
       if (!self->mDestroyed) {
         Unused << self->SendInitFailed(aReason);
       }
     });
   return IPC_OK();
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -147,29 +147,16 @@ public:
   virtual bool Supports(const TrackInfo& aTrackInfo,
                         DecoderDoctorDiagnostics* aDiagnostics) const
   {
     // By default, fall back to SupportsMimeType with just the MIME string.
     // (So PDMs do not need to override this method -- yet.)
     return SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics);
   }
 
-  enum class ConversionRequired : uint8_t
-  {
-    kNeedNone,
-    kNeedAVCC,
-    kNeedAnnexB,
-  };
-
-  // Indicates that the decoder requires a specific format.
-  // The PlatformDecoderModule will convert the demuxed data accordingly before
-  // feeding it to MediaDataDecoder::Input.
-  virtual ConversionRequired DecoderNeedsConversion(
-    const TrackInfo& aConfig) const = 0;
-
 protected:
   PlatformDecoderModule() { }
   virtual ~PlatformDecoderModule() { }
 
   friend class H264Converter;
   friend class PDMFactory;
   friend class dom::RemoteDecoderModule;
   friend class EMEDecoderModule;
@@ -311,13 +298,27 @@ public:
   // Care should be taken as ConfigurationChanged is called on the reader's
   // taskqueue.
   virtual void ConfigurationChanged(const TrackInfo& aConfig)
   {
     MOZ_ASSERT(SupportDecoderRecycling(),
                "Can only work with a decoder supporting recycling.");
   }
 
+  enum class ConversionRequired
+  {
+    kNeedNone = 0,
+    kNeedAVCC = 1,
+    kNeedAnnexB = 2,
+  };
+
+  // Indicates that the decoder requires a specific format.
+  // The demuxed data will be converted accordingly before feeding it to
+  // Decode().
+  virtual ConversionRequired NeedsConversion() const
+  {
+    return ConversionRequired::kNeedNone;
+  }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.h
+++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.h
@@ -8,22 +8,16 @@ namespace mozilla {
 class AgnosticDecoderModule : public PlatformDecoderModule
 {
 public:
   AgnosticDecoderModule() = default;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override
-  {
-    return ConversionRequired::kNeedNone;
-  }
-
 protected:
   virtual ~AgnosticDecoderModule() = default;
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -27,24 +27,24 @@ namespace mozilla {
 // MediaData objects.
 template<class BlankMediaDataCreator>
 class BlankMediaDataDecoder : public MediaDataDecoder
 {
 public:
   BlankMediaDataDecoder(BlankMediaDataCreator* aCreator,
                         const CreateDecoderParams& aParams)
     : mCreator(aCreator)
+    , mIsH264(MP4Decoder::IsH264(aParams.mConfig.mMimeType))
     , mMaxRefFrames(
-        aParams.mConfig.GetType() == TrackInfo::kVideoTrack
-        && MP4Decoder::IsH264(aParams.mConfig.mMimeType)
-           ? mp4_demuxer::AnnexB::HasSPS(aParams.VideoConfig().mExtraData)
-             ? mp4_demuxer::H264::ComputeMaxRefFrames(
-                 aParams.VideoConfig().mExtraData)
-             : 16
-           : 0)
+        mIsH264
+        ? mp4_demuxer::AnnexB::HasSPS(aParams.VideoConfig().mExtraData)
+          ? mp4_demuxer::H264::ComputeMaxRefFrames(
+              aParams.VideoConfig().mExtraData)
+          : 16
+        : 0)
     , mType(aParams.mConfig.GetType())
   {
   }
 
   RefPtr<InitPromise> Init() override
   {
     return InitPromise::CreateAndResolve(mType, __func__);
   }
@@ -90,18 +90,26 @@ public:
     return FlushPromise::CreateAndResolve(true, __func__);
   }
 
   const char* GetDescriptionName() const override
   {
     return "blank media data decoder";
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    return mIsH264
+           ? ConversionRequired::kNeedAVCC
+           : ConversionRequired::kNeedNone;
+  }
+
 private:
   nsAutoPtr<BlankMediaDataCreator> mCreator;
+  const bool mIsH264;
   const uint32_t mMaxRefFrames;
   ReorderQueue mReorderQueue;
   TrackInfo::TrackType mType;
 };
 
 class BlankVideoDataCreator
 {
 public:
@@ -255,27 +263,16 @@ public:
     return decoder.forget();
   }
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override
   {
     return true;
   }
-
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override
-  {
-    if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
-      return ConversionRequired::kNeedAVCC;
-    } else {
-      return ConversionRequired::kNeedNone;
-    }
-  }
-
 };
 
 already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule()
 {
   RefPtr<PlatformDecoderModule> pdm = new BlankDecoderModule();
   return pdm.forget();
 }
 
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -184,16 +184,21 @@ public:
     return decoder->Shutdown();
   }
 
   const char* GetDescriptionName() const override
   {
     return mDecoder->GetDescriptionName();
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    return mDecoder->NeedsConversion();
+  }
+
 private:
   RefPtr<MediaDataDecoder> mDecoder;
   RefPtr<TaskQueue> mTaskQueue;
   RefPtr<CDMProxy> mProxy;
   nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder>
     mDecrypts;
   RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
   MozPromiseRequestHolder<SamplesWaitingForKey::WaitForKeyPromise> mKeyRequest;
@@ -365,26 +370,16 @@ EMEDecoderModule::CreateAudioDecoder(con
   }
 
   RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(
     decoder, mProxy, AbstractThread::GetCurrent()->AsTaskQueue(),
     aParams.mType, aParams.mOnWaitingForKeyEvent));
   return emeDecoder.forget();
 }
 
-PlatformDecoderModule::ConversionRequired
-EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
-    return ConversionRequired::kNeedAVCC;
-  } else {
-    return ConversionRequired::kNeedNone;
-  }
-}
-
 bool
 EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                    DecoderDoctorDiagnostics* aDiagnostics) const
 {
   Maybe<nsCString> gmp;
   gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
   return GMPDecoderModule::SupportsMimeType(aMimeType, gmp);
 }
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
@@ -26,19 +26,16 @@ protected:
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override;
-
   bool
   SupportsMimeType(const nsACString &aMimeType,
                    DecoderDoctorDiagnostics *aDiagnostics) const override;
 
 private:
   virtual ~EMEDecoderModule();
   RefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
@@ -65,28 +65,16 @@ GMPDecoderModule::CreateVideoDecoder(con
 }
 
 already_AddRefed<MediaDataDecoder>
 GMPDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
 {
   return nullptr;
 }
 
-PlatformDecoderModule::ConversionRequired
-GMPDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  // GMPVideoCodecType::kGMPVideoCodecH264 specifies that encoded frames must be
-  // in AVCC format.
-  if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
-    return ConversionRequired::kNeedAVCC;
-  } else {
-    return ConversionRequired::kNeedNone;
-  }
-}
-
 /* static */
 bool
 GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                    const Maybe<nsCString>& aGMP)
 {
   if (aGMP.isNothing()) {
     return false;
   }
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
@@ -33,19 +33,16 @@ public:
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override;
-
   bool
   SupportsMimeType(const nsACString& aMimeType,
                    DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   static bool SupportsMimeType(const nsACString& aMimeType,
                                const Maybe<nsCString>& aGMP);
 
 private:
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -37,16 +37,20 @@ public:
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
   const char* GetDescriptionName() const override
   {
     return "GMP video decoder";
   }
+  ConversionRequired NeedsConversion() const override
+  {
+    return ConversionRequired::kNeedAVCC;
+  }
 
   // GMPVideoDecoderCallbackProxy
   // All those methods are called on the GMP thread.
   void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
   void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override;
   void ReceivedDecodedFrame(const uint64_t aPictureId) override;
   void InputDataExhausted() override;
   void DrainComplete() override;
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -205,18 +205,9 @@ AndroidDecoderModule::CreateAudioDecoder
   if (mProxy) {
     drmStubId = mProxy->GetMediaDrmStubId();
   }
   RefPtr<MediaDataDecoder> decoder =
    RemoteDataDecoder::CreateAudioDecoder(aParams, drmStubId, mProxy);
   return decoder.forget();
 }
 
-PlatformDecoderModule::ConversionRequired
-AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  if (aConfig.IsVideo()) {
-    return ConversionRequired::kNeedAnnexB;
-  }
-  return ConversionRequired::kNeedNone;
-}
-
 } // mozilla
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -19,19 +19,16 @@ public:
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
   AndroidDecoderModule(CDMProxy* aProxy = nullptr);
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override;
-
 private:
   virtual ~AndroidDecoderModule() { }
   RefPtr<MediaDrmCDMProxy> mProxy;
 };
 
 extern LazyLogModule sAndroidDecoderModuleLog;
 
 } // namespace mozilla
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -332,16 +332,21 @@ public:
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                                           __func__);
     }
 
     return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    return ConversionRequired::kNeedAnnexB;
+  }
+
 private:
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
     CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
 
     void HandleInputExhausted() override
     {
--- a/dom/media/platforms/apple/AppleDecoderModule.cpp
+++ b/dom/media/platforms/apple/AppleDecoderModule.cpp
@@ -18,23 +18,19 @@
 namespace mozilla {
 
 bool AppleDecoderModule::sInitialized = false;
 bool AppleDecoderModule::sIsCoreMediaAvailable = false;
 bool AppleDecoderModule::sIsVTAvailable = false;
 bool AppleDecoderModule::sIsVTHWAvailable = false;
 bool AppleDecoderModule::sCanUseHardwareVideoDecoder = true;
 
-AppleDecoderModule::AppleDecoderModule()
-{
-}
+AppleDecoderModule::AppleDecoderModule() { }
 
-AppleDecoderModule::~AppleDecoderModule()
-{
-}
+AppleDecoderModule::~AppleDecoderModule() { }
 
 /* static */
 void
 AppleDecoderModule::Init()
 {
   if (sInitialized) {
     return;
   }
@@ -92,19 +88,9 @@ AppleDecoderModule::SupportsMimeType(con
 {
   return (sIsCoreMediaAvailable &&
           (aMimeType.EqualsLiteral("audio/mpeg") ||
            aMimeType.EqualsLiteral("audio/mp4a-latm"))) ||
     (sIsVTAvailable && (aMimeType.EqualsLiteral("video/mp4") ||
                         aMimeType.EqualsLiteral("video/avc")));
 }
 
-PlatformDecoderModule::ConversionRequired
-AppleDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  if (aConfig.IsVideo()) {
-    return ConversionRequired::kNeedAVCC;
-  } else {
-    return ConversionRequired::kNeedNone;
-  }
-}
-
 } // namespace mozilla
--- a/dom/media/platforms/apple/AppleDecoderModule.h
+++ b/dom/media/platforms/apple/AppleDecoderModule.h
@@ -24,19 +24,16 @@ public:
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override;
-
   static void Init();
 
   static bool sCanUseHardwareVideoDecoder;
 
 private:
   static bool sInitialized;
   static bool sIsCoreMediaAvailable;
   static bool sIsVTAvailable;
--- a/dom/media/platforms/apple/AppleVTDecoder.h
+++ b/dom/media/platforms/apple/AppleVTDecoder.h
@@ -55,16 +55,21 @@ public:
 
   const char* GetDescriptionName() const override
   {
     return mIsHardwareAccelerated
            ? "apple hardware VT decoder"
            : "apple software VT decoder";
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    return ConversionRequired::kNeedAVCC;
+  }
+
   // Access from the taskqueue and the decoder's thread.
   // OutputFrame is thread-safe.
   void OutputFrame(CVPixelBufferRef aImage, AppleFrameRef aFrameRef);
 
 private:
   virtual ~AppleVTDecoder();
   RefPtr<FlushPromise> ProcessFlush();
   RefPtr<DecodePromise> ProcessDrain();
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -64,27 +64,15 @@ public:
     AVCodecID audioCodec = FFmpegAudioDecoder<V>::GetCodecId(aMimeType);
     if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) {
       return false;
     }
     AVCodecID codec = audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec;
     return !!FFmpegDataDecoder<V>::FindAVCodec(mLib, codec);
   }
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override
-  {
-    if (aConfig.IsVideo()
-        && (aConfig.mMimeType.EqualsLiteral("video/avc")
-            || aConfig.mMimeType.EqualsLiteral("video/mp4"))) {
-      return ConversionRequired::kNeedAVCC;
-    } else {
-      return ConversionRequired::kNeedNone;
-    }
-  }
-
 private:
   FFmpegLibWrapper* mLib;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDecoderModule_h__
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -36,16 +36,21 @@ public:
   const char* GetDescriptionName() const override
   {
 #ifdef USING_MOZFFVPX
     return "ffvpx video decoder";
 #else
     return "ffmpeg video decoder";
 #endif
   }
+  ConversionRequired NeedsConversion() const override
+  {
+    return ConversionRequired::kNeedAVCC;
+  }
+
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> ProcessDrain() override;
   RefPtr<FlushPromise> ProcessFlush() override;
   MediaResult DoDecode(MediaRawData* aSample, bool* aGotFrame,
                        DecodedData& aResults);
--- a/dom/media/platforms/omx/OmxDataDecoder.h
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -69,16 +69,21 @@ public:
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
 
   const char* GetDescriptionName() const override
   {
     return "omx decoder";
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    return ConversionRequired::kNeedAnnexB;
+  }
+
   // Return true if event is handled.
   bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
 
 protected:
   void InitializationTask();
 
   void ResolveInitPromise(const char* aMethodName);
 
--- a/dom/media/platforms/omx/OmxDecoderModule.cpp
+++ b/dom/media/platforms/omx/OmxDecoderModule.cpp
@@ -22,22 +22,16 @@ OmxDecoderModule::CreateVideoDecoder(con
 already_AddRefed<MediaDataDecoder>
 OmxDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
 {
   RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aParams.mConfig,
                                                       nullptr);
   return decoder.forget();
 }
 
-PlatformDecoderModule::ConversionRequired
-OmxDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  return ConversionRequired::kNeedNone;
-}
-
 bool
 OmxDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                    DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return OmxPlatformLayer::SupportsMimeType(aMimeType);
 }
 
 }
--- a/dom/media/platforms/omx/OmxDecoderModule.h
+++ b/dom/media/platforms/omx/OmxDecoderModule.h
@@ -16,15 +16,13 @@ public:
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
-
-  ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif // OmxDecoderModule_h_
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -241,18 +241,9 @@ WMFDecoderModule::Supports(const TrackIn
       return true;
     }
   }
 
   // Some unsupported codec.
   return false;
 }
 
-PlatformDecoderModule::ConversionRequired
-WMFDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
-{
-  if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
-    return ConversionRequired::kNeedAnnexB;
-  }
-  return ConversionRequired::kNeedNone;
-}
-
 } // namespace mozilla
--- a/dom/media/platforms/wmf/WMFDecoderModule.h
+++ b/dom/media/platforms/wmf/WMFDecoderModule.h
@@ -23,19 +23,16 @@ public:
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
   bool Supports(const TrackInfo& aTrackInfo,
                 DecoderDoctorDiagnostics* aDiagnostics) const override;
 
-  ConversionRequired
-  DecoderNeedsConversion(const TrackInfo& aConfig) const override;
-
   // Called on main thread.
   static void Init();
 
   // Called from any thread, must call init first
   static int GetNumDecoderThreads();
 
   // Accessors that report whether we have the required MFTs available
   // on the system to play various codecs. Windows Vista doesn't have the
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h
@@ -62,16 +62,21 @@ public:
 
   virtual const char* GetDescriptionName() const = 0;
 
   virtual void SetSeekThreshold(const media::TimeUnit& aTime)
   {
     mSeekTargetThreshold = Some(aTime);
   }
 
+  virtual MediaDataDecoder::ConversionRequired NeedsConversion() const
+  {
+    return MediaDataDecoder::ConversionRequired::kNeedNone;
+  }
+
 protected:
   // IMFTransform wrapper that performs the decoding.
   RefPtr<MFTDecoder> mDecoder;
 
   Maybe<media::TimeUnit> mSeekTargetThreshold;
 };
 
 // Decodes audio and video using Windows Media Foundation. Samples are decoded
@@ -97,16 +102,22 @@ public:
 
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
   const char* GetDescriptionName() const override
   {
     return mMFTManager ? mMFTManager->GetDescriptionName() : "";
   }
 
+  ConversionRequired NeedsConversion() const override
+  {
+    MOZ_ASSERT(mMFTManager);
+    return mMFTManager->NeedsConversion();
+  }
+
   virtual void SetSeekThreshold(const media::TimeUnit& aTime) override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
   RefPtr<DecodePromise> ProcessDecode(MediaRawData* aSample);
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -54,16 +54,23 @@ public:
   }
 
   void Drain() override
   {
     MFTManager::Drain();
     mDraining = true;
   }
 
+  MediaDataDecoder::ConversionRequired NeedsConversion() const override
+  {
+    return mStreamType == H264
+           ? MediaDataDecoder::ConversionRequired::kNeedAnnexB
+           : MediaDataDecoder::ConversionRequired::kNeedNone;
+  }
+
 private:
   bool ValidateVideoInfo();
 
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
   HRESULT ConfigureVideoFrameGeometry();
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -21,18 +21,16 @@ H264Converter::H264Converter(PlatformDec
   : mPDM(aPDM)
   , mOriginalConfig(aParams.VideoConfig())
   , mCurrentConfig(aParams.VideoConfig())
   , mKnowsCompositor(aParams.mKnowsCompositor)
   , mImageContainer(aParams.mImageContainer)
   , mTaskQueue(aParams.mTaskQueue)
   , mDecoder(nullptr)
   , mGMPCrashHelper(aParams.mCrashHelper)
-  , mNeedAVCC(aPDM->DecoderNeedsConversion(aParams.mConfig)
-      == PlatformDecoderModule::ConversionRequired::kNeedAVCC)
   , mLastError(NS_OK)
   , mType(aParams.mType)
   , mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent)
 {
   CreateDecoder(aParams.mDiagnostics);
 }
 
 H264Converter::~H264Converter()
@@ -93,17 +91,22 @@ H264Converter::Decode(MediaRawData* aSam
                   RESULT_DETAIL("Unable to create H264 decoder")),
       __func__);
   }
 
   if (mNeedKeyframe && !aSample->mKeyframe) {
     return DecodePromise::CreateAndResolve(DecodedData(), __func__);
   }
 
-  if (!mNeedAVCC
+  if (!mNeedAVCC) {
+    mNeedAVCC =
+      Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
+  }
+
+  if (!*mNeedAVCC
       && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) {
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("ConvertSampleToAnnexB")),
       __func__);
   }
 
   mNeedKeyframe = false;
@@ -268,17 +271,21 @@ H264Converter::OnDecoderInitFailed(const
 void
 H264Converter::DecodeFirstSample(MediaRawData* aSample)
 {
   if (mNeedKeyframe && !aSample->mKeyframe) {
     mDecodePromise.Resolve(DecodedData(), __func__);
     return;
   }
   mNeedKeyframe = false;
-  if (!mNeedAVCC
+
+  mNeedAVCC =
+    Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
+
+  if (!*mNeedAVCC
       && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) {
     mDecodePromise.Reject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("ConvertSampleToAnnexB")),
       __func__);
     return;
   }
   if (CanRecycleDecoder()) {
@@ -350,16 +357,17 @@ H264Converter::CheckForSPSChange(MediaRa
              mFlushRequest.Complete();
              mShutdownPromise = Shutdown();
              mShutdownPromise
                ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
                       __func__,
                       [self, this]() {
                         mShutdownRequest.Complete();
                         mShutdownPromise = nullptr;
+                        mNeedAVCC.reset();
                         RefPtr<MediaRawData> sample = mPendingSample.forget();
                         nsresult rv = CreateDecoderAndInit(sample);
                         if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
                           // All good so far, will continue later.
                           return;
                         }
                         MOZ_ASSERT(NS_FAILED(rv));
                         mDecodePromise.Reject(rv, __func__);
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_H264Converter_h
 #define mozilla_H264Converter_h
 
 #include "PlatformDecoderModule.h"
+#include "mozilla/Maybe.h"
 
 namespace mozilla {
 
 // H264Converter is a MediaDataDecoder wrapper used to ensure that
 // only AVCC or AnnexB is fed to the underlying MediaDataDecoder.
 // The H264Converter allows playback of content where the SPS NAL may not be
 // provided in the init segment (e.g. AVC3 or Annex B)
 // H264Converter will monitor the input data, and will delay creation of the
@@ -48,16 +49,24 @@ public:
     return false;
   }
   void ConfigurationChanged(const TrackInfo& aConfig) override
   {
     if (mDecoder && mDecoder->SupportDecoderRecycling()) {
       mDecoder->ConfigurationChanged(aConfig);
     }
   }
+  ConversionRequired NeedsConversion() const override
+  {
+    if (mDecoder) {
+      return mDecoder->NeedsConversion();
+    }
+    // Default so no conversion is performed.
+    return ConversionRequired::kNeedAVCC;
+  }
   nsresult GetLastError() const { return mLastError; }
 
 private:
   // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
   // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
   // will set mError accordingly.
   nsresult CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics);
   nsresult CreateDecoderAndInit(MediaRawData* aSample);
@@ -87,17 +96,17 @@ private:
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
   MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
   MozPromiseHolder<DecodePromise> mDecodePromise;
   MozPromiseRequestHolder<FlushPromise> mFlushRequest;
   MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
   RefPtr<ShutdownPromise> mShutdownPromise;
 
   RefPtr<GMPCrashHelper> mGMPCrashHelper;
-  bool mNeedAVCC;
+  Maybe<bool> mNeedAVCC;
   nsresult mLastError;
   bool mNeedKeyframe = true;
   // Set to true once a decoder has been created.
   bool mUseOriginalConfig = true;
   const TrackInfo::TrackType mType;
   MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
 };
 
--- a/dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp
+++ b/dom/media/platforms/wrappers/MediaDataDecoderProxy.cpp
@@ -129,9 +129,17 @@ MediaDataDecoderProxy::ConfigurationChan
   RefPtr<MediaDataDecoderProxy> self = this;
   RefPtr<TrackInfoSharedPtr> config = new TrackInfoSharedPtr(aConfig, 0);
   mProxyThread->Dispatch(NS_NewRunnableFunction([self, config] {
     const TrackInfo* trackInfo = *config;
     self->mProxyDecoder->ConfigurationChanged(*trackInfo);
   }));
 }
 
+MediaDataDecoder::ConversionRequired
+MediaDataDecoderProxy::NeedsConversion() const
+{
+  MOZ_ASSERT(!mIsShutdown);
+
+  return mProxyDecoder->NeedsConversion();
+}
+
 } // namespace mozilla
--- a/dom/media/platforms/wrappers/MediaDataDecoderProxy.h
+++ b/dom/media/platforms/wrappers/MediaDataDecoderProxy.h
@@ -46,16 +46,17 @@ public:
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
   const char* GetDescriptionName() const override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
   void SetSeekThreshold(const media::TimeUnit& aTime) override;
   bool SupportDecoderRecycling() const override;
   void ConfigurationChanged(const TrackInfo& aConfig) override;
+  ConversionRequired NeedsConversion() const override;
 
 private:
   RefPtr<MediaDataDecoder> mProxyDecoder;
   RefPtr<AbstractThread> mProxyThread;
 
 #if defined(DEBUG)
   Atomic<bool> mIsShutdown;
 #endif