WMFDecoderModule.cpp (17392B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WMFDecoderModule.h" 8 9 #include <algorithm> 10 11 #include "DriverCrashGuard.h" 12 #include "GfxDriverInfo.h" 13 #include "MFTDecoder.h" 14 #include "MP4Decoder.h" 15 #include "MediaInfo.h" 16 #include "PDMFactory.h" 17 #include "VPXDecoder.h" 18 #include "WMFAudioMFTManager.h" 19 #include "WMFMediaDataDecoder.h" 20 #include "WMFVideoMFTManager.h" 21 #include "mozilla/ProfilerMarkers.h" 22 #include "mozilla/StaticMutex.h" 23 #include "mozilla/StaticPrefs_media.h" 24 #include "mozilla/SyncRunnable.h" 25 #include "mozilla/WindowsVersion.h" 26 #include "mozilla/gfx/gfxVars.h" 27 #include "mozilla/mscom/EnsureMTA.h" 28 #include "nsComponentManagerUtils.h" 29 #include "nsIXULRuntime.h" 30 #include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart 31 #include "nsServiceManagerUtils.h" 32 #include "nsWindowsHelpers.h" 33 #include "prsystem.h" 34 35 #ifdef MOZ_AV1 36 # include "AOMDecoder.h" 37 #endif 38 39 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) 40 41 namespace mozilla { 42 43 // Helper function to add a profile marker and log at the same time. 44 static void MOZ_FORMAT_PRINTF(2, 3) 45 WmfDecoderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag, 46 const char* aFormat, ...) { 47 va_list ap; 48 va_start(ap, aFormat); 49 const nsVprintfCString markerString(aFormat, ap); 50 va_end(ap); 51 PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString); 52 LOG("%s", markerString.get()); 53 } 54 55 static const GUID CLSID_CMSAACDecMFT = { 56 0x32D186A7, 57 0x218F, 58 0x4C75, 59 {0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99}}; 60 61 static Atomic<bool> sDXVAEnabled(false); 62 63 /* static */ 64 already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() { 65 RefPtr<WMFDecoderModule> wmf = new WMFDecoderModule(); 66 return wmf.forget(); 67 } 68 69 static bool IsRemoteAcceleratedCompositor( 70 layers::KnowsCompositor* aKnowsCompositor) { 71 if (!aKnowsCompositor) { 72 return false; 73 } 74 75 if (aKnowsCompositor->UsingSoftwareWebRenderD3D11()) { 76 return true; 77 } 78 79 layers::TextureFactoryIdentifier ident = 80 aKnowsCompositor->GetTextureFactoryIdentifier(); 81 return !aKnowsCompositor->UsingSoftwareWebRender() && 82 ident.mParentProcessType == GeckoProcessType_GPU; 83 } 84 85 /* static */ 86 void WMFDecoderModule::Init() { 87 // TODO : add an assertion to prevent this from running on main thread. 88 if (XRE_IsContentProcess()) { 89 // If we're in the content process and the UseGPUDecoder pref is set, it 90 // means that we've given up on the GPU process (it's been crashing) so we 91 // should disable DXVA 92 sDXVAEnabled = !StaticPrefs::media_gpu_process_decoder(); 93 } else if (XRE_IsGPUProcess()) { 94 // Always allow DXVA in the GPU process. 95 sDXVAEnabled = true; 96 } else if (XRE_IsRDDProcess()) { 97 // Hardware accelerated decoding is explicitly only done in the GPU process 98 // to avoid copying textures whenever possible. Previously, detecting 99 // whether the video bridge was set up could be done with the following: 100 // sDXVAEnabled = !!DeviceManagerDx::Get()->GetImageDevice(); 101 // The video bridge was previously broken due to initialization order 102 // issues. For more information see Bug 1763880. 103 sDXVAEnabled = false; 104 } else { 105 // Only allow DXVA in the UI process if we aren't in e10s Firefox 106 sDXVAEnabled = !mozilla::BrowserTabsRemoteAutostart(); 107 } 108 109 // We have heavy logging below to help diagnose issue around hardware 110 // decoding failures. Due to these failures often relating to driver level 111 // problems they're hard to nail down, so we want lots of info. We may be 112 // able to relax this in future if we're not seeing such problems (see bug 113 // 1673007 for references to the bugs motivating this). 114 bool hwVideo = gfx::gfxVars::GetCanUseHardwareVideoDecodingOrDefault(); 115 WmfDecoderModuleMarkerAndLog( 116 "WMFInit DXVA Status", 117 "sDXVAEnabled: %s, CanUseHardwareVideoDecoding: %s", 118 sDXVAEnabled ? "true" : "false", hwVideo ? "true" : "false"); 119 sDXVAEnabled = sDXVAEnabled && hwVideo; 120 121 mozilla::mscom::EnsureMTA([&]() { 122 StaticMutexAutoLock lock(sMutex); 123 // Store the supported MFT decoders. 124 sSupportedTypes.clear(); 125 sLackOfExtensionTypes.clear(); 126 // i = 1 to skip Unknown. 127 for (uint32_t i = 1; i < static_cast<uint32_t>(WMFStreamType::SENTINEL); 128 i++) { 129 WMFStreamType type = static_cast<WMFStreamType>(i); 130 RefPtr<MFTDecoder> decoder = new MFTDecoder(); 131 HRESULT hr = CreateMFTDecoder(type, decoder); 132 if (SUCCEEDED(hr)) { 133 sSupportedTypes += type; 134 WmfDecoderModuleMarkerAndLog("WMFInit Decoder Supported", 135 "%s is enabled", EnumValueToString(type)); 136 } else if (hr != E_FAIL) { 137 // E_FAIL should be logged by CreateMFTDecoder. Skipping those codes 138 // will help to keep the logs readable. 139 WmfDecoderModuleMarkerAndLog("WMFInit Decoder Failed", 140 "%s failed with code 0x%lx", 141 EnumValueToString(type), hr); 142 if (hr == WINCODEC_ERR_COMPONENTNOTFOUND && 143 type == WMFStreamType::AV1) { 144 WmfDecoderModuleMarkerAndLog("No AV1 extension", 145 "Lacking of AV1 extension"); 146 sLackOfExtensionTypes += type; 147 } 148 } 149 } 150 }); 151 152 { 153 StaticMutexAutoLock lock(sMutex); 154 sSupportedTypesInitialized = true; 155 } 156 157 WmfDecoderModuleMarkerAndLog("WMFInit Result", 158 "WMFDecoderModule::Init finishing"); 159 } 160 161 /* static */ 162 int WMFDecoderModule::GetNumDecoderThreads() { 163 int32_t numCores = PR_GetNumberOfProcessors(); 164 165 // If we have more than 4 cores, let the decoder decide how many threads. 166 // On an 8 core machine, WMF chooses 4 decoder threads. 167 static const int WMF_DECODER_DEFAULT = -1; 168 if (numCores > 4) { 169 return WMF_DECODER_DEFAULT; 170 } 171 return std::max(numCores - 1, 1); 172 } 173 174 /* static */ 175 HRESULT WMFDecoderModule::CreateMFTDecoder(const WMFStreamType& aType, 176 RefPtr<MFTDecoder>& aDecoder) { 177 // Do not expose any video decoder on utility process which is only for audio 178 // decoding. 179 if (XRE_IsUtilityProcess()) { 180 switch (aType) { 181 case WMFStreamType::H264: 182 case WMFStreamType::VP8: 183 case WMFStreamType::VP9: 184 case WMFStreamType::AV1: 185 case WMFStreamType::HEVC: 186 return E_FAIL; 187 default: 188 break; 189 } 190 } 191 192 switch (aType) { 193 case WMFStreamType::H264: { 194 if (XRE_IsGPUProcess() && !sDXVAEnabled) { 195 WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, H264 Failure", 196 "SW decoder is not allowed in the GPU " 197 "process and the HW H264 requires DXVA"); 198 return E_FAIL; 199 } 200 return aDecoder->Create(CLSID_CMSH264DecoderMFT); 201 } 202 case WMFStreamType::VP8: 203 static const uint32_t VP8_USABLE_BUILD = 16287; 204 if (!IsWindows10BuildOrLater(VP8_USABLE_BUILD)) { 205 WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VP8 Failure", 206 "VP8 MFT requires Windows build %" PRId32 207 " or later", 208 VP8_USABLE_BUILD); 209 return E_FAIL; 210 } 211 if (!gfx::gfxVars::GetUseVP8HwDecodeOrDefault()) { 212 WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VP8 Failure", 213 "Gfx VP8 blocklist"); 214 return E_FAIL; 215 } 216 [[fallthrough]]; 217 case WMFStreamType::VP9: 218 if (!sDXVAEnabled) { 219 WmfDecoderModuleMarkerAndLog("CreateMFTDecoder, VPx Disabled", 220 "%s MFT requires DXVA", 221 EnumValueToString(aType)); 222 return E_FAIL; 223 } 224 225 { 226 gfx::WMFVPXVideoCrashGuard guard; 227 if (guard.Crashed()) { 228 WmfDecoderModuleMarkerAndLog( 229 "CreateMFTDecoder, VPx Failure", 230 "Will not use VPx MFT due to crash guard reporting a crash"); 231 return E_FAIL; 232 } 233 return aDecoder->Create(CLSID_CMSVPXDecMFT); 234 } 235 #ifdef MOZ_AV1 236 case WMFStreamType::AV1: 237 // If this process cannot use DXVA, the AV1 decoder will not be used. 238 // Also, upon startup, init will be called both before and after 239 // layers acceleration is setup. This prevents creating the AV1 decoder 240 // twice. 241 if (!sDXVAEnabled) { 242 WmfDecoderModuleMarkerAndLog("CreateMFTDecoder AV1 Disabled", 243 "AV1 MFT requires DXVA"); 244 return E_FAIL; 245 } 246 // TODO: MFTEnumEx is slower than creating by CLSID, it may be worth 247 // investigating other ways to instantiate the AV1 decoder. 248 return aDecoder->Create(MFT_CATEGORY_VIDEO_DECODER, MFVideoFormat_AV1, 249 MFVideoFormat_NV12); 250 #endif 251 case WMFStreamType::HEVC: 252 if (!StaticPrefs::media_hevc_enabled() || !sDXVAEnabled) { 253 return E_FAIL; 254 } 255 return aDecoder->Create(MFT_CATEGORY_VIDEO_DECODER, MFVideoFormat_HEVC, 256 MFVideoFormat_NV12); 257 case WMFStreamType::MP3: 258 return aDecoder->Create(CLSID_CMP3DecMediaObject); 259 case WMFStreamType::AAC: 260 return aDecoder->Create(CLSID_CMSAACDecMFT); 261 default: 262 return E_FAIL; 263 } 264 } 265 266 /* static */ 267 bool WMFDecoderModule::CanCreateMFTDecoder(const WMFStreamType& aType) { 268 MOZ_ASSERT(WMFStreamType::Unknown < aType && aType < WMFStreamType::SENTINEL); 269 bool hasInitialized = false; 270 { 271 StaticMutexAutoLock lock(sMutex); 272 hasInitialized = sSupportedTypesInitialized; 273 } 274 if (!hasInitialized) { 275 Init(); 276 } 277 278 // Check prefs here rather than CreateMFTDecoder so that prefs aren't baked 279 // into sSupportedTypes 280 switch (aType) { 281 case WMFStreamType::VP8: 282 case WMFStreamType::VP9: 283 if (!StaticPrefs::media_wmf_vp9_enabled()) { 284 return false; 285 } 286 break; 287 #ifdef MOZ_AV1 288 case WMFStreamType::AV1: 289 if (!StaticPrefs::media_av1_enabled() || 290 !StaticPrefs::media_wmf_av1_enabled()) { 291 return false; 292 } 293 break; 294 #endif 295 case WMFStreamType::HEVC: 296 if (!StaticPrefs::media_hevc_enabled()) { 297 return false; 298 } 299 break; 300 // Always use ffvpx for mp3 301 case WMFStreamType::MP3: 302 return false; 303 default: 304 break; 305 } 306 307 // Do not expose any video decoder on utility process which is only for audio 308 // decoding. 309 if (XRE_IsUtilityProcess()) { 310 switch (aType) { 311 case WMFStreamType::H264: 312 case WMFStreamType::VP8: 313 case WMFStreamType::VP9: 314 case WMFStreamType::AV1: 315 case WMFStreamType::HEVC: 316 return false; 317 default: 318 break; 319 } 320 } 321 StaticMutexAutoLock lock(sMutex); 322 return sSupportedTypes.contains(aType); 323 } 324 325 bool WMFDecoderModule::SupportsColorDepth( 326 gfx::ColorDepth aColorDepth, DecoderDoctorDiagnostics* aDiagnostics) const { 327 // Color depth support can be determined by creating DX decoders. 328 return true; 329 } 330 331 media::DecodeSupportSet WMFDecoderModule::Supports( 332 const SupportDecoderParams& aParams, 333 DecoderDoctorDiagnostics* aDiagnostics) const { 334 // This should only be supported by MFMediaEngineDecoderModule. 335 if (aParams.mMediaEngineId) { 336 return media::DecodeSupportSet{}; 337 } 338 // In GPU process, only support decoding if video. This only gives a hint of 339 // what the GPU decoder *may* support. The actual check will occur in 340 // CreateVideoDecoder. 341 const auto& trackInfo = aParams.mConfig; 342 if (XRE_IsGPUProcess() && !trackInfo.GetAsVideoInfo()) { 343 return media::DecodeSupportSet{}; 344 } 345 346 const auto* videoInfo = trackInfo.GetAsVideoInfo(); 347 // Temporary - forces use of VPXDecoder when alpha is present. 348 // Bug 1263836 will handle alpha scenario once implemented. It will shift 349 // the check for alpha to PDMFactory but not itself remove the need for a 350 // check. 351 if (videoInfo && (!SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics) || 352 videoInfo->HasAlpha())) { 353 return media::DecodeSupportSet{}; 354 } 355 356 if (videoInfo && VPXDecoder::IsVP9(aParams.MimeType()) && 357 aParams.mOptions.contains(CreateDecoderParams::Option::LowLatency)) { 358 // SVC layers are unsupported, and may be used in low latency use cases 359 // (WebRTC). 360 return media::DecodeSupportSet{}; 361 } 362 363 WMFStreamType type = GetStreamTypeFromMimeType(aParams.MimeType()); 364 if (type == WMFStreamType::Unknown) { 365 return media::DecodeSupportSet{}; 366 } 367 368 if (CanCreateMFTDecoder(type)) { 369 if (StreamTypeIsVideo(type)) { 370 return sDXVAEnabled ? media::DecodeSupport::HardwareDecode 371 : media::DecodeSupport::SoftwareDecode; 372 } else { 373 // Audio only supports software decode 374 return media::DecodeSupport::SoftwareDecode; 375 } 376 } 377 StaticMutexAutoLock lock(sMutex); 378 return sLackOfExtensionTypes.contains(type) 379 ? media::DecodeSupport::UnsureDueToLackOfExtension 380 : media::DecodeSupportSet{}; 381 } 382 383 nsresult WMFDecoderModule::Startup() { 384 return wmf::MediaFoundationInitializer::HasInitialized() ? NS_OK 385 : NS_ERROR_FAILURE; 386 } 387 388 already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateVideoDecoder( 389 const CreateDecoderParams& aParams) { 390 // In GPU process, only support decoding if an accelerated compositor is 391 // known. 392 if (XRE_IsGPUProcess() && 393 !IsRemoteAcceleratedCompositor(aParams.mKnowsCompositor)) { 394 return nullptr; 395 } 396 397 UniquePtr<WMFVideoMFTManager> manager(new WMFVideoMFTManager( 398 aParams.VideoConfig(), aParams.mKnowsCompositor, aParams.mImageContainer, 399 aParams.mRate.mValue, aParams.mOptions, sDXVAEnabled, 400 aParams.mTrackingId)); 401 402 MediaResult result = manager->Init(); 403 if (NS_FAILED(result)) { 404 if (aParams.mError) { 405 *aParams.mError = result; 406 } 407 WmfDecoderModuleMarkerAndLog( 408 "WMFVDecoderCreation Failure", 409 "WMFDecoderModule::CreateVideoDecoder failed for manager with " 410 "description %s with result: %s", 411 manager->GetDescriptionName().get(), result.Description().get()); 412 return nullptr; 413 } 414 415 nsAutoCString hwFailure; 416 bool isHardwareAccelerated = manager->IsHardwareAccelerated(hwFailure); 417 if (!isHardwareAccelerated) { 418 // The decoder description includes whether it is using software or 419 // hardware, but no information about how the hardware acceleration failed. 420 WmfDecoderModuleMarkerAndLog( 421 "WMFVDecoderCreation Success", 422 "WMFDecoderModule::CreateVideoDecoder success for manager with " 423 "description %s - DXVA failure: %s", 424 manager->GetDescriptionName().get(), hwFailure.get()); 425 } else { 426 WmfDecoderModuleMarkerAndLog( 427 "WMFVDecoderCreation Success", 428 "WMFDecoderModule::CreateVideoDecoder success for manager with " 429 "description %s", 430 manager->GetDescriptionName().get()); 431 } 432 433 // Ensure that if the GPU process claims to support hardware acceleration but 434 // fails to create a hardware decoder, we do not fall back to a software 435 // decoder. Software decoders are intended to run only in the RDD process. 436 if (XRE_IsGPUProcess() && !isHardwareAccelerated) { 437 WmfDecoderModuleMarkerAndLog("WMFVDecoderCreation Blocked", 438 "SW decoder is not allowed in the GPU " 439 "process"); 440 return nullptr; 441 } 442 443 RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release()); 444 return decoder.forget(); 445 } 446 447 already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateAudioDecoder( 448 const CreateDecoderParams& aParams) { 449 if (XRE_IsGPUProcess()) { 450 // Only allow video in the GPU process. 451 return nullptr; 452 } 453 454 UniquePtr<WMFAudioMFTManager> manager( 455 new WMFAudioMFTManager(aParams.AudioConfig())); 456 457 if (!manager->Init()) { 458 WmfDecoderModuleMarkerAndLog( 459 "WMFADecoderCreation Failure", 460 "WMFDecoderModule::CreateAudioDecoder failed for manager with " 461 "description %s", 462 manager->GetDescriptionName().get()); 463 return nullptr; 464 } 465 466 WmfDecoderModuleMarkerAndLog( 467 "WMFADecoderCreation Success", 468 "WMFDecoderModule::CreateAudioDecoder success for manager with " 469 "description %s", 470 manager->GetDescriptionName().get()); 471 472 RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release()); 473 return decoder.forget(); 474 } 475 476 media::DecodeSupportSet WMFDecoderModule::SupportsMimeType( 477 const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const { 478 UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType); 479 if (!trackInfo) { 480 return media::DecodeSupportSet{}; 481 } 482 auto supports = Supports(SupportDecoderParams(*trackInfo), aDiagnostics); 483 MOZ_LOG( 484 sPDMLog, LogLevel::Debug, 485 ("WMF decoder %s requested type '%s'", 486 !supports.isEmpty() ? "supports" : "rejects", aMimeType.BeginReading())); 487 return supports; 488 } 489 490 } // namespace mozilla 491 492 #undef WFM_DECODER_MODULE_STATUS_MARKER 493 #undef LOG