chromium/media/mojo/services/gpu_mojo_media_client_win.cc

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

#include "media/mojo/services/gpu_mojo_media_client.h"

#include <d3d11.h>
#include <d3d12.h>
#include <wrl.h>

#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/windows_version.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "media/base/audio_decoder.h"
#include "media/base/media_switches.h"
#include "media/base/offloading_audio_encoder.h"
#include "media/base/win/media_foundation_package_runtime_locator.h"
#include "media/base/win/mf_feature_checks.h"
#include "media/base/win/mf_initializer.h"
#include "media/filters/win/media_foundation_audio_decoder.h"
#include "media/gpu/ipc/service/media_gpu_channel_manager.h"
#include "media/gpu/windows/d3d11_video_decoder.h"
#include "media/gpu/windows/mf_audio_encoder.h"

namespace media {

class GpuMojoMediaClientWin final : public GpuMojoMediaClient {
 public:
  GpuMojoMediaClientWin(GpuMojoMediaClientTraits& traits)
      : GpuMojoMediaClientWin(
            // Grab SharedContextState before `traits` is consumed by
            // GpuMojoMediaClient().
            traits.media_gpu_channel_manager
                ? traits.media_gpu_channel_manager->GetSharedContextState()
                : nullptr,
            traits) {}

  ~GpuMojoMediaClientWin() final = default;

 protected:
  std::unique_ptr<VideoDecoder> CreatePlatformVideoDecoder(
      VideoDecoderTraits& traits) final {
    if (gpu_workarounds_.disable_d3d11_video_decoder) {
      return nullptr;
    }

    if (!multithread_protected_ &&
        IsDedicatedMediaServiceThreadEnabled(
            gpu_info_.gl_implementation_parts.angle)) {
      // Since the D3D11Device used for decoding is shared with
      // SkiaRenderer(ANGLE or Dawn), we need multithread protection turned on
      // to use it from another thread.
      Microsoft::WRL::ComPtr<ID3D11Multithread> multi_threaded;
      auto hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&multi_threaded));
      CHECK(SUCCEEDED(hr));
      multi_threaded->SetMultithreadProtected(TRUE);
      multithread_protected_ = true;
    }

    return D3D11VideoDecoder::Create(
        gpu_task_runner_, traits.media_log->Clone(), gpu_preferences_,
        gpu_workarounds_, traits.get_command_buffer_stub_cb,
        GetD3DDeviceCallback(), traits.get_cached_configs_cb.Run());
  }

  std::unique_ptr<AudioEncoder> CreatePlatformAudioEncoder(
      scoped_refptr<base::SequencedTaskRunner> task_runner) final {
    auto encoding_runner = base::ThreadPool::CreateCOMSTATaskRunner({});
    auto mf_encoder = std::make_unique<MFAudioEncoder>(encoding_runner);
    return std::make_unique<OffloadingAudioEncoder>(std::move(mf_encoder),
                                                    std::move(encoding_runner),
                                                    std::move(task_runner));
  }

  std::optional<SupportedAudioDecoderConfigs>
  GetPlatformSupportedAudioDecoderConfigs() final {
    SupportedAudioDecoderConfigs audio_configs;

#if BUILDFLAG(ENABLE_PLATFORM_AC4_AUDIO)
    if (FindMediaFoundationPackageDecoder(AudioCodec::kAC4)) {
      audio_configs.emplace_back(AudioCodec::kAC4, AudioCodecProfile::kUnknown);
    }
#endif  // BUILDFLAG(ENABLE_PLATFORM_AC4_AUDIO)

#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
    // 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.
    // Preloaded decoder dll is placed in SYSTEM folder and named as
    // 'DolbyDecMFT.dll'.
    base::FilePath dolby_dec_mft_path =
        base::PathService::CheckedGet(base::DIR_SYSTEM);
    dolby_dec_mft_path = dolby_dec_mft_path.AppendASCII("DolbyDecMFT.dll");
    bool has_legacy_dolby_ac3_eac3_mft = false;
    {
      // AC3/EAC3 decoder check needs to access file system, so allow scoped
      // blocking here.
      base::ScopedAllowBlocking allow_blocking;
      has_legacy_dolby_ac3_eac3_mft = base::PathExists(dolby_dec_mft_path);
    }
    if (has_legacy_dolby_ac3_eac3_mft ||
        FindMediaFoundationPackageDecoder(AudioCodec::kEAC3)) {
      audio_configs.emplace_back(AudioCodec::kAC3, AudioCodecProfile::kUnknown);
      audio_configs.emplace_back(AudioCodec::kEAC3,
                                 AudioCodecProfile::kUnknown);
    }
#endif  // BUILDFLAG (ENABLE_PLATFORM_AC3_EAC3_AUDIO)

    if (base::win::GetVersion() >= base::win::Version::WIN11_22H2 &&
        InitializeMediaFoundation()) {
      audio_configs.emplace_back(AudioCodec::kAAC, AudioCodecProfile::kXHE_AAC);
    }

    return audio_configs;
  }

  std::optional<SupportedVideoDecoderConfigs>
  GetPlatformSupportedVideoDecoderConfigs() final {
    // This method must be called on the GPU main thread.
    SupportedVideoDecoderConfigs supported_configs;
    if (gpu_preferences_.disable_accelerated_video_decode) {
      return supported_configs;
    }
    if (!gpu_workarounds_.disable_d3d11_video_decoder) {
      supported_configs = D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
          gpu_preferences_, gpu_workarounds_, GetD3DDeviceCallback());
    }
    return supported_configs;
  }

  std::unique_ptr<AudioDecoder> CreatePlatformAudioDecoder(
      scoped_refptr<base::SequencedTaskRunner> task_runner,
      std::unique_ptr<MediaLog> media_log) final {
    return MediaFoundationAudioDecoder::Create();
  }

  VideoDecoderType GetPlatformDecoderImplementationType() final {
    return VideoDecoderType::kD3D11;
  }

 private:
  GpuMojoMediaClientWin(
      scoped_refptr<gpu::SharedContextState> shared_context_state,
      GpuMojoMediaClientTraits& traits)
      : GpuMojoMediaClient(traits) {
    // Note: `traits` is empty after GpuMojoMediaClient().
    if (!shared_context_state) {
      return;
    }

    d3d11_device_ = shared_context_state->GetD3D11Device();
    if (base::FeatureList::IsEnabled(kD3D12VideoDecoder)) {
      Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
      CHECK_EQ(d3d11_device_.As(&dxgi_device), S_OK);
      Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
      CHECK_EQ(dxgi_device->GetAdapter(&adapter), S_OK);
      d3d12_device_ = CreateD3D12Device(adapter.Get());
    }
  }

  D3D11VideoDecoder::GetD3DDeviceCB GetD3DDeviceCallback() {
    return base::BindRepeating(
        [](Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
           Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device,
           D3D11VideoDecoder::D3DVersion d3d_version)
            -> Microsoft::WRL::ComPtr<IUnknown> {
          if (d3d_version == D3D11VideoDecoder::D3DVersion::kD3D11) {
            return d3d11_device;
          } else if (d3d_version == D3D11VideoDecoder::D3DVersion::kD3D12) {
            return d3d12_device;
          }
          NOTREACHED();
        },
        d3d11_device_, d3d12_device_);
  }

  bool multithread_protected_ = false;
  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
  Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device_;
};

std::unique_ptr<GpuMojoMediaClient> CreateGpuMediaService(
    GpuMojoMediaClientTraits& traits) {
  return std::make_unique<GpuMojoMediaClientWin>(traits);
}

}  // namespace media