Bug 1248507 - p1. Pass DecoderDoctorDiagnostics to PDMs&more - r=jya
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 19 Apr 2016 17:36:19 +1000
changeset 331693 1edb74f649f12d8083084ed534a637448554061c
parent 331692 7bffe558f364bea04f7e3eea847cc05e12d3e0ab
child 331694 90171f1c8aace2146a2abb31b473af732324dae4
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1248507
milestone48.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 1248507 - p1. Pass DecoderDoctorDiagnostics to PDMs&more - r=jya Pass declared-but-yet-undefined DecoderDoctorDiagnostics pointer to various routines that contribute to deciding if a media format can be played, and those that create decoders. Points where a DecoderDoctorDiagnostics can be injected are currently marked with "/* DecoderDoctorDiagnostics* */ nullptr", and some will be used in following patches. MozReview-Commit-ID: 7u37bvY4CpW
dom/html/HTMLMediaElement.cpp
dom/media/ADTSDecoder.cpp
dom/media/Benchmark.cpp
dom/media/DecoderTraits.cpp
dom/media/DecoderTraits.h
dom/media/MP3Decoder.cpp
dom/media/MediaFormatReader.cpp
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/fmp4/MP4Decoder.cpp
dom/media/fmp4/MP4Decoder.h
dom/media/gtest/TestMediaDataDecoder.cpp
dom/media/mediasource/MediaSource.cpp
dom/media/platforms/PDMFactory.cpp
dom/media/platforms/PDMFactory.h
dom/media/platforms/PlatformDecoderModule.h
dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
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/android/AndroidDecoderModule.cpp
dom/media/platforms/android/AndroidDecoderModule.h
dom/media/platforms/apple/AppleDecoderModule.cpp
dom/media/platforms/apple/AppleDecoderModule.h
dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
dom/media/platforms/gonk/GonkDecoderModule.cpp
dom/media/platforms/gonk/GonkDecoderModule.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/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
dom/media/wave/WaveDecoder.cpp
layout/build/nsContentDLF.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2893,17 +2893,18 @@ HTMLMediaElement::GetCanPlay(const nsASt
     return CANPLAY_NO;
 
   nsAutoString codecs;
   rv = parser.GetParameter("codecs", codecs);
 
   NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
   return DecoderTraits::CanHandleMediaType(mimeTypeUTF8.get(),
                                            NS_SUCCEEDED(rv),
-                                           codecs);
+                                           codecs,
+           /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
 {
   switch (GetCanPlay(aType)) {
   case CANPLAY_NO:
     aResult.Truncate();
@@ -2959,17 +2960,19 @@ nsresult HTMLMediaElement::InitializeDec
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
 
   nsAutoCString mimeType;
 
   aChannel->GetContentType(mimeType);
   NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
 
-  RefPtr<MediaDecoder> decoder = DecoderTraits::CreateDecoder(mimeType, this);
+  RefPtr<MediaDecoder> decoder =
+    DecoderTraits::CreateDecoder(mimeType, this,
+                                 /* DecoderDoctorDiagnostics* */ nullptr);
   if (!decoder) {
     nsAutoString src;
     GetCurrentSrc(src);
     NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
     const char16_t* params[] = { mimeUTF16.get(), src.get() };
     ReportLoadError("MediaLoadUnsupportedMimeType", params, ArrayLength(params));
     return NS_ERROR_FAILURE;
   }
--- a/dom/media/ADTSDecoder.cpp
+++ b/dom/media/ADTSDecoder.cpp
@@ -29,17 +29,18 @@ ADTSDecoder::CreateStateMachine()
   return new MediaDecoderStateMachine(this, reader);
 }
 
 /* static */ bool
 ADTSDecoder::IsEnabled()
 {
   PDMFactory::Init();
   RefPtr<PDMFactory> platform = new PDMFactory();
-  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"));
+  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"),
+                                    /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 /* static */ bool
 ADTSDecoder::CanHandleMediaType(const nsACString& aType,
                                 const nsAString& aCodecs)
 {
   if (aType.EqualsASCII("audio/aac") || aType.EqualsASCII("audio/aacp")) {
     return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("aac"));
--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -19,16 +19,17 @@ namespace mozilla {
 // Update this version number to force re-running the benchmark. Such as when
 // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
 const uint32_t VP9Benchmark::sBenchmarkVersionID = 1;
 
 const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
 const char* VP9Benchmark::sBenchmarkFpsVersionCheck = "media.benchmark.vp9.versioncheck";
 bool VP9Benchmark::sHasRunTest = false;
 
+// static
 bool
 VP9Benchmark::IsVP9DecodeFast()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   bool hasPref = Preferences::HasUserValue(sBenchmarkFpsPref);
   uint32_t hadRecentUpdate = Preferences::GetUint(sBenchmarkFpsVersionCheck, 0U);
 
@@ -195,17 +196,18 @@ BenchmarkPlayback::DemuxNextSample()
 }
 
 void
 BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
 {
   MOZ_ASSERT(OnThread());
 
   RefPtr<PDMFactory> platform = new PDMFactory();
-  mDecoder = platform->CreateDecoder(aInfo, mDecoderTaskQueue, this);
+  mDecoder = platform->CreateDecoder(aInfo, mDecoderTaskQueue, this,
+     /* DecoderDoctorDiagnostics* */ nullptr);
   if (!mDecoder) {
     MainThreadShutdown();
     return;
   }
   RefPtr<Benchmark> ref(mMainThreadState);
   mDecoder->Init()->Then(
     Thread(), __func__,
     [this, ref](TrackInfo::TrackType aTrackType) {
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -299,27 +299,29 @@ IsDirectShowSupportedType(const nsACStri
 {
   return DirectShowDecoder::GetSupportedCodecs(aType, nullptr);
 }
 #endif
 
 #ifdef MOZ_FMP4
 static bool
 IsMP4SupportedType(const nsACString& aType,
+                   DecoderDoctorDiagnostics* aDiagnostics,
                    const nsAString& aCodecs = EmptyString())
 {
-  return MP4Decoder::CanHandleMediaType(aType, aCodecs);
+  return MP4Decoder::CanHandleMediaType(aType, aCodecs, aDiagnostics);
 }
 #endif
 
 /* static */ bool
-DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType)
+DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType,
+                                   DecoderDoctorDiagnostics* aDiagnostics)
 {
 #ifdef MOZ_FMP4
-  return IsMP4SupportedType(aType);
+  return IsMP4SupportedType(aType, aDiagnostics);
 #else
   return false;
 #endif
 }
 
 static bool
 IsMP3SupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
@@ -341,17 +343,18 @@ IsAACSupportedType(const nsACString& aTy
 static bool
 IsWAVSupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
 {
   return WaveDecoder::CanHandleMediaType(aType, aCodecs);
 }
 
 /* static */
-bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
+bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType,
+                                          DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     // We should not return true for Wave types, since there are some
     // Wave codecs actually in use in the wild that we don't support, and
     // we should allow those to be handled by plugins or helper apps.
     // Furthermore people can play Wave files on most platforms by other
     // means.
     return false;
@@ -363,23 +366,25 @@ bool DecoderTraits::ShouldHandleMediaTyp
   if (nsDependentCString(aMIMEType).EqualsASCII("video/quicktime")) {
     RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
     if (pluginHost &&
         pluginHost->HavePluginForType(nsDependentCString(aMIMEType))) {
       return false;
     }
   }
 
-  return CanHandleMediaType(aMIMEType, false, EmptyString()) != CANPLAY_NO;
+  return CanHandleMediaType(aMIMEType, false, EmptyString(), aDiagnostics)
+         != CANPLAY_NO;
 }
 
 /* static */
 CanPlayStatus
 DecoderTraits::CanHandleCodecsType(const char* aMIMEType,
-                                   const nsAString& aRequestedCodecs)
+                                   const nsAString& aRequestedCodecs,
+                                   DecoderDoctorDiagnostics* aDiagnostics)
 {
   char const* const* codecList = nullptr;
 #ifdef MOZ_RAW
   if (IsRawType(nsDependentCString(aMIMEType))) {
     codecList = gRawCodecs;
   }
 #endif
   if (IsOggType(nsDependentCString(aMIMEType))) {
@@ -395,18 +400,18 @@ DecoderTraits::CanHandleCodecsType(const
     } else {
       // We can only reach this position if a particular codec was requested,
       // webm is supported and working: the codec must be invalid.
       return CANPLAY_NO;
     }
   }
 #endif
 #ifdef MOZ_FMP4
-  if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType))) {
-    if (IsMP4SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
+  if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType), aDiagnostics)) {
+    if (IsMP4SupportedType(nsDependentCString(aMIMEType), aDiagnostics, aRequestedCodecs)) {
       return CANPLAY_YES;
     } else {
       // We can only reach this position if a particular codec was requested,
       // fmp4 is supported and working: the codec must be invalid.
       return CANPLAY_NO;
     }
   }
 #endif
@@ -462,38 +467,41 @@ DecoderTraits::CanHandleCodecsType(const
 
   return CANPLAY_YES;
 }
 
 /* static */
 CanPlayStatus
 DecoderTraits::CanHandleMediaType(const char* aMIMEType,
                                   bool aHaveRequestedCodecs,
-                                  const nsAString& aRequestedCodecs)
+                                  const nsAString& aRequestedCodecs,
+                                  DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aHaveRequestedCodecs) {
-    CanPlayStatus result = CanHandleCodecsType(aMIMEType, aRequestedCodecs);
+    CanPlayStatus result = CanHandleCodecsType(aMIMEType,
+                                               aRequestedCodecs,
+                                               aDiagnostics);
     if (result == CANPLAY_NO || result == CANPLAY_YES) {
       return result;
     }
   }
 #ifdef MOZ_RAW
   if (IsRawType(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
 #endif
   if (IsOggType(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
-  if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType))) {
+  if (IsMP4TypeAndEnabled(nsDependentCString(aMIMEType), aDiagnostics)) {
     return CANPLAY_MAYBE;
   }
 #if !defined(MOZ_OMX_WEBM_DECODER)
   if (IsWebMTypeAndEnabled(nsDependentCString(aMIMEType))) {
     return CANPLAY_MAYBE;
   }
 #endif
   if (IsMP3SupportedType(nsDependentCString(aMIMEType))) {
@@ -524,23 +532,25 @@ DecoderTraits::CanHandleMediaType(const 
   }
 #endif
   return CANPLAY_NO;
 }
 
 // Instantiates but does not initialize decoder.
 static
 already_AddRefed<MediaDecoder>
-InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
+InstantiateDecoder(const nsACString& aType,
+                   MediaDecoderOwner* aOwner,
+                   DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<MediaDecoder> decoder;
 
 #ifdef MOZ_FMP4
-  if (IsMP4SupportedType(aType)) {
+  if (IsMP4SupportedType(aType, aDiagnostics)) {
     decoder = new MP4Decoder(aOwner);
     return decoder.forget();
   }
 #endif
   if (IsMP3SupportedType(aType)) {
     decoder = new MP3Decoder(aOwner);
     return decoder.forget();
   }
@@ -611,33 +621,35 @@ InstantiateDecoder(const nsACString& aTy
   }
 #endif
 
   return nullptr;
 }
 
 /* static */
 already_AddRefed<MediaDecoder>
-DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
+DecoderTraits::CreateDecoder(const nsACString& aType,
+                             MediaDecoderOwner* aOwner,
+                             DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  return InstantiateDecoder(aType, aOwner);
+  return InstantiateDecoder(aType, aOwner, aDiagnostics);
 }
 
 /* static */
 MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, AbstractMediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MediaDecoderReader* decoderReader = nullptr;
 
   if (!aDecoder) {
     return decoderReader;
   }
 #ifdef MOZ_FMP4
-  if (IsMP4SupportedType(aType)) {
+  if (IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr)) {
     decoderReader = new MediaFormatReader(aDecoder, new MP4Demuxer(aDecoder->GetResource()));
   } else
 #endif
   if (IsMP3SupportedType(aType)) {
     decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource()));
   } else
   if (IsAACSupportedType(aType)) {
     decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource()));
@@ -701,17 +713,17 @@ bool DecoderTraits::IsSupportedInVideoDo
     (IsOmxSupportedType(aType) &&
      !IsB2GSupportOnlyType(aType)) ||
 #endif
     IsWebMSupportedType(aType) ||
 #ifdef MOZ_ANDROID_OMX
     (MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) ||
 #endif
 #ifdef MOZ_FMP4
-    IsMP4SupportedType(aType) ||
+    IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr) ||
 #endif
     IsMP3SupportedType(aType) ||
     IsAACSupportedType(aType) ||
 #ifdef MOZ_DIRECTSHOW
     IsDirectShowSupportedType(aType) ||
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
     IsRtspSupportedType(aType) ||
--- a/dom/media/DecoderTraits.h
+++ b/dom/media/DecoderTraits.h
@@ -10,16 +10,17 @@
 #include "nsCOMPtr.h"
 
 class nsAString;
 class nsACString;
 
 namespace mozilla {
 
 class AbstractMediaDecoder;
+class DecoderDoctorDiagnostics;
 class MediaDecoder;
 class MediaDecoderOwner;
 class MediaDecoderReader;
 
 enum CanPlayStatus {
   CANPLAY_NO,
   CANPLAY_MAYBE,
   CANPLAY_YES
@@ -30,36 +31,40 @@ public:
   // Returns the CanPlayStatus indicating if we can handle this
   // MIME type. The MIME type should not include the codecs parameter.
   // That parameter should be passed in aRequestedCodecs, and will only be
   // used if whether a given MIME type being handled depends on the
   // codec that will be used.  If the codecs parameter has not been
   // specified, pass false in aHaveRequestedCodecs.
   static CanPlayStatus CanHandleMediaType(const char* aMIMEType,
                                           bool aHaveRequestedCodecs,
-                                          const nsAString& aRequestedCodecs);
+                                          const nsAString& aRequestedCodecs,
+                                          DecoderDoctorDiagnostics* aDiagnostics);
 
   // Returns the CanPlayStatus indicating if we can handle this MIME type and
   // codecs type natively, excluding any plugins-based reader (such as
   // GStreamer)
   // The MIME type should not include the codecs parameter.
   // That parameter is passed in aRequestedCodecs.
   static CanPlayStatus CanHandleCodecsType(const char* aMIMEType,
-                                           const nsAString& aRequestedCodecs);
+                                           const nsAString& aRequestedCodecs,
+                                           DecoderDoctorDiagnostics* aDiagnostics);
 
   // Returns true if we should handle this MIME type when it appears
   // as an <object> or as a toplevel page. If, in practice, our support
   // for the type is more limited than appears in the wild, we should return
   // false here even if CanHandleMediaType would return true.
-  static bool ShouldHandleMediaType(const char* aMIMEType);
+  static bool ShouldHandleMediaType(const char* aMIMEType,
+                                    DecoderDoctorDiagnostics* aDiagnostics);
 
   // Create a decoder for the given aType. Returns null if we
   // were unable to create the decoder.
   static already_AddRefed<MediaDecoder> CreateDecoder(const nsACString& aType,
-                                                      MediaDecoderOwner* aOwner);
+                                                      MediaDecoderOwner* aOwner,
+                                                      DecoderDoctorDiagnostics* aDiagnostics);
 
   // Create a reader for thew given MIME type aType. Returns null
   // if we were unable to create the reader.
   static MediaDecoderReader* CreateReader(const nsACString& aType,
                                           AbstractMediaDecoder* aDecoder);
 
   // Returns true if MIME type aType is supported in video documents,
   // or false otherwise. Not all platforms support all MIME types, and
@@ -67,15 +72,16 @@ public:
   static bool IsSupportedInVideoDocument(const nsACString& aType);
 
   // Returns true if we should not start decoder until we receive
   // OnConnected signal. (currently RTSP only)
   static bool DecoderWaitsForOnConnected(const nsACString& aType);
 
   static bool IsWebMTypeAndEnabled(const nsACString& aType);
   static bool IsWebMAudioType(const nsACString& aType);
-  static bool IsMP4TypeAndEnabled(const nsACString& aType);
+  static bool IsMP4TypeAndEnabled(const nsACString& aType,
+                                  DecoderDoctorDiagnostics* aDiagnostics);
 };
 
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/MP3Decoder.cpp
+++ b/dom/media/MP3Decoder.cpp
@@ -29,17 +29,18 @@ MP3Decoder::CreateStateMachine() {
   return new MediaDecoderStateMachine(this, reader);
 }
 
 /* static */
 bool
 MP3Decoder::IsEnabled() {
   PDMFactory::Init();
   RefPtr<PDMFactory> platform = new PDMFactory();
-  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"));
+  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"),
+                                    /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 /* static */
 bool MP3Decoder::CanHandleMediaType(const nsACString& aType,
                                     const nsAString& aCodecs)
 {
   if (aType.EqualsASCII("audio/mp3") || aType.EqualsASCII("audio/mpeg")) {
     return IsEnabled() &&
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -390,27 +390,29 @@ MediaFormatReader::EnsureDecoderCreated(
 
   switch (aTrack) {
     case TrackType::kAudioTrack:
       decoder.mDecoder =
         mPlatform->CreateDecoder(decoder.mInfo ?
                                    *decoder.mInfo->GetAsAudioInfo() :
                                    mInfo.mAudio,
                                  decoder.mTaskQueue,
-                                 decoder.mCallback);
+                                 decoder.mCallback,
+                                 /* DecoderDoctorDiagnostics* */ nullptr);
       break;
     case TrackType::kVideoTrack:
       // Decoders use the layers backend to decide if they can use hardware decoding,
       // so specify LAYERS_NONE if we want to forcibly disable it.
       decoder.mDecoder =
         mPlatform->CreateDecoder(mVideo.mInfo ?
                                    *mVideo.mInfo->GetAsVideoInfo() :
                                    mInfo.mVideo,
                                  decoder.mTaskQueue,
                                  decoder.mCallback,
+                                 /* DecoderDoctorDiagnostics* */ nullptr,
                                  mLayersBackendType,
                                  GetImageContainer());
       break;
     default:
       break;
   }
   if (decoder.mDecoder ) {
     decoder.mDescription = decoder.mDecoder->GetDescriptionName();
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -356,17 +356,18 @@ GMPDecryptsAndGeckoDecodesH264(mozIGecko
   MOZ_ASSERT(HaveGMPFor(aGMPService,
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   MOZ_ASSERT(IsH264ContentType(aContentType));
   return !HaveGMPFor(aGMPService,
                      NS_ConvertUTF16toUTF8(aKeySystem),
                      NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
                      NS_LITERAL_CSTRING("h264")) &&
-         MP4Decoder::CanHandleMediaType(aContentType);
+         MP4Decoder::CanHandleMediaType(aContentType,
+                                        /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 static bool
 GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
                               const nsAString& aKeySystem,
                               const nsAString& aContentType)
 {
   MOZ_ASSERT(HaveGMPFor(aGMPService,
@@ -381,17 +382,18 @@ GMPDecryptsAndGeckoDecodesAAC(mozIGeckoM
 #if defined(MOZ_WIDEVINE_EME) && defined(XP_WIN)
          // Widevine CDM doesn't include an AAC decoder. So if WMF can't
          // decode AAC, and a codec wasn't specified, be conservative
          // and reject the MediaKeys request, since our policy is to prevent
          //  the Adobe GMP's unencrypted AAC decoding path being used to
          // decode content decrypted by the Widevine CDM.
         (!aKeySystem.EqualsLiteral("com.widevine.alpha") || WMFDecoderModule::HasAAC()) &&
 #endif
-    MP4Decoder::CanHandleMediaType(aContentType);
+    MP4Decoder::CanHandleMediaType(aContentType,
+                                   /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 static bool
 IsSupportedAudio(mozIGeckoMediaPluginService* aGMPService,
                  const nsAString& aKeySystem,
                  const nsAString& aAudioType)
 {
   return IsAACContentType(aAudioType) &&
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -85,17 +85,18 @@ IsWhitelistedH264Codec(const nsAString& 
           profile == H264_PROFILE_MAIN ||
           profile == H264_PROFILE_EXTENDED ||
           profile == H264_PROFILE_HIGH);
 }
 
 /* static */
 bool
 MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
-                               const nsAString& aCodecs)
+                               const nsAString& aCodecs,
+                               DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (!IsEnabled()) {
     return false;
   }
 
   // Whitelist MP4 types, so they explicitly match what we encounter on
   // the web, as opposed to what we use internally (i.e. what our demuxers
   // etc output).
@@ -148,37 +149,40 @@ MP4Decoder::CanHandleMediaType(const nsA
       return false;
     }
   }
 
   // Verify that we have a PDM that supports the whitelisted types.
   PDMFactory::Init();
   RefPtr<PDMFactory> platform = new PDMFactory();
   for (const nsCString& codecMime : codecMimes) {
-    if (!platform->SupportsMimeType(codecMime)) {
+    if (!platform->SupportsMimeType(codecMime, aDiagnostics)) {
       return false;
     }
   }
 
   return true;
 }
 
 /* static */ bool
-MP4Decoder::CanHandleMediaType(const nsAString& aContentType)
+MP4Decoder::CanHandleMediaType(const nsAString& aContentType,
+                               DecoderDoctorDiagnostics* aDiagnostics)
 {
   nsContentTypeParser parser(aContentType);
   nsAutoString mimeType;
   nsresult rv = parser.GetType(mimeType);
   if (NS_FAILED(rv)) {
     return false;
   }
   nsString codecs;
   parser.GetParameter("codecs", codecs);
 
-  return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs);
+  return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType),
+                            codecs,
+                            aDiagnostics);
 }
 
 /* static */
 bool
 MP4Decoder::IsEnabled()
 {
   return Preferences::GetBool("media.mp4.enabled", true);
 }
@@ -226,17 +230,19 @@ CreateTestH264Decoder(layers::LayersBack
   aConfig.mExtraData = new MediaByteBuffer();
   aConfig.mExtraData->AppendElements(sTestH264ExtraData,
                                      MOZ_ARRAY_LENGTH(sTestH264ExtraData));
 
   PDMFactory::Init();
 
   RefPtr<PDMFactory> platform = new PDMFactory();
   RefPtr<MediaDataDecoder> decoder(
-    platform->CreateDecoder(aConfig, aTaskQueue, nullptr, aBackend, nullptr));
+    platform->CreateDecoder(aConfig, aTaskQueue, nullptr,
+                            /* DecoderDoctorDiagnostics* */ nullptr,
+                            aBackend, nullptr));
 
   return decoder.forget();
 }
 
 /* static */ already_AddRefed<dom::Promise>
 MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend, nsIGlobalObject* aParent)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -27,19 +27,21 @@ public:
 
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if aMIMEType is a type that we think we can render with the
   // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
   // with a comma-delimited list of codecs to check support for. Notes in
   // out params wether the codecs string contains AAC or H.264.
   static bool CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
-                                 const nsAString& aCodecs);
+                                 const nsAString& aCodecs,
+                                 DecoderDoctorDiagnostics* aDiagnostics);
 
-  static bool CanHandleMediaType(const nsAString& aMIMEType);
+  static bool CanHandleMediaType(const nsAString& aMIMEType,
+                                 DecoderDoctorDiagnostics* aDiagnostics);
 
   // Returns true if the MP4 backend is preffed on.
   static bool IsEnabled();
 
   static already_AddRefed<dom::Promise>
   IsVideoAccelerated(layers::LayersBackend aBackend, nsIGlobalObject* aParent);
 
   void GetMozDebugReaderData(nsAString& aString) override;
--- a/dom/media/gtest/TestMediaDataDecoder.cpp
+++ b/dom/media/gtest/TestMediaDataDecoder.cpp
@@ -37,17 +37,18 @@ public:
   }
 
 private:
   RefPtr<Benchmark> mBenchmark;
 };
 
 TEST(MediaDataDecoder, H264)
 {
-  if (!DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"))) {
+  if (!DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4")
+        , /* DecoderDoctorDiagnostics* */ nullptr)) {
     EXPECT_TRUE(true);
   } else {
     RefPtr<MediaResource> resource =
       new MockMediaResource("gizmo.mp4", NS_LITERAL_CSTRING("video/mp4"));
     nsresult rv = resource->Open(nullptr);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
 
     BenchmarkRunner runner(new Benchmark(new MP4Demuxer(resource)));
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -70,26 +70,27 @@ static const char* const gMediaSourceTyp
 // 1. If MP4/H264 isn't supported:
 //   * Windows XP
 //   * Windows Vista and Server 2008 without the optional "Platform Update Supplement"
 //   * N/KN editions (Europe and Korea) of Windows 7/8/8.1/10 without the
 //     optional "Windows Media Feature Pack"
 // 2. If H264 hardware acceleration is not available.
 // 3. The CPU is considered to be fast enough
 static bool
-IsWebMForced()
+IsWebMForced(DecoderDoctorDiagnostics* aDiagnostics)
 {
   bool mp4supported =
-    DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"));
+    DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"),
+                                       aDiagnostics);
   bool hwsupported = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
   return !mp4supported || !hwsupported || VP9Benchmark::IsVP9DecodeFast();
 }
 
 static nsresult
-IsTypeSupported(const nsAString& aType)
+IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (aType.IsEmpty()) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
   nsContentTypeParser parser(aType);
   nsAutoString mimeType;
   nsresult rv = parser.GetType(mimeType);
   if (NS_FAILED(rv)) {
@@ -97,36 +98,38 @@ IsTypeSupported(const nsAString& aType)
   }
   NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
 
   nsAutoString codecs;
   bool hasCodecs = NS_SUCCEEDED(parser.GetParameter("codecs", codecs));
 
   for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
     if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
-      if (DecoderTraits::IsMP4TypeAndEnabled(mimeTypeUTF8)) {
+      if (DecoderTraits::IsMP4TypeAndEnabled(mimeTypeUTF8, aDiagnostics)) {
         if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) {
           return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         }
         if (hasCodecs &&
             DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(),
-                                               codecs) == CANPLAY_NO) {
+                                               codecs,
+                                               aDiagnostics) == CANPLAY_NO) {
           return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         }
         return NS_OK;
       } else if (DecoderTraits::IsWebMTypeAndEnabled(mimeTypeUTF8)) {
         if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
               (Preferences::GetBool("media.mediasource.webm.audio.enabled", true) &&
                DecoderTraits::IsWebMAudioType(mimeTypeUTF8)) ||
-              IsWebMForced())) {
+              IsWebMForced(aDiagnostics))) {
           return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         }
         if (hasCodecs &&
             DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(),
-                                               codecs) == CANPLAY_NO) {
+                                               codecs,
+                                               aDiagnostics) == CANPLAY_NO) {
           return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
         }
         return NS_OK;
       }
     }
   }
 
   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
@@ -215,17 +218,18 @@ MediaSource::SetDuration(double aDuratio
   MSE_API("SetDuration(aDuration=%f)", aDuration);
   mDecoder->SetMediaSourceDuration(aDuration, aAction);
 }
 
 already_AddRefed<SourceBuffer>
 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  nsresult rv = mozilla::IsTypeSupported(aType);
+  nsresult rv = mozilla::IsTypeSupported(aType,
+                                         /* DecoderDoctorDiagnostics* */ nullptr);
   MSE_API("AddSourceBuffer(aType=%s)%s",
           NS_ConvertUTF16toUTF8(aType).get(),
           rv == NS_OK ? "" : " [not supported]");
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
   if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
@@ -334,17 +338,18 @@ MediaSource::EndOfStream(const Optional<
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 }
 
 /* static */ bool
 MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  nsresult rv = mozilla::IsTypeSupported(aType);
+  nsresult rv = mozilla::IsTypeSupported(aType,
+                                         /* DecoderDoctorDiagnostics* */ nullptr);
 #define this nullptr
   MSE_API("IsTypeSupported(aType=%s)%s ",
           NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "OK" : "[not supported]");
 #undef this // don't ever remove this line !
   return NS_SUCCEEDED(rv);
 }
 
 /* static */ bool
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -139,64 +139,69 @@ PDMFactory::PDMFactory()
 PDMFactory::~PDMFactory()
 {
 }
 
 already_AddRefed<MediaDataDecoder>
 PDMFactory::CreateDecoder(const TrackInfo& aConfig,
                           FlushableTaskQueue* aTaskQueue,
                           MediaDataDecoderCallback* aCallback,
+                          DecoderDoctorDiagnostics* aDiagnostics,
                           layers::LayersBackend aLayersBackend,
                           layers::ImageContainer* aImageContainer)
 {
   bool isEncrypted = mEMEPDM && aConfig.mCrypto.mValid;
 
   if (isEncrypted) {
     return CreateDecoderWithPDM(mEMEPDM,
                                 aConfig,
                                 aTaskQueue,
                                 aCallback,
+                                aDiagnostics,
                                 aLayersBackend,
                                 aImageContainer);
   }
 
   for (auto& current : mCurrentPDMs) {
-    if (!current->SupportsMimeType(aConfig.mMimeType)) {
+    if (!current->SupportsMimeType(aConfig.mMimeType, aDiagnostics)) {
       continue;
     }
     RefPtr<MediaDataDecoder> m =
       CreateDecoderWithPDM(current,
                            aConfig,
                            aTaskQueue,
                            aCallback,
+                           aDiagnostics,
                            aLayersBackend,
                            aImageContainer);
     if (m) {
       return m.forget();
     }
   }
   NS_WARNING("Unable to create a decoder, no platform found.");
   return nullptr;
 }
 
 already_AddRefed<MediaDataDecoder>
 PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
                                  const TrackInfo& aConfig,
                                  FlushableTaskQueue* aTaskQueue,
                                  MediaDataDecoderCallback* aCallback,
+                                 DecoderDoctorDiagnostics* aDiagnostics,
                                  layers::LayersBackend aLayersBackend,
                                  layers::ImageContainer* aImageContainer)
 {
   MOZ_ASSERT(aPDM);
   RefPtr<MediaDataDecoder> m;
 
   if (aConfig.GetAsAudioInfo()) {
     m = aPDM->CreateAudioDecoder(*aConfig.GetAsAudioInfo(),
                                  aTaskQueue,
-                                 aCallback);
+                                 aCallback,
+                                 aDiagnostics);
     return m.forget();
   }
 
   if (!aConfig.GetAsVideoInfo()) {
     return nullptr;
   }
 
   MediaDataDecoderCallback* callback = aCallback;
@@ -211,46 +216,49 @@ PDMFactory::CreateDecoderWithPDM(Platfor
 
   if (H264Converter::IsH264(aConfig)) {
     RefPtr<H264Converter> h
       = new H264Converter(aPDM,
                           *aConfig.GetAsVideoInfo(),
                           aLayersBackend,
                           aImageContainer,
                           aTaskQueue,
-                          callback);
+                          callback,
+                          aDiagnostics);
     const nsresult rv = h->GetLastError();
     if (NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_INITIALIZED) {
       // The H264Converter either successfully created the wrapped decoder,
       // or there wasn't enough AVCC data to do so. Otherwise, there was some
       // problem, for example WMF DLLs were missing.
       m = h.forget();
     }
   } else {
     m = aPDM->CreateVideoDecoder(*aConfig.GetAsVideoInfo(),
                                  aLayersBackend,
                                  aImageContainer,
                                  aTaskQueue,
-                                 callback);
+                                 callback,
+                                 aDiagnostics);
   }
 
   if (callbackWrapper && m) {
     m = new DecoderFuzzingWrapper(m.forget(), callbackWrapper.forget());
   }
 
   return m.forget();
 }
 
 bool
-PDMFactory::SupportsMimeType(const nsACString& aMimeType) const
+PDMFactory::SupportsMimeType(const nsACString& aMimeType,
+                             DecoderDoctorDiagnostics* aDiagnostics) const
 {
   if (mEMEPDM) {
-    return mEMEPDM->SupportsMimeType(aMimeType);
+    return mEMEPDM->SupportsMimeType(aMimeType, aDiagnostics);
   }
-  RefPtr<PlatformDecoderModule> current = GetDecoder(aMimeType);
+  RefPtr<PlatformDecoderModule> current = GetDecoder(aMimeType, aDiagnostics);
   return !!current;
 }
 
 void
 PDMFactory::CreatePDMs()
 {
   RefPtr<PlatformDecoderModule> m;
 
@@ -319,21 +327,22 @@ PDMFactory::StartupPDM(PlatformDecoderMo
   if (aPDM && NS_SUCCEEDED(aPDM->Startup())) {
     mCurrentPDMs.AppendElement(aPDM);
     return true;
   }
   return false;
 }
 
 already_AddRefed<PlatformDecoderModule>
-PDMFactory::GetDecoder(const nsACString& aMimeType) const
+PDMFactory::GetDecoder(const nsACString& aMimeType,
+                       DecoderDoctorDiagnostics* aDiagnostics) const
 {
   RefPtr<PlatformDecoderModule> pdm;
   for (auto& current : mCurrentPDMs) {
-    if (current->SupportsMimeType(aMimeType)) {
+    if (current->SupportsMimeType(aMimeType, aDiagnostics)) {
       pdm = current;
       break;
     }
   }
   return pdm.forget();
 }
 
 #ifdef MOZ_EME
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -8,16 +8,18 @@
 #define PDMFactory_h_
 
 #include "PlatformDecoderModule.h"
 
 class CDMProxy;
 
 namespace mozilla {
 
+class DecoderDoctorDiagnostics;
+
 class PDMFactory final {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PDMFactory)
 
   PDMFactory();
 
   // Call on the main thread to initialize the static state
   // needed by Create().
@@ -27,20 +29,22 @@ public:
   // the platform we're running on. Caller is responsible for deleting this
   // instance. It's expected that there will be multiple
   // PlatformDecoderModules alive at the same time.
   // This is called on the decode task queue.
   already_AddRefed<MediaDataDecoder>
   CreateDecoder(const TrackInfo& aConfig,
                 FlushableTaskQueue* aTaskQueue,
                 MediaDataDecoderCallback* aCallback,
+                DecoderDoctorDiagnostics* aDiagnostics,
                 layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE,
                 layers::ImageContainer* aImageContainer = nullptr);
 
-  bool SupportsMimeType(const nsACString& aMimeType) const;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const;
 
 #ifdef MOZ_EME
   // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
   // decrypt-and-decode EME encrypted content. If the CDM only decrypts and
   // does not decode, we create a PDM and use that to create MediaDataDecoders
   // that we use on on aTaskQueue to decode the decrypted stream.
   // This is called on the decode task queue.
   void SetCDMProxy(CDMProxy* aProxy);
@@ -48,23 +52,25 @@ public:
 
 private:
   virtual ~PDMFactory();
   void CreatePDMs();
   // Startup the provided PDM and add it to our list if successful.
   bool StartupPDM(PlatformDecoderModule* aPDM);
   // Returns the first PDM in our list supporting the mimetype.
   already_AddRefed<PlatformDecoderModule>
-  GetDecoder(const nsACString& aMimeType) const;
+  GetDecoder(const nsACString& aMimeType,
+             DecoderDoctorDiagnostics* aDiagnostics) const;
 
   already_AddRefed<MediaDataDecoder>
   CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
                        const TrackInfo& aConfig,
                        FlushableTaskQueue* aTaskQueue,
                        MediaDataDecoderCallback* aCallback,
+                       DecoderDoctorDiagnostics* aDiagnostics,
                        layers::LayersBackend aLayersBackend,
                        layers::ImageContainer* aImageContainer);
 
   // PDM pref caches...
   static bool sUseBlankDecoder;
 #ifdef MOZ_GONK_MEDIACODEC
   static bool sGonkDecoderEnabled;
 #endif
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -15,16 +15,17 @@
 #include "mozilla/RefPtr.h"
 #include <queue>
 
 namespace mozilla {
 class TrackInfo;
 class AudioInfo;
 class VideoInfo;
 class MediaRawData;
+class DecoderDoctorDiagnostics;
 
 namespace layers {
 class ImageContainer;
 } // namespace layers
 
 class MediaDataDecoder;
 class MediaDataDecoderCallback;
 class FlushableTaskQueue;
@@ -48,17 +49,18 @@ class PlatformDecoderModule {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule)
 
   // Perform any per-instance initialization.
   // This is called on the decode task queue.
   virtual nsresult Startup() { return NS_OK; };
 
   // Indicates if the PlatformDecoderModule supports decoding of aMimeType.
-  virtual bool SupportsMimeType(const nsACString& aMimeType) const = 0;
+  virtual bool SupportsMimeType(const nsACString& aMimeType,
+                                DecoderDoctorDiagnostics* aDiagnostics) const = 0;
 
   enum ConversionRequired {
     kNeedNone,
     kNeedAVCC,
     kNeedAnnexB,
   };
 
   // Indicates that the decoder requires a specific format.
@@ -84,32 +86,34 @@ protected:
   // Returns nullptr if the decoder can't be created.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) = 0;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) = 0;
 
   // Creates an Audio decoder with the specified properties.
   // Asynchronous decoding of audio should be done in runnables dispatched to
   // aAudioTaskQueue. If the task queue isn't needed, the decoder should
   // not hold a reference to it.
   // Output and errors should be returned to the reader via aCallback.
   // Returns nullptr if the decoder can't be created.
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) = 0;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) = 0;
 };
 
 // A callback used by MediaDataDecoder to return output/errors to the
 // MediaFormatReader.
 // Implementation is threadsafe, and can be called on any thread.
 class MediaDataDecoderCallback {
 public:
   virtual ~MediaDataDecoderCallback() {}
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
@@ -8,47 +8,50 @@
 #include "OpusDecoder.h"
 #include "VorbisDecoder.h"
 #include "VPXDecoder.h"
 #include "WAVDecoder.h"
 
 namespace mozilla {
 
 bool
-AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                        DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return VPXDecoder::IsVPX(aMimeType) ||
     OpusDataDecoder::IsOpus(aMimeType) ||
     VorbisDataDecoder::IsVorbis(aMimeType) ||
     WaveDataDecoder::IsWave(aMimeType);
 }
 
 already_AddRefed<MediaDataDecoder>
 AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                           layers::LayersBackend aLayersBackend,
                                           layers::ImageContainer* aImageContainer,
                                           FlushableTaskQueue* aVideoTaskQueue,
-                                          MediaDataDecoderCallback* aCallback)
+                                          MediaDataDecoderCallback* aCallback,
+                                          DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> m;
 
   if (VPXDecoder::IsVPX(aConfig.mMimeType)) {
     m = new VPXDecoder(*aConfig.GetAsVideoInfo(),
                        aImageContainer,
                        aVideoTaskQueue,
                        aCallback);
   }
 
   return m.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                           FlushableTaskQueue* aAudioTaskQueue,
-                                          MediaDataDecoderCallback* aCallback)
+                                          MediaDataDecoderCallback* aCallback,
+                                          DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> m;
 
   if (VorbisDataDecoder::IsVorbis(aConfig.mMimeType)) {
     m = new VorbisDataDecoder(*aConfig.GetAsAudioInfo(),
                               aAudioTaskQueue,
                               aCallback);
   } else if (OpusDataDecoder::IsOpus(aConfig.mMimeType)) {
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.h
+++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.h
@@ -5,35 +5,38 @@
 
 namespace mozilla {
 
 class AgnosticDecoderModule : public PlatformDecoderModule {
 public:
   AgnosticDecoderModule() = default;
   virtual ~AgnosticDecoderModule() = default;
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override
   {
     return ConversionRequired::kNeedNone;
   }
 
 protected:
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 };
 
 } // namespace mozilla
 
 #endif /* AgnosticDecoderModule_h_ */
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -225,45 +225,48 @@ class BlankDecoderModule : public Platfo
 public:
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override {
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override {
     BlankVideoDataCreator* creator = new BlankVideoDataCreator(
       aConfig.mDisplay.width, aConfig.mDisplay.height, aImageContainer);
     RefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankVideoDataCreator>(creator,
                                                        aVideoTaskQueue,
                                                        aCallback,
                                                        TrackInfo::kVideoTrack);
     return decoder.forget();
   }
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override {
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override {
     BlankAudioDataCreator* creator = new BlankAudioDataCreator(
       aConfig.mChannels, aConfig.mRate);
 
     RefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
                                                        aAudioTaskQueue,
                                                        aCallback,
                                                        TrackInfo::kAudioTrack);
     return decoder.forget();
   }
 
   bool
-  SupportsMimeType(const nsACString& aMimeType) const override
+  SupportsMimeType(const nsACString& aMimeType,
+                   DecoderDoctorDiagnostics* aDiagnostics) const override
   {
     return true;
   }
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override
   {
     return kNeedNone;
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -236,17 +236,18 @@ CreateDecoderWrapper(MediaDataDecoderCal
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 EMEDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      layers::LayersBackend aLayersBackend,
                                      layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(aConfig.mCrypto.mValid);
 
   if (mCDMDecodesVideo) {
     RefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aVideoTaskQueue);
     wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy,
                                                 aConfig,
                                                 aLayersBackend,
@@ -256,48 +257,50 @@ EMEDecoderModule::CreateVideoDecoder(con
     return wrapper.forget();
   }
 
   MOZ_ASSERT(mPDM);
   RefPtr<MediaDataDecoder> decoder(
     mPDM->CreateDecoder(aConfig,
                         aVideoTaskQueue,
                         aCallback,
+                        aDiagnostics,
                         aLayersBackend,
                         aImageContainer));
   if (!decoder) {
     return nullptr;
   }
 
   RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy,
                                                          AbstractThread::GetCurrent()->AsTaskQueue()));
   return emeDecoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 EMEDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                      FlushableTaskQueue* aAudioTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(aConfig.mCrypto.mValid);
 
   if (mCDMDecodesAudio) {
     RefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
     wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
                                                 aConfig,
                                                 aAudioTaskQueue,
                                                 wrapper->Callback()));
     return wrapper.forget();
   }
 
   MOZ_ASSERT(mPDM);
   RefPtr<MediaDataDecoder> decoder(
-    mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback));
+    mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback, aDiagnostics));
   if (!decoder) {
     return nullptr;
   }
 
   RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
                                                          aCallback,
                                                          mProxy,
                                                          AbstractThread::GetCurrent()->AsTaskQueue()));
@@ -310,16 +313,17 @@ EMEDecoderModule::DecoderNeedsConversion
   if (aConfig.IsVideo()) {
     return kNeedAVCC;
   } else {
     return kNeedNone;
   }
 }
 
 bool
-EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                   DecoderDoctorDiagnostics* aDiagnostics) const
 {
   Maybe<nsCString> gmp;
   gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
   return GMPDecoderModule::SupportsMimeType(aMimeType, gmp);
 }
 
 } // namespace mozilla
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
@@ -28,29 +28,32 @@ public:
 
 protected:
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                     layers::LayersBackend aLayersBackend,
                     layers::ImageContainer* aImageContainer,
                     FlushableTaskQueue* aVideoTaskQueue,
-                    MediaDataDecoderCallback* aCallback) override;
+                    MediaDataDecoderCallback* aCallback,
+                    DecoderDoctorDiagnostics* aDiagnostics) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   bool
-  SupportsMimeType(const nsACString& aMimeType) const override;
+  SupportsMimeType(const nsACString& aMimeType,
+                   DecoderDoctorDiagnostics* aDiagnostics) const override;
 
 private:
   RefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
   RefPtr<PDMFactory> mPDM;
   // We run the PDM on its own task queue.
   RefPtr<TaskQueue> mTaskQueue;
   bool mCDMDecodesAudio;
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
@@ -43,17 +43,18 @@ CreateDecoderWrapper(MediaDataDecoderCal
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 GMPDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      layers::LayersBackend aLayersBackend,
                                      layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (!aConfig.mMimeType.EqualsLiteral("video/avc")) {
     return nullptr;
   }
 
   RefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
   wrapper->SetProxyTarget(new GMPVideoDecoder(aConfig,
                                               aLayersBackend,
@@ -61,17 +62,18 @@ GMPDecoderModule::CreateVideoDecoder(con
                                               aVideoTaskQueue,
                                               wrapper->Callback()));
   return wrapper.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 GMPDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                      FlushableTaskQueue* aAudioTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (!aConfig.mMimeType.EqualsLiteral("audio/mp4a-latm")) {
     return nullptr;
   }
 
   RefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
   wrapper->SetProxyTarget(new GMPAudioDecoder(aConfig,
                                               aAudioTaskQueue,
@@ -97,21 +99,23 @@ HasGMPFor(const nsACString& aAPI,
 {
   MOZ_ASSERT(NS_IsMainThread());
 #ifdef XP_WIN
   // gmp-clearkey uses WMF for decoding, so if we're using clearkey we must
   // verify that WMF works before continuing.
   if (aGMP.EqualsLiteral("org.w3.clearkey")) {
     RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
     if (aCodec.EqualsLiteral("aac") &&
-        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))) {
+        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"),
+                               /* DecoderDoctorDiagnostics* */ nullptr)) {
       return false;
     }
     if (aCodec.EqualsLiteral("h264") &&
-        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"))) {
+        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"),
+                               /* DecoderDoctorDiagnostics* */ nullptr)) {
       return false;
     }
   }
 #endif
   nsTArray<nsCString> tags;
   tags.AppendElement(aCodec);
   tags.AppendElement(aGMP);
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
@@ -220,14 +224,15 @@ GMPDecoderModule::SupportsMimeType(const
       return true;
     }
   }
 
   return false;
 }
 
 bool
-GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                   DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return SupportsMimeType(aMimeType, PreferredGMP(aMimeType));
 }
 
 } // namespace mozilla
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
@@ -32,29 +32,32 @@ public:
   virtual ~GMPDecoderModule();
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   bool
-  SupportsMimeType(const nsACString& aMimeType) const override;
+  SupportsMimeType(const nsACString& aMimeType,
+                   DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   // Main thread only.
   static void Init();
 
   static const Maybe<nsCString> PreferredGMP(const nsACString& aMimeType);
 
   static bool SupportsMimeType(const nsACString& aMimeType,
                                const Maybe<nsCString>& aGMP);
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -237,17 +237,18 @@ public:
                                            numChannels,
                                            sampleRate);
     INVOKE_CALLBACK(Output, data);
     return NS_OK;
   }
 };
 
 bool
-AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                       DecoderDoctorDiagnostics* aDiagnostics) const
 {
   if (!AndroidBridge::Bridge() ||
       AndroidBridge::Bridge()->GetAPIVersion() < 16) {
     return false;
   }
 
   if (aMimeType.EqualsLiteral("video/mp4") ||
       aMimeType.EqualsLiteral("video/avc")) {
@@ -275,17 +276,18 @@ AndroidDecoderModule::SupportsMimeType(c
   return widget::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
       nsCString(TranslateMimeType(aMimeType)));
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(
     const VideoInfo& aConfig, layers::LayersBackend aLayersBackend,
     layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue,
-    MediaDataDecoderCallback* aCallback)
+    MediaDataDecoderCallback* aCallback,
+    DecoderDoctorDiagnostics* aDiagnostics)
 {
   MediaFormat::LocalRef format;
 
   NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
       TranslateMimeType(aConfig.mMimeType),
       aConfig.mDisplay.width,
       aConfig.mDisplay.height,
       &format), nullptr);
@@ -294,17 +296,18 @@ AndroidDecoderModule::CreateVideoDecoder
     new VideoDataDecoder(aConfig, format, aCallback, aImageContainer);
 
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateAudioDecoder(
     const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue,
-    MediaDataDecoderCallback* aCallback)
+    MediaDataDecoderCallback* aCallback,
+    DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(aConfig.mBitDepth == 16, "We only handle 16-bit audio!");
 
   MediaFormat::LocalRef format;
 
   LOG("CreateAudioFormat with mimeType=%s, mRate=%d, channels=%d",
       aConfig.mMimeType.Data(), aConfig.mRate, aConfig.mChannels);
 
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -20,28 +20,31 @@ typedef std::deque<RefPtr<MediaRawData>>
 
 class AndroidDecoderModule : public PlatformDecoderModule {
 public:
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
 
   AndroidDecoderModule() {}
   virtual ~AndroidDecoderModule() {}
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 };
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
 
--- a/dom/media/platforms/apple/AppleDecoderModule.cpp
+++ b/dom/media/platforms/apple/AppleDecoderModule.cpp
@@ -78,17 +78,18 @@ AppleDecoderModule::Startup()
   return NS_OK;
 }
 
 already_AddRefed<MediaDataDecoder>
 AppleDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                        layers::LayersBackend aLayersBackend,
                                        layers::ImageContainer* aImageContainer,
                                        FlushableTaskQueue* aVideoTaskQueue,
-                                       MediaDataDecoderCallback* aCallback)
+                                       MediaDataDecoderCallback* aCallback,
+                                       DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> decoder;
 
   if (sIsVDAAvailable && (!sIsVTHWAvailable || sForceVDA)) {
     decoder =
       AppleVDADecoder::CreateVDADecoder(aConfig,
                                         aVideoTaskQueue,
                                         aCallback,
@@ -104,25 +105,27 @@ AppleDecoderModule::CreateVideoDecoder(c
       new AppleVTDecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
   }
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                        FlushableTaskQueue* aAudioTaskQueue,
-                                       MediaDataDecoderCallback* aCallback)
+                                       MediaDataDecoderCallback* aCallback,
+                                       DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> decoder =
     new AppleATDecoder(aConfig, aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
 bool
-AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                     DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return (sIsCoreMediaAvailable &&
           (aMimeType.EqualsLiteral("audio/mpeg") ||
            aMimeType.EqualsLiteral("audio/mp4a-latm"))) ||
     ((sIsVTAvailable || sIsVDAAvailable) &&
      (aMimeType.EqualsLiteral("video/mp4") ||
       aMimeType.EqualsLiteral("video/avc")));
 }
--- a/dom/media/platforms/apple/AppleDecoderModule.h
+++ b/dom/media/platforms/apple/AppleDecoderModule.h
@@ -19,25 +19,28 @@ public:
   nsresult Startup() override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   static void Init();
 
   static bool sCanUseHardwareVideoDecoder;
 
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -30,39 +30,42 @@ public:
   explicit FFmpegDecoderModule(FFmpegLibWrapper* aLib) : mLib(aLib) {}
   virtual ~FFmpegDecoderModule() {}
 
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override
   {
     RefPtr<MediaDataDecoder> decoder =
       new FFmpegVideoDecoder<V>(mLib, aVideoTaskQueue, aCallback, aConfig,
                                 aImageContainer);
     return decoder.forget();
   }
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override
   {
 #ifdef USING_MOZFFVPX
     return nullptr;
 #else
     RefPtr<MediaDataDecoder> decoder =
       new FFmpegAudioDecoder<V>(mLib, aAudioTaskQueue, aCallback, aConfig);
     return decoder.forget();
 #endif
   }
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const override
   {
     AVCodecID videoCodec = FFmpegVideoDecoder<V>::GetCodecId(aMimeType);
 #ifdef USING_MOZFFVPX
     if (videoCodec == AV_CODEC_ID_NONE) {
         return false;
     }
     return !!FFmpegDataDecoder<V>::FindAVCodec(mLib, videoCodec);
 #else
--- a/dom/media/platforms/gonk/GonkDecoderModule.cpp
+++ b/dom/media/platforms/gonk/GonkDecoderModule.cpp
@@ -26,28 +26,30 @@ GonkDecoderModule::Init()
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
 }
 
 already_AddRefed<MediaDataDecoder>
 GonkDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      mozilla::layers::LayersBackend aLayersBackend,
                                      mozilla::layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> decoder =
   new GonkMediaDataDecoder(new GonkVideoDecoderManager(aImageContainer, aConfig),
                            aVideoTaskQueue, aCallback);
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 GonkDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                       FlushableTaskQueue* aAudioTaskQueue,
-                                      MediaDataDecoderCallback* aCallback)
+                                      MediaDataDecoderCallback* aCallback,
+                                      DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<MediaDataDecoder> decoder =
   new GonkMediaDataDecoder(new GonkAudioDecoderManager(aConfig),
                            aAudioTaskQueue, aCallback);
   return decoder.forget();
 }
 
 PlatformDecoderModule::ConversionRequired
@@ -56,17 +58,18 @@ GonkDecoderModule::DecoderNeedsConversio
   if (aConfig.IsVideo()) {
     return kNeedAnnexB;
   } else {
     return kNeedNone;
   }
 }
 
 bool
-GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                    DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
     aMimeType.EqualsLiteral("audio/3gpp") ||
     aMimeType.EqualsLiteral("audio/amr-wb") ||
     aMimeType.EqualsLiteral("audio/mpeg") ||
     aMimeType.EqualsLiteral("video/mp4") ||
     aMimeType.EqualsLiteral("video/mp4v-es") ||
     aMimeType.EqualsLiteral("video/avc") ||
--- a/dom/media/platforms/gonk/GonkDecoderModule.h
+++ b/dom/media/platforms/gonk/GonkDecoderModule.h
@@ -17,28 +17,31 @@ public:
   virtual ~GonkDecoderModule();
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      mozilla::layers::LayersBackend aLayersBackend,
                      mozilla::layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   // Decode thread.
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   static void Init();
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        DecoderDoctorDiagnostics* aDiagnostics) const override;
 
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/omx/OmxDecoderModule.cpp
+++ b/dom/media/platforms/omx/OmxDecoderModule.cpp
@@ -11,26 +11,28 @@
 
 namespace mozilla {
 
 already_AddRefed<MediaDataDecoder>
 OmxDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      mozilla::layers::LayersBackend aLayersBackend,
                                      mozilla::layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback, aImageContainer);
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 OmxDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                      FlushableTaskQueue* aAudioTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback, nullptr);
   return decoder.forget();
 }
 
 void
 OmxDecoderModule::Init()
 {
@@ -39,14 +41,15 @@ OmxDecoderModule::Init()
 
 PlatformDecoderModule::ConversionRequired
 OmxDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   return kNeedNone;
 }
 
 bool
-OmxDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+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
@@ -13,25 +13,28 @@ namespace mozilla {
 
 class OmxDecoderModule : public PlatformDecoderModule {
 public:
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      mozilla::layers::LayersBackend aLayersBackend,
                      mozilla::layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   static void Init();
 
-  bool SupportsMimeType(const nsACString& aMimeType) const 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
@@ -97,17 +97,18 @@ WMFDecoderModule::Startup()
   return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE;
 }
 
 already_AddRefed<MediaDataDecoder>
 WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      layers::LayersBackend aLayersBackend,
                                      layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   nsAutoPtr<WMFVideoMFTManager> manager(
     new WMFVideoMFTManager(aConfig,
                            aLayersBackend,
                            aImageContainer,
                            sDXVAEnabled));
 
   if (!manager->Init()) {
@@ -118,17 +119,18 @@ WMFDecoderModule::CreateVideoDecoder(con
     new WMFMediaDataDecoder(manager.forget(), aVideoTaskQueue, aCallback);
 
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                      FlushableTaskQueue* aAudioTaskQueue,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
   nsAutoPtr<WMFAudioMFTManager> manager(new WMFAudioMFTManager(aConfig));
 
   if (!manager->Init()) {
     return nullptr;
   }
 
   RefPtr<MediaDataDecoder> decoder =
@@ -208,17 +210,18 @@ WMFDecoderModule::HasH264()
 
 /* static */ bool
 WMFDecoderModule::HasAAC()
 {
   return CanCreateWMFDecoder<CLSID_CMSAACDecMFT>();
 }
 
 bool
-WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+                                   DecoderDoctorDiagnostics* aDiagnostics) const
 {
   if ((aMimeType.EqualsLiteral("audio/mp4a-latm") ||
        aMimeType.EqualsLiteral("audio/mp4")) &&
        WMFDecoderModule::HasAAC()) {
     return true;
   }
   if ((aMimeType.EqualsLiteral("video/avc") ||
        aMimeType.EqualsLiteral("video/mp4")) &&
--- a/dom/media/platforms/wmf/WMFDecoderModule.h
+++ b/dom/media/platforms/wmf/WMFDecoderModule.h
@@ -19,24 +19,27 @@ public:
   // Initializes the module, loads required dynamic libraries, etc.
   nsresult Startup() override;
 
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
-                     MediaDataDecoderCallback* aCallback) override;
+                     MediaDataDecoderCallback* aCallback,
+                     DecoderDoctorDiagnostics* aDiagnostics) override;
 
-  bool SupportsMimeType(const nsACString& aMimeType) const override;
+  bool SupportsMimeType(const nsACString& aMimeType,
+                        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
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -15,29 +15,30 @@
 namespace mozilla
 {
 
 H264Converter::H264Converter(PlatformDecoderModule* aPDM,
                              const VideoInfo& aConfig,
                              layers::LayersBackend aLayersBackend,
                              layers::ImageContainer* aImageContainer,
                              FlushableTaskQueue* aVideoTaskQueue,
-                             MediaDataDecoderCallback* aCallback)
+                             MediaDataDecoderCallback* aCallback,
+                             DecoderDoctorDiagnostics* aDiagnostics)
   : mPDM(aPDM)
   , mOriginalConfig(aConfig)
   , mCurrentConfig(aConfig)
   , mLayersBackend(aLayersBackend)
   , mImageContainer(aImageContainer)
   , mVideoTaskQueue(aVideoTaskQueue)
   , mCallback(aCallback)
   , mDecoder(nullptr)
   , mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
   , mLastError(NS_OK)
 {
-  CreateDecoder();
+  CreateDecoder(aDiagnostics);
 }
 
 H264Converter::~H264Converter()
 {
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 H264Converter::Init()
@@ -127,34 +128,35 @@ H264Converter::IsHardwareAccelerated(nsA
 {
   if (mDecoder) {
     return mDecoder->IsHardwareAccelerated(aFailureReason);
   }
   return MediaDataDecoder::IsHardwareAccelerated(aFailureReason);
 }
 
 nsresult
-H264Converter::CreateDecoder()
+H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (mNeedAVCC && !mp4_demuxer::AnnexB::HasSPS(mCurrentConfig.mExtraData)) {
     // nothing found yet, will try again later
     return NS_ERROR_NOT_INITIALIZED;
   }
   UpdateConfigFromExtraData(mCurrentConfig.mExtraData);
   if (!mNeedAVCC) {
     // When using a decoder handling AnnexB, we get here only once from the
     // constructor. We do want to get the dimensions extracted from the SPS.
     mOriginalConfig = mCurrentConfig;
   }
 
   mDecoder = mPDM->CreateVideoDecoder(mNeedAVCC ? mCurrentConfig : mOriginalConfig,
                                       mLayersBackend,
                                       mImageContainer,
                                       mVideoTaskQueue,
-                                      mCallback);
+                                      mCallback,
+                                      aDiagnostics);
   if (!mDecoder) {
     mLastError = NS_ERROR_FAILURE;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
@@ -162,17 +164,17 @@ H264Converter::CreateDecoderAndInit(Medi
 {
   RefPtr<MediaByteBuffer> extra_data =
     mp4_demuxer::AnnexB::ExtractExtraData(aSample);
   if (!mp4_demuxer::AnnexB::HasSPS(extra_data)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   UpdateConfigFromExtraData(extra_data);
 
-  nsresult rv = CreateDecoder();
+  nsresult rv = CreateDecoder(/* DecoderDoctorDiagnostics* */ nullptr);
 
   if (NS_SUCCEEDED(rv)) {
     // Queue the incoming sample.
     mMediaRawSamples.AppendElement(aSample);
 
     RefPtr<H264Converter> self = this;
 
     mInitPromiseRequest.Begin(mDecoder->Init()
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -21,17 +21,18 @@ namespace mozilla {
 class H264Converter : public MediaDataDecoder {
 public:
 
   H264Converter(PlatformDecoderModule* aPDM,
                 const VideoInfo& aConfig,
                 layers::LayersBackend aLayersBackend,
                 layers::ImageContainer* aImageContainer,
                 FlushableTaskQueue* aVideoTaskQueue,
-                MediaDataDecoderCallback* aCallback);
+                MediaDataDecoderCallback* aCallback,
+                DecoderDoctorDiagnostics* aDiagnostics);
   virtual ~H264Converter();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
@@ -46,17 +47,17 @@ public:
   // Return true if mimetype is H.264.
   static bool IsH264(const TrackInfo& aConfig);
   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();
+  nsresult CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics);
   nsresult CreateDecoderAndInit(MediaRawData* aSample);
   nsresult CheckForSPSChange(MediaRawData* aSample);
   void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData);
 
   void OnDecoderInitDone(const TrackType aTrackType);
   void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
 
   RefPtr<PlatformDecoderModule> mPDM;
--- a/dom/media/wave/WaveDecoder.cpp
+++ b/dom/media/wave/WaveDecoder.cpp
@@ -37,17 +37,18 @@ bool
 WaveDecoder::IsEnabled()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!Preferences::GetBool("media.wave.decoder.enabled", false)) {
     return false;
   }
   PDMFactory::Init();
   RefPtr<PDMFactory> platform = new PDMFactory();
-  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"));
+  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"),
+                                    /* DecoderDoctorDiagnostics* */ nullptr);
 }
 
 /* static */
 bool
 WaveDecoder::CanHandleMediaType(const nsACString& aType,
                                const nsAString& aCodecs)
 {
   if (aType.EqualsASCII("audio/wave") || aType.EqualsASCII("audio/x-wav") ||
--- a/layout/build/nsContentDLF.cpp
+++ b/layout/build/nsContentDLF.cpp
@@ -207,17 +207,18 @@ nsContentDLF::CreateInstance(const char*
     if (!MayUseXULXBL(aChannel)) {
       return NS_ERROR_REMOTE_XUL;
     }
 
     return CreateXULDocument(aCommand, aChannel, aLoadGroup, aContainer,
                              aExtraInfo, aDocListener, aDocViewer);
   }
 
-  if (mozilla::DecoderTraits::ShouldHandleMediaType(contentType.get())) {
+  if (mozilla::DecoderTraits::ShouldHandleMediaType(contentType.get(),
+                    /* DecoderDoctorDiagnostics* */ nullptr)) {
     return CreateDocument(aCommand,
                           aChannel, aLoadGroup,
                           aContainer, kVideoDocumentCID,
                           aDocListener, aDocViewer);
   }
 
   // Try image types
   if (IsImageContentType(contentType.get())) {