/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WMFDecoderModule.h" #include #include #include "DriverCrashGuard.h" #include "GfxDriverInfo.h" #include "MFTDecoder.h" #include "MP4Decoder.h" #include "MediaInfo.h" #include "PDMFactory.h" #include "VPXDecoder.h" #include "WMF.h" #include "WMFAudioMFTManager.h" #include "WMFMediaDataDecoder.h" #include "WMFVideoMFTManager.h" #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/WindowsVersion.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/mscom/EnsureMTA.h" #include "mozilla/ProfilerMarkers.h" #include "nsComponentManagerUtils.h" #include "nsIXULRuntime.h" #include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart #include "nsServiceManagerUtils.h" #include "nsWindowsHelpers.h" #include "prsystem.h" #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) extern const GUID CLSID_WebmMfVpxDec; namespace mozilla { // Helper function to add a profile marker and log at the same time. static void MOZ_FORMAT_PRINTF(2, 3) WmfDecoderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag, const char* aFormat, ...) { va_list ap; va_start(ap, aFormat); const nsVprintfCString markerString(aFormat, ap); va_end(ap); PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString); LOG("%s", markerString.get()); } static Atomic sDXVAEnabled(false); static Atomic sUsableVPXMFT(false); /* static */ already_AddRefed WMFDecoderModule::Create() { RefPtr wmf = new WMFDecoderModule(); return wmf.forget(); } WMFDecoderModule::~WMFDecoderModule() { if (mWMFInitialized) { DebugOnly hr = wmf::MFShutdown(); NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed"); } } static bool IsRemoteAcceleratedCompositor( layers::KnowsCompositor* aKnowsCompositor) { if (!aKnowsCompositor) { return false; } if (aKnowsCompositor->UsingSoftwareWebRenderD3D11()) { return true; } TextureFactoryIdentifier ident = aKnowsCompositor->GetTextureFactoryIdentifier(); return !aKnowsCompositor->UsingSoftwareWebRender() && ident.mParentProcessType == GeckoProcessType_GPU; } static bool CanCreateMFTDecoder(const GUID& aGuid) { // The IMFTransform interface used by MFTDecoder is documented to require to // run on an MTA thread. // https://msdn.microsoft.com/en-us/library/windows/desktop/ee892371(v=vs.85).aspx#components // Note: our normal SharedThreadPool task queues are initialized to MTA, but // the main thread (which calls in here from our CanPlayType implementation) // is not. bool canCreateDecoder = false; mozilla::mscom::EnsureMTA([&]() -> void { if (FAILED(wmf::MFStartup())) { return; } RefPtr decoder(new MFTDecoder()); canCreateDecoder = SUCCEEDED(decoder->Create(aGuid)); wmf::MFShutdown(); }); return canCreateDecoder; } /* static */ void WMFDecoderModule::Init() { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); if (XRE_IsContentProcess()) { // If we're in the content process and the UseGPUDecoder pref is set, it // means that we've given up on the GPU process (it's been crashing) so we // should disable DXVA sDXVAEnabled = !StaticPrefs::media_gpu_process_decoder(); } else if (XRE_IsGPUProcess()) { // Always allow DXVA in the GPU process. sDXVAEnabled = true; } else if (XRE_IsRDDProcess()) { // Only allows DXVA if we have an image device. We may have explicitly // disabled its creation following an earlier RDD process crash. sDXVAEnabled = !!DeviceManagerDx::Get()->GetImageDevice(); } else { // Only allow DXVA in the UI process if we aren't in e10s Firefox sDXVAEnabled = !mozilla::BrowserTabsRemoteAutostart(); } // We have heavy logging below to help diagnose issue around hardware decoding // failures. Due to these failures often relating to driver level problems // they're hard to nail down, so we want lots of info. We may be able to relax // this in future if we're not seeing such problems (see bug 1673007 for // references to the bugs motivating this). sDXVAEnabled = sDXVAEnabled && gfx::gfxVars::CanUseHardwareVideoDecoding(); bool testForVPx = gfx::gfxVars::CanUseHardwareVideoDecoding(); if (testForVPx && StaticPrefs::media_wmf_vp9_enabled_AtStartup()) { gfx::WMFVPXVideoCrashGuard guard; if (!guard.Crashed()) { WmfDecoderModuleMarkerAndLog("WMFInit VPx Pending", "Attempting to create MFT decoder for VPx"); sUsableVPXMFT = CanCreateMFTDecoder(CLSID_WebmMfVpxDec); WmfDecoderModuleMarkerAndLog("WMFInit VPx Initialized", "CanCreateMFTDecoder returned %s for VPx", sUsableVPXMFT ? "true" : "false"); } else { WmfDecoderModuleMarkerAndLog( "WMFInit VPx Failure", "Will not use MFT VPx due to crash guard reporting a crash"); } } WmfDecoderModuleMarkerAndLog( "WMFInit Result", "WMFDecoderModule::Init finishing with sDXVAEnabled=%s testForVPx=%s " "sUsableVPXMFT=%s", sDXVAEnabled ? "true" : "false", testForVPx ? "true" : "false", sUsableVPXMFT ? "true" : "false"); } /* static */ int WMFDecoderModule::GetNumDecoderThreads() { int32_t numCores = PR_GetNumberOfProcessors(); // If we have more than 4 cores, let the decoder decide how many threads. // On an 8 core machine, WMF chooses 4 decoder threads. static const int WMF_DECODER_DEFAULT = -1; if (numCores > 4) { return WMF_DECODER_DEFAULT; } return std::max(numCores - 1, 1); } nsresult WMFDecoderModule::Startup() { mWMFInitialized = SUCCEEDED(wmf::MFStartup()); return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE; } already_AddRefed WMFDecoderModule::CreateVideoDecoder( const CreateDecoderParams& aParams) { // In GPU process, only support decoding if an accelerated compositor is // known. if (XRE_IsGPUProcess() && !IsRemoteAcceleratedCompositor(aParams.mKnowsCompositor)) { return nullptr; } UniquePtr manager(new WMFVideoMFTManager( aParams.VideoConfig(), aParams.mKnowsCompositor, aParams.mImageContainer, aParams.mRate.mValue, aParams.mOptions, sDXVAEnabled)); MediaResult result = manager->Init(); if (NS_FAILED(result)) { if (aParams.mError) { *aParams.mError = result; } WmfDecoderModuleMarkerAndLog( "WMFVDecoderCreation Failure", "WMFDecoderModule::CreateVideoDecoder failed for manager with " "description %s with result: %s", manager->GetDescriptionName().get(), result.Description().get()); return nullptr; } WmfDecoderModuleMarkerAndLog( "WMFVDecoderCreation Success", "WMFDecoderModule::CreateVideoDecoder success for manager with " "description %s", manager->GetDescriptionName().get()); RefPtr decoder = new WMFMediaDataDecoder(manager.release()); return decoder.forget(); } already_AddRefed WMFDecoderModule::CreateAudioDecoder( const CreateDecoderParams& aParams) { if (XRE_IsGPUProcess()) { // Only allow video in the GPU process. return nullptr; } UniquePtr manager( new WMFAudioMFTManager(aParams.AudioConfig())); if (!manager->Init()) { WmfDecoderModuleMarkerAndLog( "WMFADecoderCreation Failure", "WMFDecoderModule::CreateAudioDecoder failed for manager with " "description %s", manager->GetDescriptionName().get()); return nullptr; } WmfDecoderModuleMarkerAndLog( "WMFADecoderCreation Success", "WMFDecoderModule::CreateAudioDecoder success for manager with " "description %s", manager->GetDescriptionName().get()); RefPtr decoder = new WMFMediaDataDecoder(manager.release()); return decoder.forget(); } template static bool CanCreateWMFDecoder() { static StaticMutex sMutex MOZ_UNANNOTATED; StaticMutexAutoLock lock(sMutex); static Maybe result; if (result.isNothing()) { result.emplace(CanCreateMFTDecoder(aGuid)); } return result.value(); } /* static */ bool WMFDecoderModule::HasH264() { return CanCreateWMFDecoder(); } /* static */ bool WMFDecoderModule::HasVP8() { return sUsableVPXMFT && CanCreateWMFDecoder(); } /* static */ bool WMFDecoderModule::HasVP9() { return sUsableVPXMFT && CanCreateWMFDecoder(); } /* static */ bool WMFDecoderModule::HasAAC() { return CanCreateWMFDecoder(); } /* static */ bool WMFDecoderModule::HasMP3() { return CanCreateWMFDecoder(); } bool WMFDecoderModule::SupportsMimeType( const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { UniquePtr trackInfo = CreateTrackInfoWithMIMEType(aMimeType); if (!trackInfo) { return false; } return Supports(SupportDecoderParams(*trackInfo), aDiagnostics); } bool WMFDecoderModule::Supports(const SupportDecoderParams& aParams, DecoderDoctorDiagnostics* aDiagnostics) const { // In GPU process, only support decoding if video. This only gives a hint of // what the GPU decoder *may* support. The actual check will occur in // CreateVideoDecoder. const auto& trackInfo = aParams.mConfig; if (XRE_IsGPUProcess() && !trackInfo.GetAsVideoInfo()) { return false; } const auto* videoInfo = trackInfo.GetAsVideoInfo(); // Temporary - forces use of VPXDecoder when alpha is present. // Bug 1263836 will handle alpha scenario once implemented. It will shift // the check for alpha to PDMFactory but not itself remove the need for a // check. if (videoInfo && (!SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics) || videoInfo->HasAlpha())) { return false; } if ((trackInfo.mMimeType.EqualsLiteral("audio/mp4a-latm") || trackInfo.mMimeType.EqualsLiteral("audio/mp4")) && WMFDecoderModule::HasAAC()) { return true; } if (MP4Decoder::IsH264(trackInfo.mMimeType) && WMFDecoderModule::HasH264()) { return true; } if (trackInfo.mMimeType.EqualsLiteral("audio/mpeg") && !StaticPrefs::media_ffvpx_mp3_enabled() && WMFDecoderModule::HasMP3()) { return true; } static const uint32_t VP8_USABLE_BUILD = 16287; if (VPXDecoder::IsVP8(trackInfo.mMimeType) && IsWindowsBuildOrLater(VP8_USABLE_BUILD) && WMFDecoderModule::HasVP8()) { return true; } if (VPXDecoder::IsVP9(trackInfo.mMimeType) && WMFDecoderModule::HasVP9()) { return true; } // Some unsupported codec. return false; } } // namespace mozilla #undef WFM_DECODER_MODULE_STATUS_MARKER #undef LOG