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
--- 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_ */