#include "media/mojo/services/gpu_mojo_media_client.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/sequenced_task_runner.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/config/gpu_feature_info.h"
#include "media/base/audio_decoder.h"
#include "media/base/audio_encoder.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/gpu/chromeos/mailbox_video_frame_converter.h"
#include "media/gpu/chromeos/platform_video_frame_pool.h"
#include "media/gpu/chromeos/simple_video_frame_converter.h"
#include "media/gpu/chromeos/video_decoder_pipeline.h"
namespace media {
namespace {
BASE_FEATURE(kAcceleratedVideoDecodeLinuxZeroCopyGL,
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kRenderableMM21, base::FEATURE_DISABLED_BY_DEFAULT);
VideoDecoderType GetPreferredLinuxDecoderImplementation() {
if (!base::FeatureList::IsEnabled(kAcceleratedVideoDecodeLinux)) {
return VideoDecoderType::kUnknown;
}
if (IsOutOfProcessVideoDecodingEnabled()) {
return VideoDecoderType::kOutOfProcess;
}
#if BUILDFLAG(USE_VAAPI)
return VideoDecoderType::kVaapi;
#elif BUILDFLAG(USE_V4L2_CODEC)
return VideoDecoderType::kV4L2;
#endif
}
std::vector<Fourcc> GetPreferredRenderableFourccs(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuFeatureInfo& gpu_feature_info) {
std::vector<Fourcc> renderable_fourccs;
#if BUILDFLAG(ENABLE_VULKAN)
if (gpu_preferences.gr_context_type == gpu::GrContextType::kVulkan) {
renderable_fourccs.emplace_back(Fourcc::NV12);
renderable_fourccs.emplace_back(Fourcc::P010);
} else
#endif
#if BUILDFLAG(IS_OZONE)
if (gpu_preferences.gr_context_type == gpu::GrContextType::kGL &&
base::FeatureList::IsEnabled(kAcceleratedVideoDecodeLinuxZeroCopyGL)) {
if (base::Contains(
gpu_feature_info.supported_formats_for_gl_native_pixmap_import,
viz::MultiPlaneFormat::kNV12)) {
if (base::FeatureList::IsEnabled(kRenderableMM21)) {
renderable_fourccs.emplace_back(Fourcc::MM21);
}
renderable_fourccs.emplace_back(Fourcc::NV12);
}
if (base::Contains(
gpu_feature_info.supported_formats_for_gl_native_pixmap_import,
viz::MultiPlaneFormat::kP010)) {
renderable_fourccs.emplace_back(Fourcc::P010);
}
}
#endif
renderable_fourccs.emplace_back(Fourcc::AR24);
return renderable_fourccs;
}
VideoDecoderType GetActualPlatformDecoderImplementation(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GPUInfo& gpu_info) {
switch (GetPreferredLinuxDecoderImplementation()) {
case VideoDecoderType::kUnknown:
return VideoDecoderType::kUnknown;
case VideoDecoderType::kOutOfProcess:
return VideoDecoderType::kOutOfProcess;
case VideoDecoderType::kV4L2:
if (gpu_preferences.gr_context_type == gpu::GrContextType::kGL) {
if (base::FeatureList::IsEnabled(kAcceleratedVideoDecodeLinuxGL)) {
return VideoDecoderType::kV4L2;
} else {
return VideoDecoderType::kUnknown;
}
}
return VideoDecoderType::kV4L2;
case VideoDecoderType::kVaapi: {
if (gpu_preferences.gr_context_type == gpu::GrContextType::kGL) {
if (base::FeatureList::IsEnabled(kAcceleratedVideoDecodeLinuxGL)) {
return VideoDecoderType::kVaapi;
} else {
return VideoDecoderType::kUnknown;
}
}
#if BUILDFLAG(ENABLE_VULKAN)
if (gpu_preferences.gr_context_type != gpu::GrContextType::kVulkan) {
return VideoDecoderType::kUnknown;
}
if (!base::FeatureList::IsEnabled(features::kVulkanFromANGLE)) {
return VideoDecoderType::kUnknown;
}
if (!base::FeatureList::IsEnabled(features::kDefaultANGLEVulkan)) {
return VideoDecoderType::kUnknown;
}
if (!gpu_info.vulkan_info.has_value()) {
return VideoDecoderType::kUnknown;
}
if (gpu_info.vulkan_info->physical_devices.empty()) {
return VideoDecoderType::kUnknown;
}
constexpr int kIntel = 0x8086;
const auto& device = gpu_info.vulkan_info->physical_devices[0];
switch (device.properties.vendorID) {
case kIntel: {
if (device.properties.driverVersion < VK_MAKE_VERSION(21, 1, 5)) {
return VideoDecoderType::kUnknown;
}
return VideoDecoderType::kVaapi;
}
default: {
if (base::FeatureList::IsEnabled(kVaapiIgnoreDriverChecks)) {
return VideoDecoderType::kVaapi;
}
return VideoDecoderType::kUnknown;
}
}
#else
return VideoDecoderType::kUnknown;
#endif
}
default:
return VideoDecoderType::kUnknown;
}
}
}
class GpuMojoMediaClientLinux final : public GpuMojoMediaClient {
public:
GpuMojoMediaClientLinux(GpuMojoMediaClientTraits& traits)
: GpuMojoMediaClient(traits) {}
~GpuMojoMediaClientLinux() final = default;
protected:
std::unique_ptr<VideoDecoder> CreatePlatformVideoDecoder(
VideoDecoderTraits& traits) final {
const auto decoder_type =
GetActualPlatformDecoderImplementation(gpu_preferences_, gpu_info_);
CHECK_EQ(!!traits.oop_video_decoder,
(decoder_type == VideoDecoderType::kOutOfProcess));
switch (decoder_type) {
case VideoDecoderType::kOutOfProcess: {
auto frame_converter =
base::FeatureList::IsEnabled(kUseSharedImageInOOPVDProcess)
? SimpleVideoFrameConverter::Create()
: MailboxVideoFrameConverter::Create(
gpu_task_runner_, traits.get_command_buffer_stub_cb);
return VideoDecoderPipeline::Create(
gpu_workarounds_, traits.task_runner, nullptr,
std::move(frame_converter),
GetPreferredRenderableFourccs(gpu_preferences_, gpu_feature_info_),
traits.media_log->Clone(), std::move(traits.oop_video_decoder),
false);
}
case VideoDecoderType::kVaapi:
case VideoDecoderType::kV4L2: {
auto frame_pool = std::make_unique<PlatformVideoFramePool>();
auto frame_converter = MailboxVideoFrameConverter::Create(
gpu_task_runner_, traits.get_command_buffer_stub_cb);
return VideoDecoderPipeline::Create(
gpu_workarounds_, traits.task_runner, std::move(frame_pool),
std::move(frame_converter),
GetPreferredRenderableFourccs(gpu_preferences_, gpu_feature_info_),
traits.media_log->Clone(), {},
false);
}
default:
return nullptr;
}
}
void NotifyPlatformDecoderSupport(
mojo::PendingRemote<mojom::VideoDecoder> oop_video_decoder,
base::OnceCallback<void(mojo::PendingRemote<mojom::VideoDecoder>)> cb)
final {
switch (
GetActualPlatformDecoderImplementation(gpu_preferences_, gpu_info_)) {
case VideoDecoderType::kOutOfProcess:
case VideoDecoderType::kVaapi:
case VideoDecoderType::kV4L2:
VideoDecoderPipeline::NotifySupportKnown(std::move(oop_video_decoder),
std::move(cb));
break;
default:
std::move(cb).Run(std::move(oop_video_decoder));
}
}
std::optional<SupportedVideoDecoderConfigs>
GetPlatformSupportedVideoDecoderConfigs() final {
VideoDecoderType decoder_implementation =
GetActualPlatformDecoderImplementation(gpu_preferences_, gpu_info_);
base::UmaHistogramEnumeration("Media.VaapiLinux.SupportedVideoDecoder",
decoder_implementation);
switch (decoder_implementation) {
case VideoDecoderType::kOutOfProcess:
case VideoDecoderType::kVaapi:
case VideoDecoderType::kV4L2:
return VideoDecoderPipeline::GetSupportedConfigs(decoder_implementation,
gpu_workarounds_);
default:
return std::nullopt;
}
}
VideoDecoderType GetPlatformDecoderImplementationType() final {
return GetPreferredLinuxDecoderImplementation();
}
};
std::unique_ptr<GpuMojoMediaClient> CreateGpuMediaService(
GpuMojoMediaClientTraits& traits) {
return std::make_unique<GpuMojoMediaClientLinux>(traits);
}
}