static void ReportToConsole(nsIDocument* aDocument, const char* aConsoleStringId, nsTArray<const char16_t*>& aParams) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aDocument); DD_DEBUG("DecoderDoctorDiagnostics.cpp:ReportToConsole(doc=%p) ReportToConsole" " - aMsg='%s' params={%s%s%s%s}", aDocument, aConsoleStringId, aParams.IsEmpty() ? "<no params>" : NS_ConvertUTF16toUTF8(aParams[0]).get(), (aParams.Length() < 1 || !aParams[1]) ? "" : ", ", (aParams.Length() < 1 || !aParams[1]) ? "" : NS_ConvertUTF16toUTF8(aParams[1]).get(), aParams.Length() < 2 ? "" : ", ..."); nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), aDocument, nsContentUtils::eDOM_PROPERTIES, aConsoleStringId, aParams.IsEmpty() ? nullptr : aParams.Elements(), aParams.Length()); }
NS_IMETHODIMP DecoderDoctorDocumentWatcher::Notify(nsITimer* timer) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(timer == mTimer); // Forget timer. (Assuming timer keeps itself and us alive during this call.) mTimer = nullptr; if (!mDocument) { return NS_OK; } if (mDiagnosticsSequence.Length() > mDiagnosticsHandled) { // We have new diagnostic data. mDiagnosticsHandled = mDiagnosticsSequence.Length(); SynthesizeAnalysis(); // Restart timer, to redo analysis or stop watching this document, // depending on whether anything new happens. EnsureTimerIsStarted(); } else { DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - No new diagnostics to analyze -> Stop watching", this, mDocument); // Stop watching this document, we don't expect more diagnostics for now. // If more diagnostics come in, we'll treat them as another burst, separately. // 'true' to remove the property from the document. StopWatching(true); } return NS_OK; }
static void DispatchNotification(nsISupports* aSubject, const NotificationAndReportStringId& aNotification, bool aIsSolved, const nsAString& aFormats) { if (!aSubject) { return; } dom::DecoderDoctorNotification data; data.mType = aNotification.mNotificationType; data.mIsSolved = aIsSolved; data.mDecoderDoctorReportId.Assign( NS_ConvertUTF8toUTF16(aNotification.mReportStringId)); if (!aFormats.IsEmpty()) { data.mFormats.Construct(aFormats); } nsAutoString json; data.ToJSON(json); if (json.IsEmpty()) { DD_WARN("DecoderDoctorDiagnostics/DispatchEvent() - Could not create json for dispatch"); // No point in dispatching this notification without data, the front-end // wouldn't know what to display. return; } DD_DEBUG("DecoderDoctorDiagnostics/DispatchEvent() %s", NS_ConvertUTF16toUTF8(json).get()); nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (obs) { obs->NotifyObservers(aSubject, "decoder-doctor-notification", json.get()); } }
DecoderDoctorDocumentWatcher::~DecoderDoctorDocumentWatcher() { MOZ_ASSERT(NS_IsMainThread()); DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p <- expect 0]::~DecoderDoctorDocumentWatcher()", this, mDocument); // mDocument should have been reset through StopWatching()! MOZ_ASSERT(!mDocument); }
DecoderDoctorDocumentWatcher::DecoderDoctorDocumentWatcher(nsIDocument* aDocument) : mDocument(aDocument) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mDocument); DD_DEBUG("DecoderDoctorDocumentWatcher[%p]::DecoderDoctorDocumentWatcher(doc=%p)", this, mDocument); }
void DecoderDoctorDocumentWatcher::RemovePropertyFromDocument() { MOZ_ASSERT(NS_IsMainThread()); DecoderDoctorDocumentWatcher* watcher = static_cast<DecoderDoctorDocumentWatcher*>( mDocument->GetProperty(nsGkAtoms::decoderDoctor)); if (!watcher) { return; } DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::RemovePropertyFromDocument()\n", watcher, watcher->mDocument); // This will remove the property and call our DestroyPropertyCallback. mDocument->DeleteProperty(nsGkAtoms::decoderDoctor); }
void DecoderDoctorDocumentWatcher::AddDiagnostics(DecoderDoctorDiagnostics&& aDiagnostics, const char* aCallSite) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aDiagnostics.Type() != DecoderDoctorDiagnostics::eEvent); if (!mDocument) { return; } DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(DecoderDoctorDiagnostics{%s}, call site '%s')", this, mDocument, aDiagnostics.GetDescription().Data(), aCallSite); mDiagnosticsSequence.AppendElement(Diagnostics(std::move(aDiagnostics), aCallSite)); EnsureTimerIsStarted(); }
void DecoderDoctorDocumentWatcher::ReportAnalysis( const NotificationAndReportStringId& aNotification, bool aIsSolved, const nsAString& aParams) { MOZ_ASSERT(NS_IsMainThread()); if (!mDocument) { return; } // Report non-solved issues to console. if (!aIsSolved) { // 'params' will only be forwarded for non-empty strings. const char16_t* params[1] = { aParams.Data() }; DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::ReportAnalysis() ReportToConsole - aMsg='%s' params[0]='%s'", this, mDocument, aNotification.mReportStringId, aParams.IsEmpty() ? "<no params>" : NS_ConvertUTF16toUTF8(params[0]).get()); nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), mDocument, nsContentUtils::eDOM_PROPERTIES, aNotification.mReportStringId, aParams.IsEmpty() ? nullptr : params, aParams.IsEmpty() ? 0 : 1); } // "media.decoder-doctor.notifications-allowed" controls which notifications // may be dispatched to the front-end. It either contains: // - '*' -> Allow everything. // - Comma-separater list of ids -> Allow if aReportStringId (from // dom.properties) is one of them. // - Nothing (missing or empty) -> Disable everything. nsAdoptingCString filter = Preferences::GetCString("media.decoder-doctor.notifications-allowed"); filter.StripWhitespace(); if (filter.EqualsLiteral("*") || StringListContains(filter, aNotification.mReportStringId)) { DispatchNotification( mDocument->GetInnerWindow(), aNotification, aIsSolved, aParams); } }
// Callback for property destructors. |aObject| is the object // the property is being removed for, |aPropertyName| is the property // being removed, |aPropertyValue| is the value of the property, and |aData| // is the opaque destructor data that was passed to SetProperty(). // static void DecoderDoctorDocumentWatcher::DestroyPropertyCallback(void* aObject, nsAtom* aPropertyName, void* aPropertyValue, void*) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPropertyName == nsGkAtoms::decoderDoctor); DecoderDoctorDocumentWatcher* watcher = static_cast<DecoderDoctorDocumentWatcher*>(aPropertyValue); MOZ_ASSERT(watcher); #ifdef DEBUG nsIDocument* document = static_cast<nsIDocument*>(aObject); MOZ_ASSERT(watcher->mDocument == document); #endif DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::DestroyPropertyCallback()\n", watcher, watcher->mDocument); // 'false': StopWatching should not try and remove the property. watcher->StopWatching(false); NS_RELEASE(watcher); }
void DecoderDoctorDocumentWatcher::SynthesizeAnalysis() { MOZ_ASSERT(NS_IsMainThread()); nsAutoString playableFormats; nsAutoString unplayableFormats; // Subsets of unplayableFormats that require a specific platform decoder: #if defined(XP_WIN) nsAutoString formatsRequiringWMF; #endif #if defined(MOZ_FFMPEG) nsAutoString formatsRequiringFFMpeg; #endif nsAutoString supportedKeySystems; nsAutoString unsupportedKeySystems; DecoderDoctorDiagnostics::KeySystemIssue lastKeySystemIssue = DecoderDoctorDiagnostics::eUnset; // Only deal with one decode error per document (the first one found). const MediaResult* firstDecodeError = nullptr; const nsString* firstDecodeErrorMediaSrc = nullptr; // Only deal with one decode warning per document (the first one found). const MediaResult* firstDecodeWarning = nullptr; const nsString* firstDecodeWarningMediaSrc = nullptr; for (const auto& diag : mDiagnosticsSequence) { switch (diag.mDecoderDoctorDiagnostics.Type()) { case DecoderDoctorDiagnostics::eFormatSupportCheck: if (diag.mDecoderDoctorDiagnostics.CanPlay()) { AppendToFormatsList(playableFormats, diag.mDecoderDoctorDiagnostics.Format()); } else { AppendToFormatsList(unplayableFormats, diag.mDecoderDoctorDiagnostics.Format()); #if defined(XP_WIN) if (diag.mDecoderDoctorDiagnostics.DidWMFFailToLoad()) { AppendToFormatsList(formatsRequiringWMF, diag.mDecoderDoctorDiagnostics.Format()); } #endif #if defined(MOZ_FFMPEG) if (diag.mDecoderDoctorDiagnostics.DidFFmpegFailToLoad()) { AppendToFormatsList(formatsRequiringFFMpeg, diag.mDecoderDoctorDiagnostics.Format()); } #endif } break; case DecoderDoctorDiagnostics::eMediaKeySystemAccessRequest: if (diag.mDecoderDoctorDiagnostics.IsKeySystemSupported()) { AppendToFormatsList(supportedKeySystems, diag.mDecoderDoctorDiagnostics.KeySystem()); } else { AppendToFormatsList(unsupportedKeySystems, diag.mDecoderDoctorDiagnostics.KeySystem()); DecoderDoctorDiagnostics::KeySystemIssue issue = diag.mDecoderDoctorDiagnostics.GetKeySystemIssue(); if (issue != DecoderDoctorDiagnostics::eUnset) { lastKeySystemIssue = issue; } } break; case DecoderDoctorDiagnostics::eEvent: MOZ_ASSERT_UNREACHABLE("Events shouldn't be stored for processing."); break; case DecoderDoctorDiagnostics::eDecodeError: if (!firstDecodeError) { firstDecodeError = &diag.mDecoderDoctorDiagnostics.DecodeIssue(); firstDecodeErrorMediaSrc = &diag.mDecoderDoctorDiagnostics.DecodeIssueMediaSrc(); } break; case DecoderDoctorDiagnostics::eDecodeWarning: if (!firstDecodeWarning) { firstDecodeWarning = &diag.mDecoderDoctorDiagnostics.DecodeIssue(); firstDecodeWarningMediaSrc = &diag.mDecoderDoctorDiagnostics.DecodeIssueMediaSrc(); } break; default: MOZ_ASSERT_UNREACHABLE("Unhandled DecoderDoctorDiagnostics type"); break; } } // Check if issues have been solved, by finding if some now-playable // key systems or formats were previously recorded as having issues. if (!supportedKeySystems.IsEmpty() || !playableFormats.IsEmpty()) { DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - supported key systems '%s', playable formats '%s'; See if they show issues have been solved...", this, mDocument, NS_ConvertUTF16toUTF8(supportedKeySystems).Data(), NS_ConvertUTF16toUTF8(playableFormats).get()); const nsAString* workingFormatsArray[] = { &supportedKeySystems, &playableFormats }; // For each type of notification, retrieve the pref that contains formats/ // key systems with issues. for (const NotificationAndReportStringId* id : sAllNotificationsAndReportStringIds) { nsAutoCString formatsPref("media.decoder-doctor."); formatsPref += id->mReportStringId; formatsPref += ".formats"; nsAutoString formatsWithIssues; Preferences::GetString(formatsPref.Data(), formatsWithIssues); if (formatsWithIssues.IsEmpty()) { continue; } // See if that list of formats-with-issues contains any formats that are // now playable/supported. bool solved = false; for (const nsAString* workingFormats : workingFormatsArray) { for (const auto& workingFormat : MakeStringListRange(*workingFormats)) { if (FormatsListContains(formatsWithIssues, workingFormat)) { // This now-working format used not to work -> Report solved issue. DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s solved ('%s' now works, it was in pref(%s)='%s')", this, mDocument, id->mReportStringId, NS_ConvertUTF16toUTF8(workingFormat).get(), formatsPref.Data(), NS_ConvertUTF16toUTF8(formatsWithIssues).get()); ReportAnalysis(mDocument, *id, true, workingFormat); // This particular Notification&ReportId has been solved, no need // to keep looking at other keysys/formats that might solve it too. solved = true; break; } } if (solved) { break; } } if (!solved) { DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s not solved (pref(%s)='%s')", this, mDocument, id->mReportStringId, formatsPref.Data(), NS_ConvertUTF16toUTF8(formatsWithIssues).get()); } } } // Look at Key System issues first, as they take precedence over format checks. if (!unsupportedKeySystems.IsEmpty() && supportedKeySystems.IsEmpty()) { // No supported key systems! switch (lastKeySystemIssue) { case DecoderDoctorDiagnostics::eWidevineWithNoWMF: DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unsupported key systems: %s, Widevine without WMF", this, mDocument, NS_ConvertUTF16toUTF8(unsupportedKeySystems).get()); ReportAnalysis(mDocument, sMediaWidevineNoWMF, false, unsupportedKeySystems); return; default: break; } } // Next, check playability of requested formats. if (!unplayableFormats.IsEmpty()) { // Some requested formats cannot be played. if (playableFormats.IsEmpty()) { // No requested formats can be played. See if we can help the user, by // going through expected decoders from most to least desirable. #if defined(XP_WIN) if (!formatsRequiringWMF.IsEmpty()) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because WMF was not found", this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringWMF).get()); ReportAnalysis(mDocument, sMediaWMFNeeded, false, formatsRequiringWMF); return; } #endif #if defined(MOZ_FFMPEG) if (!formatsRequiringFFMpeg.IsEmpty()) { switch (FFmpegRuntimeLinker::LinkStatusCode()) { case FFmpegRuntimeLinker::LinkStatus_INVALID_FFMPEG_CANDIDATE: case FFmpegRuntimeLinker::LinkStatus_UNUSABLE_LIBAV57: case FFmpegRuntimeLinker::LinkStatus_INVALID_LIBAV_CANDIDATE: case FFmpegRuntimeLinker::LinkStatus_OBSOLETE_FFMPEG: case FFmpegRuntimeLinker::LinkStatus_OBSOLETE_LIBAV: case FFmpegRuntimeLinker::LinkStatus_INVALID_CANDIDATE: DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because of unsupported %s (Reason: %s)", this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringFFMpeg).get(), FFmpegRuntimeLinker::LinkStatusLibraryName(), FFmpegRuntimeLinker::LinkStatusString()); ReportAnalysis(mDocument, sUnsupportedLibavcodec, false, formatsRequiringFFMpeg); return; case FFmpegRuntimeLinker::LinkStatus_INIT: MOZ_FALLTHROUGH_ASSERT("Unexpected LinkStatus_INIT"); case FFmpegRuntimeLinker::LinkStatus_SUCCEEDED: MOZ_FALLTHROUGH_ASSERT("Unexpected LinkStatus_SUCCEEDED"); case FFmpegRuntimeLinker::LinkStatus_NOT_FOUND: DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because platform decoder was not found (Reason: %s)", this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringFFMpeg).get(), FFmpegRuntimeLinker::LinkStatusString()); ReportAnalysis(mDocument, sMediaPlatformDecoderNotFound, false, formatsRequiringFFMpeg); return; } } #endif DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Cannot play media, unplayable formats: %s", this, mDocument, NS_ConvertUTF16toUTF8(unplayableFormats).get()); ReportAnalysis(mDocument, sMediaCannotPlayNoDecoders, false, unplayableFormats); return; } DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, but no decoders for some requested formats: %s", this, mDocument, NS_ConvertUTF16toUTF8(unplayableFormats).get()); if (Preferences::GetBool("media.decoder-doctor.verbose", false)) { ReportAnalysis(mDocument, sMediaNoDecoders, false, unplayableFormats); } return; } if (firstDecodeError) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Decode error: %s", this, mDocument, firstDecodeError->Description().get()); ReportAnalysis(mDocument, sMediaDecodeError, false, NS_LITERAL_STRING(""), *firstDecodeError, true, // aDecodeIssueIsError=true mDocument->GetDocumentURI()->GetSpecOrDefault(), *firstDecodeErrorMediaSrc); return; } if (firstDecodeWarning) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Decode warning: %s", this, mDocument, firstDecodeWarning->Description().get()); ReportAnalysis(mDocument, sMediaDecodeWarning, false, NS_LITERAL_STRING(""), *firstDecodeWarning, false, // aDecodeIssueIsError=false mDocument->GetDocumentURI()->GetSpecOrDefault(), *firstDecodeWarningMediaSrc); return; } DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, decoders available for all requested formats", this, mDocument); }
void DecoderDoctorDocumentWatcher::SynthesizeAnalysis() { MOZ_ASSERT(NS_IsMainThread()); nsAutoString playableFormats; nsAutoString unplayableFormats; // Subsets of unplayableFormats that require a specific platform decoder: #if defined(XP_WIN) nsAutoString formatsRequiringWMF; #endif #if defined(MOZ_FFMPEG) nsAutoString formatsRequiringFFMpeg; #endif nsAutoString supportedKeySystems; nsAutoString unsupportedKeySystems; DecoderDoctorDiagnostics::KeySystemIssue lastKeySystemIssue = DecoderDoctorDiagnostics::eUnset; for (const auto& diag : mDiagnosticsSequence) { switch (diag.mDecoderDoctorDiagnostics.Type()) { case DecoderDoctorDiagnostics::eFormatSupportCheck: if (diag.mDecoderDoctorDiagnostics.CanPlay()) { AppendToFormatsList(playableFormats, diag.mDecoderDoctorDiagnostics.Format()); } else { AppendToFormatsList(unplayableFormats, diag.mDecoderDoctorDiagnostics.Format()); #if defined(XP_WIN) if (diag.mDecoderDoctorDiagnostics.DidWMFFailToLoad()) { AppendToFormatsList(formatsRequiringWMF, diag.mDecoderDoctorDiagnostics.Format()); } #endif #if defined(MOZ_FFMPEG) if (diag.mDecoderDoctorDiagnostics.DidFFmpegFailToLoad()) { AppendToFormatsList(formatsRequiringFFMpeg, diag.mDecoderDoctorDiagnostics.Format()); } #endif } break; case DecoderDoctorDiagnostics::eMediaKeySystemAccessRequest: if (diag.mDecoderDoctorDiagnostics.IsKeySystemSupported()) { AppendToFormatsList(supportedKeySystems, diag.mDecoderDoctorDiagnostics.KeySystem()); } else { AppendToFormatsList(unsupportedKeySystems, diag.mDecoderDoctorDiagnostics.KeySystem()); DecoderDoctorDiagnostics::KeySystemIssue issue = diag.mDecoderDoctorDiagnostics.GetKeySystemIssue(); if (issue != DecoderDoctorDiagnostics::eUnset) { lastKeySystemIssue = issue; } } break; default: MOZ_ASSERT(diag.mDecoderDoctorDiagnostics.Type() == DecoderDoctorDiagnostics::eFormatSupportCheck || diag.mDecoderDoctorDiagnostics.Type() == DecoderDoctorDiagnostics::eMediaKeySystemAccessRequest); break; } } // Check if issues have been solved, by finding if some now-playable // key systems or formats were previously recorded as having issues. if (!supportedKeySystems.IsEmpty() || !playableFormats.IsEmpty()) { DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - supported key systems '%s', playable formats '%s'; See if they show issues have been solved...", this, mDocument, NS_ConvertUTF16toUTF8(supportedKeySystems).Data(), NS_ConvertUTF16toUTF8(playableFormats).get()); const nsAString* workingFormatsArray[] = { &supportedKeySystems, &playableFormats }; // For each type of notification, retrieve the pref that contains formats/ // key systems with issues. for (const NotificationAndReportStringId* id : sAllNotificationsAndReportStringIds) { nsAutoCString formatsPref("media.decoder-doctor."); formatsPref += id->mReportStringId; formatsPref += ".formats"; nsAdoptingString formatsWithIssues = Preferences::GetString(formatsPref.Data()); if (formatsWithIssues.IsEmpty()) { continue; } // See if that list of formats-with-issues contains any formats that are // now playable/supported. bool solved = false; for (const nsAString* workingFormats : workingFormatsArray) { for (const auto& workingFormat : MakeStringListRange(*workingFormats)) { if (FormatsListContains(formatsWithIssues, workingFormat)) { // This now-working format used not to work -> Report solved issue. DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s solved ('%s' now works, it was in pref(%s)='%s')", this, mDocument, id->mReportStringId, NS_ConvertUTF16toUTF8(workingFormat).get(), formatsPref.Data(), NS_ConvertUTF16toUTF8(formatsWithIssues).get()); ReportAnalysis(*id, true, workingFormat); // This particular Notification&ReportId has been solved, no need // to keep looking at other keysys/formats that might solve it too. solved = true; break; } } if (solved) { break; } } if (!solved) { DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - %s not solved (pref(%s)='%s')", this, mDocument, id->mReportStringId, formatsPref.Data(), NS_ConvertUTF16toUTF8(formatsWithIssues).get()); } } } // Look at Key System issues first, as they take precedence over format checks. if (!unsupportedKeySystems.IsEmpty() && supportedKeySystems.IsEmpty()) { // No supported key systems! switch (lastKeySystemIssue) { case DecoderDoctorDiagnostics::eWidevineWithNoWMF: if (CheckSilverlight() != eSilverlightEnabled) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unsupported key systems: %s, widevine without WMF nor Silverlight", this, mDocument, NS_ConvertUTF16toUTF8(unsupportedKeySystems).get()); ReportAnalysis( sMediaWidevineNoWMFNoSilverlight, false, unsupportedKeySystems); return; } break; default: break; } } // Next, check playability of requested formats. if (!unplayableFormats.IsEmpty()) { // Some requested formats cannot be played. if (playableFormats.IsEmpty()) { // No requested formats can be played. See if we can help the user, by // going through expected decoders from most to least desirable. #if defined(XP_WIN) if (!formatsRequiringWMF.IsEmpty()) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because WMF was not found", this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringWMF).get()); ReportAnalysis(sMediaWMFNeeded, false, formatsRequiringWMF); return; } #endif #if defined(MOZ_FFMPEG) if (!formatsRequiringFFMpeg.IsEmpty()) { DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because platform decoder was not found", this, mDocument, NS_ConvertUTF16toUTF8(formatsRequiringFFMpeg).get()); ReportAnalysis(sMediaPlatformDecoderNotFound, false, formatsRequiringFFMpeg); return; } #endif DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Cannot play media, unplayable formats: %s", this, mDocument, NS_ConvertUTF16toUTF8(unplayableFormats).get()); ReportAnalysis(sMediaCannotPlayNoDecoders, false, unplayableFormats); return; } DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, but no decoders for some requested formats: %s", this, mDocument, NS_ConvertUTF16toUTF8(unplayableFormats).get()); if (Preferences::GetBool("media.decoder-doctor.verbose", false)) { ReportAnalysis(sMediaNoDecoders, false, unplayableFormats); } return; } DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, decoders available for all requested formats", this, mDocument); }