chromium/media/base/win/media_foundation_package_locator_unittest.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include <mfapi.h>

#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/windows_version.h"
#include "media/base/test_helpers.h"
#include "media/base/win/media_foundation_package_locator_helper.h"
#include "media/base/win/mf_initializer.h"

namespace media {

namespace {

using VideoCodecMap = base::flat_map<VideoCodec, GUID>;
using AudioCodecMap = base::flat_map<AudioCodec, GUID>;

const VideoCodecMap& GetVideoCodecsMap() {
  static const base::NoDestructor<VideoCodecMap> AllVideoCodecsMap(
      {{VideoCodec::kVP9, MFVideoFormat_VP90},
       {VideoCodec::kHEVC, MFVideoFormat_HEVC},
       {VideoCodec::kAV1, MFVideoFormat_AV1}});
  return *AllVideoCodecsMap;
}

const AudioCodecMap& GetAudioCodecsMap() {
  static const base::NoDestructor<AudioCodecMap> AllAudioCodecsMap(
      {{AudioCodec::kEAC3, MFAudioFormat_Dolby_DDPlus},
       {AudioCodec::kAC4, MFAudioFormat_Dolby_AC4}});
  return *AllAudioCodecsMap;
}

template <typename TCodec, typename TType, typename TCategory, typename TFunc>
bool CanMfDecodeCodec(TCodec codec,
                      TType mft_type,
                      TCategory mft_category,
                      TFunc get_codecs) {
  auto codecs = get_codecs();
  MFT_REGISTER_TYPE_INFO input_type = {mft_type, codecs[codec]};
  base::win::ScopedCoMem<IMFActivate*> imf_activates;
  uint32_t count = 0;
  if (FAILED(MFTEnumEx(mft_category,
                       MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT |
                           MFT_ENUM_FLAG_HARDWARE,
                       &input_type, /*output_type=*/nullptr, &imf_activates,
                       &count))) {
    return false;
  }

  for (uint32_t i = 0; i < count; ++i) {
    imf_activates[i]->Release();
  }
  if (count == 0) {
    DLOG(INFO) << "No MFT for " << media::GetCodecName(codec);
    return false;
  }
  return true;
}

}  // namespace

class MediaFoundationPackageLocatorTest : public testing::Test {
 public:
  MediaFoundationPackageLocatorTest() = default;
  ~MediaFoundationPackageLocatorTest() override = default;

 protected:
  void SetUp() override {
    // We would like to use `MFTEnumEx()` in the test.
    ASSERT_TRUE(InitializeMediaFoundation());
  }

  void AddPackageFamilyName(const wchar_t* package_family_name) {
    media_foundation_package_family_names_.emplace_back(package_family_name);
  }

  std::vector<base::FilePath> GetMediaFoundationPackageInstallPaths(
      const std::wstring_view& decoder_lib_name,
      MediaFoundationCodecPackage codec_package) {
    return MediaFoundationPackageInstallPaths(
        media_foundation_package_family_names_, decoder_lib_name,
        codec_package);
  }

  void VerifyMfCodecPaths(std::vector<base::FilePath>& codec_paths) {
    ASSERT_FALSE(codec_paths.empty());
    // Verify the MF Codec Pack DLL module exists.
    bool mf_codec_dll_module_found = false;
    for (const auto& package_path : codec_paths) {
      DVLOG(2) << __func__ << ": package_path=" << package_path.value();
      if (base::PathExists(package_path)) {
        mf_codec_dll_module_found = true;
        break;
      }
    }
    ASSERT_TRUE(mf_codec_dll_module_found);
  }

  std::vector<std::wstring_view> media_foundation_package_family_names_;
};

TEST_F(MediaFoundationPackageLocatorTest, VP9) {
  AddPackageFamilyName(L"Microsoft.VP9VideoExtensions_8wekyb3d8bbwe");
  std::vector<base::FilePath> paths = GetMediaFoundationPackageInstallPaths(
      L"msvp9dec_store.dll", media::MediaFoundationCodecPackage::kVP9);

  if (CanMfDecodeCodec(VideoCodec::kVP9, MFMediaType_Video,
                       MFT_CATEGORY_VIDEO_DECODER, GetVideoCodecsMap)) {
    DVLOG(2) << __func__ << ": MF VP9 installed";
    VerifyMfCodecPaths(paths);
  } else {
    ASSERT_TRUE(paths.empty());
  }
}

TEST_F(MediaFoundationPackageLocatorTest, AV1) {
  AddPackageFamilyName(L"Microsoft.AV1VideoExtension_8wekyb3d8bbwe");
  std::vector<base::FilePath> paths = GetMediaFoundationPackageInstallPaths(
      L"av1decodermft_store.dll", media::MediaFoundationCodecPackage::kAV1);

  if (CanMfDecodeCodec(VideoCodec::kAV1, MFMediaType_Video,
                       MFT_CATEGORY_VIDEO_DECODER, GetVideoCodecsMap)) {
    DVLOG(2) << __func__ << ": MF AV1 installed";
    VerifyMfCodecPaths(paths);
  } else {
    ASSERT_TRUE(paths.empty());
  }
}

TEST_F(MediaFoundationPackageLocatorTest, HEVC) {
  AddPackageFamilyName(L"Microsoft.HEVCVideoExtension_8wekyb3d8bbwe");
  AddPackageFamilyName(L"Microsoft.HEVCVideoExtensions_8wekyb3d8bbwe");  // OEM.
  std::vector<base::FilePath> paths = GetMediaFoundationPackageInstallPaths(
      L"hevcdecoder_store.dll", media::MediaFoundationCodecPackage::kHEVC);

  if (CanMfDecodeCodec(VideoCodec::kHEVC, MFMediaType_Video,
                       MFT_CATEGORY_VIDEO_DECODER, GetVideoCodecsMap)) {
    DVLOG(2) << __func__ << ": MF HEVC installed";
    VerifyMfCodecPaths(paths);
  } else {
    ASSERT_TRUE(paths.empty());
  }
}

TEST_F(MediaFoundationPackageLocatorTest, EAC3) {
  AddPackageFamilyName(
      L"DolbyLaboratories.DolbyDigitalPlusDecoderOEM_rz1tebttyb220");
  std::vector<base::FilePath> paths = GetMediaFoundationPackageInstallPaths(
      L"DolbyDDPDecMft.dll", media::MediaFoundationCodecPackage::kEAC3);

  // MS preloaded Dolby's AC3,EAC3 decoder into Windows image, but from
  // Windows 11 build 25992, all of them will be removed and provided by Dolby
  // as codec packs.
  base::FilePath dolby_dec_mft_path =
      base::PathService::CheckedGet(base::DIR_SYSTEM);
  dolby_dec_mft_path = dolby_dec_mft_path.AppendASCII("DolbyDecMFT.dll");
  bool is_inbox_decoder_present = base::PathExists(dolby_dec_mft_path);
  bool can_decode =
      CanMfDecodeCodec(AudioCodec::kEAC3, MFMediaType_Audio,
                       MFT_CATEGORY_AUDIO_DECODER, GetAudioCodecsMap);
  if (is_inbox_decoder_present) {
    DVLOG(2) << __func__ << ": MF EAC3/AC3 inbox decoder present.";
    ASSERT_TRUE(can_decode);
  } else {
    if (can_decode) {
      DVLOG(2) << __func__ << ": MF EAC3/AC3 installed";
      VerifyMfCodecPaths(paths);
    } else {
      ASSERT_TRUE(paths.empty());
    }
  }
}

TEST_F(MediaFoundationPackageLocatorTest, AC4) {
  AddPackageFamilyName(L"DolbyLaboratories.DolbyAC4DecoderOEM_rz1tebttyb220");
  std::vector<base::FilePath> paths = GetMediaFoundationPackageInstallPaths(
      L"DolbyAc4DecMft.dll", media::MediaFoundationCodecPackage::kAC4);

  if (CanMfDecodeCodec(AudioCodec::kAC4, MFMediaType_Audio,
                       MFT_CATEGORY_AUDIO_DECODER, GetAudioCodecsMap)) {
    DVLOG(2) << __func__ << ": MF AC4 installed";
    VerifyMfCodecPaths(paths);
  } else {
    ASSERT_TRUE(paths.empty());
  }
}

}  // namespace media