Bug 1248507 - p10. Detect and report when FFMpeg/Linux fails to load - r=jya draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 19 Apr 2016 11:13:21 +1000
changeset 353019 46dcad9b8292af34e7b68a03c214bdb9ed61b505
parent 353018 96e4eeaa4f72b659c67e616fe51d04593786f6ee
child 518803 33a77c027ef679826c8c53c1754c418c83cc53e2
push id15860
push usergsquelart@mozilla.com
push dateTue, 19 Apr 2016 04:00:08 +0000
reviewersjya
bugs1248507
milestone48.0a1
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_ */