#include "media/gpu/gpu_video_encode_accelerator_factory.h"
#if BUILDFLAG(IS_WIN)
#include "third_party/microsoft_dxheaders/src/include/directx/d3d12video.h"
#endif
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/encoder_status.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/gpu/buildflags.h"
#include "media/gpu/gpu_video_accelerator_util.h"
#include "media/gpu/macros.h"
#include "media/video/video_encode_accelerator.h"
#if BUILDFLAG(IS_ANDROID)
#include "media/gpu/android/ndk_video_encode_accelerator.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "media/gpu/mac/vt_video_encode_accelerator_mac.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "media/gpu/windows/d3d12_helpers.h"
#include "media/gpu/windows/d3d12_video_encode_accelerator.h"
#include "media/gpu/windows/media_foundation_video_encode_accelerator_win.h"
#endif
#if BUILDFLAG(USE_V4L2_CODEC)
#include "media/gpu/v4l2/v4l2_video_encode_accelerator.h"
#elif BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_video_encode_accelerator.h"
#endif
#if BUILDFLAG(IS_FUCHSIA)
#include "media/fuchsia/video/fuchsia_video_encode_accelerator.h"
#endif
#if BUILDFLAG(IS_ARKWEB)
#include "media/base/ohos/ohos_media_codec_util.h"
#include "media/gpu/ohos/ohos_video_encode_accelerator.h"
#endif
namespace media {
namespace {
#if BUILDFLAG(USE_V4L2_CODEC)
std::unique_ptr<VideoEncodeAccelerator> CreateV4L2VEA() {
#if BUILDFLAG(IS_CHROMEOS)
return base::WrapUnique<VideoEncodeAccelerator>(
new V4L2VideoEncodeAccelerator(base::MakeRefCounted<V4L2Device>()));
#else
return nullptr;
#endif
}
#elif BUILDFLAG(USE_VAAPI)
std::unique_ptr<VideoEncodeAccelerator> CreateVaapiVEA() {
return base::WrapUnique<VideoEncodeAccelerator>(
new VaapiVideoEncodeAccelerator());
}
#endif
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<VideoEncodeAccelerator> CreateAndroidVEA() {
return base::WrapUnique<VideoEncodeAccelerator>(new NdkVideoEncodeAccelerator(
base::SequencedTaskRunner::GetCurrentDefault()));
}
#endif
#if BUILDFLAG(IS_MAC)
std::unique_ptr<VideoEncodeAccelerator> CreateVTVEA() {
return base::WrapUnique<VideoEncodeAccelerator>(
new VTVideoEncodeAccelerator());
}
#endif
#if BUILDFLAG(IS_WIN)
std::unique_ptr<VideoEncodeAccelerator> CreateMediaFoundationVEA(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device) {
return base::WrapUnique<VideoEncodeAccelerator>(
new MediaFoundationVideoEncodeAccelerator(
gpu_preferences, gpu_workarounds, gpu_device.luid));
}
Microsoft::WRL::ComPtr<IDXGIAdapter> GetDxgiAdapterByLuid(CHROME_LUID luid) {
Microsoft::WRL::ComPtr<IDXGIFactory4> dxgi_factory4;
if (FAILED(::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory4)))) {
LOG(ERROR) << "Failed to create DXGI factory";
return nullptr;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
if (FAILED(dxgi_factory4->EnumAdapterByLuid(
{.LowPart = luid.LowPart, .HighPart = luid.HighPart},
IID_PPV_ARGS(&adapter)))) {
LOG(ERROR) << "Failed to enum adapter by LUID";
return nullptr;
}
return adapter;
}
std::unique_ptr<VideoEncodeAccelerator> CreateD3D12VEA(
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device) {
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter =
GetDxgiAdapterByLuid(gpu_device.luid);
if (!adapter) {
LOG(ERROR) << "Failed to get an adapter by LUID";
return nullptr;
}
ComD3D12Device d3d12_device = CreateD3D12Device(adapter.Get());
if (!d3d12_device) {
LOG(ERROR) << "Failed to create D3D12 device";
return nullptr;
}
return base::WrapUnique<VideoEncodeAccelerator>(
new D3D12VideoEncodeAccelerator(std::move(d3d12_device),
gpu_workarounds));
}
#endif
#if BUILDFLAG(IS_FUCHSIA)
std::unique_ptr<VideoEncodeAccelerator> CreateFuchsiaVEA() {
return base::WrapUnique<VideoEncodeAccelerator>(
new FuchsiaVideoEncodeAccelerator());
}
#endif
#if BUILDFLAG(ARKWEB_HEIF_SUPPORT)
std::unique_ptr<VideoEncodeAccelerator> CreateOHOSVEA() {
return base::WrapUnique<VideoEncodeAccelerator>(
new OHOSVideoEncodeAccelerator());
}
#endif
using VEAFactoryFunction =
base::RepeatingCallback<std::unique_ptr<VideoEncodeAccelerator>()>;
std::vector<VEAFactoryFunction> GetVEAFactoryFunctions(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device) {
static base::NoDestructor<std::vector<VEAFactoryFunction>>
vea_factory_functions;
if (gpu_preferences.disable_accelerated_video_encode)
return *vea_factory_functions;
if (!vea_factory_functions->empty()) {
return *vea_factory_functions;
}
#if BUILDFLAG(USE_VAAPI)
#if BUILDFLAG(IS_LINUX)
if (base::FeatureList::IsEnabled(kAcceleratedVideoEncodeLinux)) {
vea_factory_functions->push_back(base::BindRepeating(&CreateVaapiVEA));
}
#else
vea_factory_functions->push_back(base::BindRepeating(&CreateVaapiVEA));
#endif
#elif BUILDFLAG(USE_V4L2_CODEC)
#if BUILDFLAG(IS_LINUX)
if (base::FeatureList::IsEnabled(kAcceleratedVideoEncodeLinux)) {
vea_factory_functions->push_back(base::BindRepeating(&CreateV4L2VEA));
}
#else
vea_factory_functions->push_back(base::BindRepeating(&CreateV4L2VEA));
#endif
#endif
#if BUILDFLAG(IS_ANDROID)
vea_factory_functions->push_back(base::BindRepeating(&CreateAndroidVEA));
#endif
#if BUILDFLAG(IS_MAC)
vea_factory_functions->push_back(base::BindRepeating(&CreateVTVEA));
#endif
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(kD3D12VideoEncodeAccelerator) &&
!gpu_workarounds.disable_d3d12_video_encoder) {
vea_factory_functions->push_back(
base::BindRepeating(&CreateD3D12VEA, gpu_workarounds, gpu_device));
}
if (base::FeatureList::IsEnabled(kMediaFoundationVideoEncodeAccelerator)) {
vea_factory_functions->push_back(
base::BindRepeating(&CreateMediaFoundationVEA, gpu_preferences,
gpu_workarounds, gpu_device));
}
#endif
#if BUILDFLAG(IS_FUCHSIA)
if (base::FeatureList::IsEnabled(kFuchsiaMediacodecVideoEncoder)) {
vea_factory_functions->push_back(base::BindRepeating(&CreateFuchsiaVEA));
}
#endif
#if BUILDFLAG(ARKWEB_HEIF_SUPPORT)
vea_factory_functions->push_back(base::BindRepeating(&CreateOHOSVEA));
#endif
return *vea_factory_functions;
}
VideoEncodeAccelerator::SupportedProfiles GetSupportedProfilesInternal(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device) {
if (gpu_preferences.disable_accelerated_video_encode)
return VideoEncodeAccelerator::SupportedProfiles();
VideoEncodeAccelerator::SupportedProfiles profiles;
for (const auto& create_vea :
GetVEAFactoryFunctions(gpu_preferences, gpu_workarounds, gpu_device)) {
auto vea = std::move(create_vea).Run();
if (!vea)
continue;
auto vea_profiles = vea->GetSupportedProfiles();
GpuVideoAcceleratorUtil::InsertUniqueEncodeProfiles(vea_profiles,
&profiles);
}
return profiles;
}
}
MEDIA_GPU_EXPORT EncoderStatus::Or<std::unique_ptr<VideoEncodeAccelerator>>
GpuVideoEncodeAcceleratorFactory::CreateVEA(
const VideoEncodeAccelerator::Config& config,
VideoEncodeAccelerator::Client* client,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device,
std::unique_ptr<MediaLog> media_log,
GetCommandBufferHelperCB get_command_buffer_helper_cb,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner) {
if (!media_log)
media_log = std::make_unique<media::NullMediaLog>();
EncoderStatus initialization_err{
EncoderStatus::Codes::kEncoderInitializationError};
std::vector<VEAFactoryFunction> create_vea_functions =
GetVEAFactoryFunctions(gpu_preferences, gpu_workarounds, gpu_device);
for (const auto& create_vea : create_vea_functions) {
std::unique_ptr<VideoEncodeAccelerator> vea = create_vea.Run();
if (!vea)
continue;
if (create_vea_functions.size() > 1) {
const auto profiles = vea->GetSupportedProfiles();
if (std::ranges::find(
profiles, config.output_profile,
&VideoEncodeAccelerator::SupportedProfile::profile) ==
profiles.end()) {
continue;
}
}
if (!get_command_buffer_helper_cb.is_null()) {
vea->SetCommandBufferHelperCB(get_command_buffer_helper_cb,
gpu_task_runner);
}
if (auto status = vea->Initialize(config, client, media_log->Clone());
!status.is_ok()) {
DLOG(ERROR) << "VEA initialize failed (" << config.AsHumanReadableString()
<< "): " << status.message();
initialization_err = status;
continue;
}
return vea;
}
return std::move(initialization_err);
}
MEDIA_GPU_EXPORT VideoEncodeAccelerator::SupportedProfiles
GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
const gpu::GPUInfo::GPUDevice& gpu_device) {
static base::NoDestructor<VideoEncodeAccelerator::SupportedProfiles> profiles(
GetSupportedProfilesInternal(gpu_preferences, gpu_workarounds,
gpu_device));
#if BUILDFLAG(USE_V4L2_CODEC)
if (profiles->empty()) {
VLOGF(1) << "Supported profiles empty, querying again...";
*profiles = GetSupportedProfilesInternal(gpu_preferences, gpu_workarounds,
gpu_device);
}
#endif
if (gpu_workarounds.disable_accelerated_av1_encode) {
std::erase_if(*profiles, [](const auto& vea_profile) {
return vea_profile.profile >= AV1PROFILE_PROFILE_MAIN &&
vea_profile.profile <= AV1PROFILE_PROFILE_PRO;
});
}
if (gpu_workarounds.disable_accelerated_vp8_encode) {
std::erase_if(*profiles, [](const auto& vea_profile) {
return vea_profile.profile == VP8PROFILE_ANY;
});
}
if (gpu_workarounds.disable_accelerated_vp9_encode) {
std::erase_if(*profiles, [](const auto& vea_profile) {
return vea_profile.profile >= VP9PROFILE_PROFILE0 &&
vea_profile.profile <= VP9PROFILE_PROFILE3;
});
}
if (gpu_workarounds.disable_accelerated_h264_encode) {
std::erase_if(*profiles, [](const auto& vea_profile) {
return vea_profile.profile >= H264PROFILE_MIN &&
vea_profile.profile <= H264PROFILE_MAX;
});
}
return *profiles;
}
}