diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/platforms/wmf/WMFDecoderModule.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/platforms/wmf/WMFDecoderModule.cpp')
-rw-r--r-- | dom/media/platforms/wmf/WMFDecoderModule.cpp | 454 |
1 files changed, 454 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..5366071840 --- /dev/null +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -0,0 +1,454 @@ +/* -*- 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 "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/SyncRunnable.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" + +#ifdef MOZ_AV1 +# include "AOMDecoder.h" +#endif + +#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) + +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 const GUID CLSID_CMSAACDecMFT = { + 0x32D186A7, + 0x218F, + 0x4C75, + {0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99}}; + +static Atomic<bool> sDXVAEnabled(false); + +/* static */ +already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() { + RefPtr<WMFDecoderModule> wmf = new WMFDecoderModule(); + return wmf.forget(); +} + +static bool IsRemoteAcceleratedCompositor( + layers::KnowsCompositor* aKnowsCompositor) { + if (!aKnowsCompositor) { + return false; + } + + if (aKnowsCompositor->UsingSoftwareWebRenderD3D11()) { + return true; + } + + layers::TextureFactoryIdentifier ident = + aKnowsCompositor->GetTextureFactoryIdentifier(); + return !aKnowsCompositor->UsingSoftwareWebRender() && + ident.mParentProcessType == GeckoProcessType_GPU; +} + +static Atomic<bool> sSupportedTypesInitialized(false); +static EnumSet<WMFStreamType> sSupportedTypes; + +/* 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()) { + // Hardware accelerated decoding is explicitly only done in the GPU process + // to avoid copying textures whenever possible. Previously, detecting + // whether the video bridge was set up could be done with the following: + // sDXVAEnabled = !!DeviceManagerDx::Get()->GetImageDevice(); + // The video bridge was previously broken due to initialization order + // issues. For more information see Bug 1763880. + sDXVAEnabled = false; + } 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). + bool hwVideo = gfx::gfxVars::GetCanUseHardwareVideoDecodingOrDefault(); + WmfDecoderModuleMarkerAndLog( + "WMFInit DXVA Status", + "sDXVAEnabled: %s, CanUseHardwareVideoDecoding: %s", + sDXVAEnabled ? "true" : "false", hwVideo ? "true" : "false"); + sDXVAEnabled = sDXVAEnabled && hwVideo; + + mozilla::mscom::EnsureMTA([&]() { + // Store the supported MFT decoders. + sSupportedTypes.clear(); + // i = 1 to skip Unknown. + for (uint32_t i = 1; i < static_cast<uint32_t>(WMFStreamType::SENTINEL); + i++) { + WMFStreamType type = static_cast<WMFStreamType>(i); + RefPtr<MFTDecoder> decoder = new MFTDecoder(); + HRESULT hr = CreateMFTDecoder(type, decoder); + if (SUCCEEDED(hr)) { + sSupportedTypes += type; + WmfDecoderModuleMarkerAndLog("WMFInit Decoder Supported", + "%s is enabled", StreamTypeToString(type)); + } else if (hr != E_FAIL) { + // E_FAIL should be logged by CreateMFTDecoder. Skipping those codes + // will help to keep the logs readable. + WmfDecoderModuleMarkerAndLog("WMFInit Decoder Failed", + "%s failed with code 0x%lx", + StreamTypeToString(type), hr); + } + } + }); + + sSupportedTypesInitialized = true; + + WmfDecoderModuleMarkerAndLog("WMFInit Result", + "WMFDecoderModule::Init finishing"); +} + +/* 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); +} + +/* static */ +HRESULT WMFDecoderModule::CreateMFTDecoder(const WMFStreamType& aType, + RefPtr<MFTDecoder>& aDecoder) { + // Do not expose any video decoder on utility process which is only for audio + // decoding. + if (XRE_IsUtilityProcess()) { + switch (aType) { + case WMFStreamType::H264: + case WMFStreamType::VP8: + case WMFStreamType::VP9: + case WMFStreamType::AV1: + return E_FAIL; + default: + break; + } + } + + switch (aType) { + case WMFStreamType::H264: + return aDecoder->Create(CLSID_CMSH264DecoderMFT); + case WMFStreamType::VP8: + static const uint32_t VP8_USABLE_BUILD = 16287; + if (!IsWindowsBuildOrLater(VP8_USABLE_BUILD)) { + WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VP8 Failure", + "VP8 MFT requires Windows build %" PRId32 + " or later", + VP8_USABLE_BUILD); + return E_FAIL; + } + if (!gfx::gfxVars::UseVP8HwDecode()) { + WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VP8 Failure", + "Gfx VP8 blocklist"); + return E_FAIL; + } + [[fallthrough]]; + case WMFStreamType::VP9: + if (!sDXVAEnabled) { + WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VPx Disabled", + "%s MFT requires DXVA", + StreamTypeToString(aType)); + return E_FAIL; + } + + { + gfx::WMFVPXVideoCrashGuard guard; + if (guard.Crashed()) { + WmfDecoderModuleMarkerAndLog( + "CreateMFTDecoder, VPx Failure", + "Will not use VPx MFT due to crash guard reporting a crash"); + return E_FAIL; + } + return aDecoder->Create(CLSID_CMSVPXDecMFT); + } +#ifdef MOZ_AV1 + case WMFStreamType::AV1: + // If this process cannot use DXVA, the AV1 decoder will not be used. + // Also, upon startup, init will be called both before and after + // layers acceleration is setup. This prevents creating the AV1 decoder + // twice. + if (!sDXVAEnabled) { + WmfDecoderModuleMarkerAndLog("CreateMFTDecoder AV1 Disabled", + "AV1 MFT requires DXVA"); + return E_FAIL; + } + // TODO: MFTEnumEx is slower than creating by CLSID, it may be worth + // investigating other ways to instantiate the AV1 decoder. + return aDecoder->Create(MFT_CATEGORY_VIDEO_DECODER, MFVideoFormat_AV1, + MFVideoFormat_NV12); +#endif + case WMFStreamType::MP3: + return aDecoder->Create(CLSID_CMP3DecMediaObject); + case WMFStreamType::AAC: + return aDecoder->Create(CLSID_CMSAACDecMFT); + default: + return E_FAIL; + } +} + +/* static */ +bool WMFDecoderModule::CanCreateMFTDecoder(const WMFStreamType& aType) { + MOZ_ASSERT(WMFStreamType::Unknown < aType && aType < WMFStreamType::SENTINEL); + if (!sSupportedTypesInitialized) { + if (NS_IsMainThread()) { + Init(); + } else { + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableFunction("WMFDecoderModule::Init", [&]() { Init(); }); + SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), + runnable); + } + } + + // Check prefs here rather than CreateMFTDecoder so that prefs aren't baked + // into sSupportedTypes + switch (aType) { + case WMFStreamType::VP8: + case WMFStreamType::VP9: + if (!StaticPrefs::media_wmf_vp9_enabled()) { + return false; + } + break; +#ifdef MOZ_AV1 + case WMFStreamType::AV1: + if (!StaticPrefs::media_av1_enabled() || + !StaticPrefs::media_wmf_av1_enabled()) { + return false; + } + break; +#endif + case WMFStreamType::MP3: + // Prefer ffvpx mp3 decoder over WMF. + if (StaticPrefs::media_ffvpx_mp3_enabled()) { + return false; + } + break; + default: + break; + } + + // Do not expose any video decoder on utility process which is only for audio + // decoding. + if (XRE_IsUtilityProcess()) { + switch (aType) { + case WMFStreamType::H264: + case WMFStreamType::VP8: + case WMFStreamType::VP9: + case WMFStreamType::AV1: + return false; + default: + break; + } + } + + return sSupportedTypes.contains(aType); +} + +bool WMFDecoderModule::SupportsColorDepth( + gfx::ColorDepth aColorDepth, DecoderDoctorDiagnostics* aDiagnostics) const { + // Color depth support can be determined by creating DX decoders. + return true; +} + +media::DecodeSupportSet WMFDecoderModule::Supports( + const SupportDecoderParams& aParams, + DecoderDoctorDiagnostics* aDiagnostics) const { + // This should only be supported by MFMediaEngineDecoderModule. + if (aParams.mMediaEngineId) { + return media::DecodeSupport::Unsupported; + } + // 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 media::DecodeSupport::Unsupported; + } + + 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 media::DecodeSupport::Unsupported; + } + + WMFStreamType type = GetStreamTypeFromMimeType(aParams.MimeType()); + if (type == WMFStreamType::Unknown) { + return media::DecodeSupport::Unsupported; + } + + if (CanCreateMFTDecoder(type)) { + if (StreamTypeIsVideo(type)) { + return sDXVAEnabled ? media::DecodeSupport::HardwareDecode + : media::DecodeSupport::SoftwareDecode; + } else { + // Audio only supports software decode + return media::DecodeSupport::SoftwareDecode; + } + } + + return media::DecodeSupport::Unsupported; +} + +nsresult WMFDecoderModule::Startup() { + return wmf::MediaFoundationInitializer::HasInitialized() ? 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, + aParams.mTrackingId)); + + 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; + } + + nsAutoCString hwFailure; + if (!manager->IsHardwareAccelerated(hwFailure)) { + // The decoder description includes whether it is using software or + // hardware, but no information about how the hardware acceleration failed. + WmfDecoderModuleMarkerAndLog( + "WMFVDecoderCreation Success", + "WMFDecoderModule::CreateVideoDecoder success for manager with " + "description %s - DXVA failure: %s", + manager->GetDescriptionName().get(), hwFailure.get()); + } else { + 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(); +} + +media::DecodeSupportSet WMFDecoderModule::SupportsMimeType( + const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { + UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType); + if (!trackInfo) { + return media::DecodeSupport::Unsupported; + } + auto supports = Supports(SupportDecoderParams(*trackInfo), aDiagnostics); + MOZ_LOG( + sPDMLog, LogLevel::Debug, + ("WMF decoder %s requested type '%s'", + supports != media::DecodeSupport::Unsupported ? "supports" : "rejects", + aMimeType.BeginReading())); + return supports; +} + +} // namespace mozilla + +#undef WFM_DECODER_MODULE_STATUS_MARKER +#undef LOG |