tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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