Bug 1248507 - p10. Detect and report when FFMpeg/Linux fails to load - r=jya
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 19 Apr 2016 17:36:20 +1000
changeset 331702 a4c2e710a68f5c1f148fb2753b29bf4be8bb908f
parent 331701 5c2ed3f337d4a60f2cf0306cce331ae64cfc6104
child 331703 ffb1d08f2bd00a9490f76c89c58325e32c199066
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 - p10. Detect and report when FFMpeg/Linux fails to load - r=jya If the FFmpeg decoder module cannot be started, the failure is recorded in the DecoderDoctorDiagnostics structure. In this case, on Linux if there are no suitable decoders for any requested format, a "platform decoder not found" notification is sent to Chrome (a separate bug will implement the actual front-end notification), and logged to the web console. Note: All front-end notifications (that could display a notification bar) are currently disabled by default. Set the following pref to true to enable them: "media.decoderdoctor.enable-notification-bar". MozReview-Commit-ID: CdaX7QUdWtd
dom/media/DecoderDoctorDiagnostics.cpp
dom/media/DecoderDoctorDiagnostics.h
dom/media/platforms/PDMFactory.cpp
dom/media/platforms/PDMFactory.h
--- a/dom/media/DecoderDoctorDiagnostics.cpp
+++ b/dom/media/DecoderDoctorDiagnostics.cpp
@@ -285,41 +285,64 @@ DecoderDoctorDocumentWatcher::ReportAnal
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                   NS_LITERAL_CSTRING("Media"),
                                   mDocument,
                                   nsContentUtils::eDOM_PROPERTIES,
                                   aReportStringId,
                                   params,
                                   ArrayLength(params));
 
-  DispatchNotification(mDocument->GetInnerWindow(), aNotificationType, aFormats);
+  // For now, disable all front-end notifications by default.
+  // TODO: Future bugs will use finer-grained filtering instead.
+  if (Preferences::GetBool("media.decoderdoctor.enable-notification-bar", false)) {
+    DispatchNotification(
+      mDocument->GetInnerWindow(), aNotificationType, aFormats);
+  }
 }
 
 void
 DecoderDoctorDocumentWatcher::SynthesizeAnalysis()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   bool canPlay = false;
+#if defined(MOZ_FFMPEG)
+  bool PlatformDecoderNeeded = false;
+#endif
   nsAutoString formats;
   for (auto& diag : mDiagnosticsSequence) {
     if (diag.mDecoderDoctorDiagnostics.CanPlay()) {
       canPlay = true;
     } else {
+#if defined(MOZ_FFMPEG)
+      if (diag.mDecoderDoctorDiagnostics.DidFFmpegFailToLoad()) {
+        PlatformDecoderNeeded = true;
+      }
+#endif
       if (!formats.IsEmpty()) {
         formats += NS_LITERAL_STRING(", ");
       }
       formats += diag.mFormat;
     }
   }
   if (!canPlay) {
-    DD_WARN("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Cannot play media, formats: %s",
-            this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
-    ReportAnalysis(dom::DecoderDoctorNotificationType::Cannot_play,
-                   "MediaCannotPlayNoDecoders", formats);
+#if defined(MOZ_FFMPEG)
+    if (PlatformDecoderNeeded) {
+      DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - formats: %s -> Cannot play media because platform decoder was not found",
+               this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
+      ReportAnalysis(dom::DecoderDoctorNotificationType::Platform_decoder_not_found,
+                     "MediaPlatformDecoderNotFound", formats);
+    } else
+#endif
+    {
+      DD_WARN("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Cannot play media, formats: %s",
+              this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
+      ReportAnalysis(dom::DecoderDoctorNotificationType::Cannot_play,
+                     "MediaCannotPlayNoDecoders", formats);
+    }
   } else if (!formats.IsEmpty()) {
     DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Can play media, but no decoders for some requested formats: %s",
             this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
     if (Preferences::GetBool("media.decoderdoctor.verbose", false)) {
       ReportAnalysis(
         dom::DecoderDoctorNotificationType::Can_play_but_some_missing_decoders,
         "MediaNoDecoders", formats);
     }
@@ -335,19 +358,19 @@ DecoderDoctorDocumentWatcher::AddDiagnos
                                             DecoderDoctorDiagnostics&& aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mDocument) {
     return;
   }
 
-  DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(format='%s', call site '%s', can play=%d)",
+  DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(format='%s', call site '%s', can play=%d, platform lib failed to load=%d)",
            this, mDocument, NS_ConvertUTF16toUTF8(aFormat).get(),
-           aCallSite, aDiagnostics.CanPlay());
+           aCallSite, aDiagnostics.CanPlay(), aDiagnostics.DidFFmpegFailToLoad());
   mDiagnosticsSequence.AppendElement(
     Diagnostics(Move(aDiagnostics), aFormat, aCallSite));
   EnsureTimerIsStarted();
 }
 
 NS_IMETHODIMP
 DecoderDoctorDocumentWatcher::Notify(nsITimer* timer)
 {
--- a/dom/media/DecoderDoctorDiagnostics.h
+++ b/dom/media/DecoderDoctorDiagnostics.h
@@ -39,16 +39,21 @@ public:
                         const nsAString& aFormat,
                         const char* aCallSite);
 
   // Methods to record diagnostic information:
 
   void SetCanPlay() { mCanPlay = true; }
   bool CanPlay() const { return mCanPlay; }
 
+  void SetFFmpegFailedToLoad() { mFFmpegFailedToLoad = true; }
+  bool DidFFmpegFailToLoad() const { return mFFmpegFailedToLoad; }
+
 private:
   // True if there is at least one decoder that can play the media.
   bool mCanPlay = false;
+
+  bool mFFmpegFailedToLoad = false;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -37,16 +37,18 @@
 
 #include "AgnosticDecoderModule.h"
 
 #ifdef MOZ_EME
 #include "EMEDecoderModule.h"
 #include "mozilla/CDMProxy.h"
 #endif
 
+#include "DecoderDoctorDiagnostics.h"
+
 namespace mozilla {
 
 extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
 extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
 
 bool PDMFactory::sUseBlankDecoder = false;
 #ifdef MOZ_GONK_MEDIACODEC
 bool PDMFactory::sGonkDecoderEnabled = false;
@@ -155,16 +157,24 @@ PDMFactory::CreateDecoder(const TrackInf
                                 aConfig,
                                 aTaskQueue,
                                 aCallback,
                                 aDiagnostics,
                                 aLayersBackend,
                                 aImageContainer);
   }
 
+  if (aDiagnostics) {
+    // If libraries failed to load, the following loop over mCurrentPDMs
+    // will not even try to use them. So we record failures now.
+    if (mFFmpegFailedToLoad) {
+      aDiagnostics->SetFFmpegFailedToLoad();
+    }
+  }
+
   for (auto& current : mCurrentPDMs) {
     if (!current->SupportsMimeType(aConfig.mMimeType, aDiagnostics)) {
       continue;
     }
     RefPtr<MediaDataDecoder> m =
       CreateDecoderWithPDM(current,
                            aConfig,
                            aTaskQueue,
@@ -287,17 +297,19 @@ PDMFactory::CreatePDMs()
   if (sFFVPXDecoderEnabled) {
     m = FFVPXRuntimeLinker::CreateDecoderModule();
     StartupPDM(m);
   }
 #endif
 #ifdef MOZ_FFMPEG
   if (sFFmpegDecoderEnabled) {
     m = FFmpegRuntimeLinker::CreateDecoderModule();
-    StartupPDM(m);
+    if (!StartupPDM(m)) {
+      mFFmpegFailedToLoad = true;
+    }
   }
 #endif
 #ifdef MOZ_APPLEMEDIA
   m = new AppleDecoderModule();
   StartupPDM(m);
 #endif
 #ifdef MOZ_GONK_MEDIACODEC
   if (sGonkDecoderEnabled) {
@@ -330,16 +342,24 @@ PDMFactory::StartupPDM(PlatformDecoderMo
   }
   return false;
 }
 
 already_AddRefed<PlatformDecoderModule>
 PDMFactory::GetDecoder(const nsACString& aMimeType,
                        DecoderDoctorDiagnostics* aDiagnostics) const
 {
+  if (aDiagnostics) {
+    // If libraries failed to load, the following loop over mCurrentPDMs
+    // will not even try to use them. So we record failures now.
+    if (mFFmpegFailedToLoad) {
+      aDiagnostics->SetFFmpegFailedToLoad();
+    }
+  }
+
   RefPtr<PlatformDecoderModule> pdm;
   for (auto& current : mCurrentPDMs) {
     if (current->SupportsMimeType(aMimeType, aDiagnostics)) {
       pdm = current;
       break;
     }
   }
   return pdm.forget();
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -89,13 +89,15 @@ private:
   static bool sWMFDecoderEnabled;
 #endif
   static bool sEnableFuzzingWrapper;
   static uint32_t sVideoOutputMinimumInterval_ms;
   static bool sDontDelayInputExhausted;
 
   nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
   RefPtr<PlatformDecoderModule> mEMEPDM;
+
+  bool mFFmpegFailedToLoad = false;
 };
 
 } // namespace mozilla
 
 #endif /* PDMFactory_h_ */