summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/wmf/WMFDecoderModule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/wmf/WMFDecoderModule.cpp')
-rw-r--r--dom/media/platforms/wmf/WMFDecoderModule.cpp454
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