diff options
Diffstat (limited to 'dom/media/platforms/wmf/WMFDecoderModule.cpp')
-rw-r--r-- | dom/media/platforms/wmf/WMFDecoderModule.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp new file mode 100644 index 0000000000..64e14675f9 --- /dev/null +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -0,0 +1,342 @@ +/* -*- 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 <algorithm> +#include <vector> + +#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<bool> sDXVAEnabled(false); +static Atomic<bool> sUsableVPXMFT(false); + +/* static */ +already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() { + return MakeAndAddRef<WMFDecoderModule>(); +} + +WMFDecoderModule::~WMFDecoderModule() { + if (mWMFInitialized) { + DebugOnly<HRESULT> hr = wmf::MFShutdown(); + NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed"); + } +} + +static bool IsRemoteAcceleratedCompositor( + layers::KnowsCompositor* aKnowsCompositor) { + if (!aKnowsCompositor) { + return false; + } + + TextureFactoryIdentifier ident = + aKnowsCompositor->GetTextureFactoryIdentifier(); + return ident.mParentBackend != LayersBackend::LAYERS_BASIC && + !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<MFTDecoder> 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<MediaDataDecoder> 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<WMFVideoMFTManager> 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<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release()); + return decoder.forget(); +} + +already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateAudioDecoder( + const CreateDecoderParams& aParams) { + if (XRE_IsGPUProcess()) { + // Only allow video in the GPU process. + return nullptr; + } + + UniquePtr<WMFAudioMFTManager> 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<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release()); + return decoder.forget(); +} + +template <const GUID& aGuid> +static bool CanCreateWMFDecoder() { + static StaticMutex sMutex; + StaticMutexAutoLock lock(sMutex); + static Maybe<bool> result; + if (result.isNothing()) { + result.emplace(CanCreateMFTDecoder(aGuid)); + } + return result.value(); +} + +/* static */ +bool WMFDecoderModule::HasH264() { + return CanCreateWMFDecoder<CLSID_CMSH264DecoderMFT>(); +} + +/* static */ +bool WMFDecoderModule::HasVP8() { + return sUsableVPXMFT && CanCreateWMFDecoder<CLSID_WebmMfVpxDec>(); +} + +/* static */ +bool WMFDecoderModule::HasVP9() { + return sUsableVPXMFT && CanCreateWMFDecoder<CLSID_WebmMfVpxDec>(); +} + +/* static */ +bool WMFDecoderModule::HasAAC() { + return CanCreateWMFDecoder<CLSID_CMSAACDecMFT>(); +} + +/* static */ +bool WMFDecoderModule::HasMP3() { + return CanCreateWMFDecoder<CLSID_CMP3DecMediaObject>(); +} + +bool WMFDecoderModule::SupportsMimeType( + const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { + UniquePtr<TrackInfo> 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()) { + const auto audioInfo = trackInfo.GetAsAudioInfo(); + if (audioInfo && audioInfo->mRate > 0) { + // Supported sampling rates per: + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd742784(v=vs.85).aspx + const std::vector<uint32_t> frequencies = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + }; + return std::find(frequencies.begin(), frequencies.end(), + audioInfo->mRate) != frequencies.end(); + } + 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 |