/* -*- 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 "WMF.h" #include "WMFDecoderModule.h" #include "WMFVideoMFTManager.h" #include "WMFAudioMFTManager.h" #include "MFTDecoder.h" #include "mozilla/DebugOnly.h" #include "mozilla/Services.h" #include "WMFMediaDataDecoder.h" #include "nsAutoPtr.h" #include "nsIWindowsRegKey.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "nsIGfxInfo.h" #include "nsWindowsHelpers.h" #include "GfxDriverInfo.h" #include "mozilla/gfx/gfxVars.h" #include "MediaInfo.h" #include "MediaPrefs.h" #include "prsystem.h" #include "mozilla/Maybe.h" #include "mozilla/StaticMutex.h" #include "mozilla/WindowsVersion.h" #include "MP4Decoder.h" #include "VPXDecoder.h" namespace mozilla { static Atomic sDXVAEnabled(false); WMFDecoderModule::WMFDecoderModule() : mWMFInitialized(false) { } WMFDecoderModule::~WMFDecoderModule() { if (mWMFInitialized) { DebugOnly hr = wmf::MFShutdown(); NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed"); } } /* static */ void WMFDecoderModule::Init() { sDXVAEnabled = gfx::gfxVars::CanUseHardwareVideoDecoding(); } /* 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 const int WMF_DECODER_DEFAULT = -1; int32_t prefThreadCount = WMF_DECODER_DEFAULT; if (XRE_GetProcessType() != GeckoProcessType_GPU) { prefThreadCount = MediaPrefs::PDMWMFThreadCount(); } if (prefThreadCount != WMF_DECODER_DEFAULT) { return std::max(prefThreadCount, 1); } else 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) { nsAutoPtr manager( new WMFVideoMFTManager(aParams.VideoConfig(), aParams.mKnowsCompositor, aParams.mImageContainer, sDXVAEnabled)); if (!manager->Init()) { return nullptr; } RefPtr decoder = new WMFMediaDataDecoder(manager.forget(), aParams.mTaskQueue, aParams.mCallback); return decoder.forget(); } already_AddRefed WMFDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams) { nsAutoPtr manager(new WMFAudioMFTManager(aParams.AudioConfig())); if (!manager->Init()) { return nullptr; } RefPtr decoder = new WMFMediaDataDecoder(manager.forget(), aParams.mTaskQueue, aParams.mCallback); return decoder.forget(); } static bool CanCreateMFTDecoder(const GUID& aGuid) { if (FAILED(wmf::MFStartup())) { return false; } bool hasdecoder = false; { RefPtr decoder(new MFTDecoder()); hasdecoder = SUCCEEDED(decoder->Create(aGuid)); } wmf::MFShutdown(); return hasdecoder; } template static bool CanCreateWMFDecoder() { static StaticMutex sMutex; StaticMutexAutoLock lock(sMutex); static Maybe result; if (result.isNothing()) { result.emplace(CanCreateMFTDecoder(aGuid)); } return result.value(); } static bool IsH264DecoderBlacklisted() { #ifdef BLACKLIST_CRASHY_H264_DECODERS WCHAR systemPath[MAX_PATH + 1]; if (!ConstructSystem32Path(L"msmpeg2vdec.dll", systemPath, MAX_PATH + 1)) { // Cannot build path -> Assume it's not the blacklisted DLL. return false; } DWORD zero; DWORD infoSize = GetFileVersionInfoSizeW(systemPath, &zero); if (infoSize == 0) { // Can't get file info -> Assume we don't have the blacklisted DLL. return false; } auto infoData = MakeUnique(infoSize); VS_FIXEDFILEINFO *vInfo; UINT vInfoLen; if (GetFileVersionInfoW(systemPath, 0, infoSize, infoData.get()) && VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)) { if ((vInfo->dwFileVersionMS == ((12u << 16) | 0u)) && ((vInfo->dwFileVersionLS == ((9200u << 16) | 16426u)) || (vInfo->dwFileVersionLS == ((9200u << 16) | 17037u)))) { // 12.0.9200.16426 & .17037 are blacklisted on Win64, see bug 1242343. return true; } } #endif // BLACKLIST_CRASHY_H264_DECODERS return false; } /* static */ bool WMFDecoderModule::HasH264() { if (IsH264DecoderBlacklisted()) { return false; } return CanCreateWMFDecoder(); } /* static */ bool WMFDecoderModule::HasAAC() { return CanCreateWMFDecoder(); } bool WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { UniquePtr trackInfo = CreateTrackInfoWithMIMEType(aMimeType); if (!trackInfo) { return false; } return Supports(*trackInfo, aDiagnostics); } bool WMFDecoderModule::Supports(const TrackInfo& aTrackInfo, DecoderDoctorDiagnostics* aDiagnostics) const { if ((aTrackInfo.mMimeType.EqualsLiteral("audio/mp4a-latm") || aTrackInfo.mMimeType.EqualsLiteral("audio/mp4")) && WMFDecoderModule::HasAAC()) { return true; } if (MP4Decoder::IsH264(aTrackInfo.mMimeType) && WMFDecoderModule::HasH264()) { const VideoInfo* videoInfo = aTrackInfo.GetAsVideoInfo(); MOZ_ASSERT(videoInfo); // Check Windows format constraints, based on: // https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx if (IsWin8OrLater()) { // Windows >7 supports at most 4096x2304. if (videoInfo->mImage.width > 4096 || videoInfo->mImage.height > 2304) { return false; } } else { // Windows <=7 supports at most 1920x1088. if (videoInfo->mImage.width > 1920 || videoInfo->mImage.height > 1088) { return false; } } return true; } if (aTrackInfo.mMimeType.EqualsLiteral("audio/mpeg") && CanCreateWMFDecoder()) { return true; } if (MediaPrefs::PDMWMFVP9DecoderEnabled() && sDXVAEnabled) { if ((VPXDecoder::IsVP8(aTrackInfo.mMimeType) || VPXDecoder::IsVP9(aTrackInfo.mMimeType)) && CanCreateWMFDecoder()) { return true; } } // Some unsupported codec. return false; } PlatformDecoderModule::ConversionRequired WMFDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const { if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) { return ConversionRequired::kNeedAnnexB; } else { return ConversionRequired::kNeedNone; } } } // namespace mozilla