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); }