| // Copyright 2020 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/gpu/test/video_encoder/video_encoder_test_environment.h" |
| |
| #include <iterator> |
| #include <utility> |
| |
| #include "base/containers/flat_set.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/strings/pattern.h" |
| #include "base/system/sys_info.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "build/chromeos_buildflags.h" |
| #include "gpu/ipc/service/gpu_memory_buffer_factory.h" |
| #include "media/base/bitrate.h" |
| #include "media/base/media_switches.h" |
| #include "media/gpu/buildflags.h" |
| #include "media/gpu/gpu_video_encode_accelerator_helpers.h" |
| #include "media/gpu/macros.h" |
| #include "media/gpu/test/video.h" |
| |
| namespace media { |
| namespace test { |
| namespace { |
| struct CodecParamToProfile { |
| const char* codec; |
| const VideoCodecProfile profile; |
| } kCodecParamToProfile[] = { |
| {"h264baseline", H264PROFILE_BASELINE}, |
| {"h264", H264PROFILE_BASELINE}, |
| {"h264main", H264PROFILE_MAIN}, |
| {"h264high", H264PROFILE_HIGH}, |
| {"vp8", VP8PROFILE_ANY}, |
| {"vp9", VP9PROFILE_PROFILE0}, |
| {"av1", AV1PROFILE_PROFILE_MAIN}, |
| }; |
| |
| uint32_t GetDefaultTargetBitrate(const gfx::Size& resolution, |
| const uint32_t framerate) { |
| // This calculation is based on tinyurl.com/cros-platform-video-encoding. |
| return resolution.GetArea() * 0.1 * framerate; |
| } |
| |
| std::vector<VideoEncodeAccelerator::Config::SpatialLayer> |
| GetDefaultSpatialLayers(const VideoBitrateAllocation& bitrate, |
| const Video* video, |
| size_t num_spatial_layers, |
| size_t num_temporal_layers) { |
| // Returns empty spatial layer config because one temporal layer stream is |
| // equivalent to a simple stream. |
| if (num_temporal_layers == 1u && num_spatial_layers == 1u) |
| return {}; |
| |
| constexpr int kSpatialLayersResolutionScaleDenom[][3] = { |
| {1, 0, 0}, // For one spatial layer. |
| {2, 1, 0}, // For two spatial layers. |
| {4, 2, 1}, // For three spatial layers. |
| }; |
| |
| std::vector<VideoEncodeAccelerator::Config::SpatialLayer> spatial_layers; |
| for (size_t sid = 0; sid < num_spatial_layers; ++sid) { |
| VideoEncodeAccelerator::Config::SpatialLayer spatial_layer; |
| const int resolution_denom = |
| kSpatialLayersResolutionScaleDenom[num_spatial_layers - 1][sid]; |
| LOG_IF(WARNING, video->Resolution().width() % resolution_denom != 0) |
| << "width of SL#" << sid << " is not dividable by " << resolution_denom; |
| LOG_IF(WARNING, video->Resolution().height() % resolution_denom != 0) |
| << "height of SL#" << sid << " is not dividable by " |
| << resolution_denom; |
| spatial_layer.width = video->Resolution().width() / resolution_denom; |
| spatial_layer.height = video->Resolution().height() / resolution_denom; |
| uint32_t spatial_layer_bitrate = 0; |
| for (size_t tid = 0; tid < num_temporal_layers; ++tid) |
| spatial_layer_bitrate += bitrate.GetBitrateBps(sid, tid); |
| spatial_layer.bitrate_bps = spatial_layer_bitrate; |
| spatial_layer.framerate = video->FrameRate(); |
| spatial_layer.num_of_temporal_layers = num_temporal_layers; |
| // Note: VideoEncodeAccelerator currently ignores this max_qp parameter. |
| spatial_layer.max_qp = 30u; |
| spatial_layers.push_back(spatial_layer); |
| } |
| return spatial_layers; |
| } |
| |
| } // namespace |
| |
| // static |
| VideoEncoderTestEnvironment* VideoEncoderTestEnvironment::Create( |
| const base::FilePath& video_path, |
| const base::FilePath& video_metadata_path, |
| bool enable_bitstream_validator, |
| const base::FilePath& output_folder, |
| const std::string& codec, |
| size_t num_temporal_layers, |
| size_t num_spatial_layers, |
| bool save_output_bitstream, |
| absl::optional<uint32_t> encode_bitrate, |
| Bitrate::Mode bitrate_mode, |
| bool reverse, |
| const FrameOutputConfig& frame_output_config, |
| const std::vector<base::test::FeatureRef>& enabled_features, |
| const std::vector<base::test::FeatureRef>& disabled_features) { |
| if (video_path.empty()) { |
| LOG(ERROR) << "No video specified"; |
| return nullptr; |
| } |
| auto video = |
| std::make_unique<media::test::Video>(video_path, video_metadata_path); |
| if (!video->Load(kMaxReadFrames)) { |
| LOG(ERROR) << "Failed to load " << video_path; |
| return nullptr; |
| } |
| |
| // If the video file has the .webm format it needs to be decoded first. |
| // TODO(b/151134705): Add support to cache decompressed video files. |
| if (video->FilePath().MatchesExtension(FILE_PATH_LITERAL(".webm"))) { |
| VLOGF(1) << "Test video " << video->FilePath() |
| << " is compressed, decoding..."; |
| if (!video->Decode()) { |
| LOG(ERROR) << "Failed to decode " << video->FilePath(); |
| return nullptr; |
| } |
| } |
| |
| if (video->PixelFormat() == VideoPixelFormat::PIXEL_FORMAT_UNKNOWN) { |
| LOG(ERROR) << "Test video " << video->FilePath() |
| << " has an invalid video pixel format " |
| << VideoPixelFormatToString(video->PixelFormat()); |
| return nullptr; |
| } |
| |
| const auto* it = base::ranges::find(kCodecParamToProfile, codec, |
| &CodecParamToProfile::codec); |
| if (it == std::end(kCodecParamToProfile)) { |
| LOG(ERROR) << "Unknown codec: " << codec; |
| return nullptr; |
| } |
| |
| const VideoCodecProfile profile = it->profile; |
| if (num_spatial_layers > 1u && profile != VP9PROFILE_PROFILE0) { |
| LOG(ERROR) << "Spatial layer encoding is supported only if output profile " |
| << "is vp9"; |
| return nullptr; |
| } |
| |
| // TODO(b/182008564) Add checks to make sure no features are duplicated, and |
| // there is no intersection between the enabled and disabled set. |
| std::vector<base::test::FeatureRef> combined_enabled_features( |
| enabled_features); |
| std::vector<base::test::FeatureRef> combined_disabled_features( |
| disabled_features); |
| combined_disabled_features.push_back(media::kFFmpegDecodeOpaqueVP8); |
| #if BUILDFLAG(USE_VAAPI) |
| // TODO(crbug.com/828482): remove once enabled by default. |
| combined_enabled_features.push_back(media::kVaapiLowPowerEncoderGen9x); |
| // TODO(crbug.com/811912): remove once enabled by default. |
| combined_enabled_features.push_back(media::kVaapiVP9Encoder); |
| |
| // Disable this feature so that the encoder test can test a resolution |
| // which is denied for the sake of performance. See crbug.com/1008491. |
| combined_disabled_features.push_back( |
| media::kVaapiEnforceVideoMinMaxResolution); |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_VAAPI) |
| // TODO(crbug.com/1186051): remove once enabled by default. |
| combined_enabled_features.push_back(media::kVaapiVp9kSVCHWEncoding); |
| // TODO(b/202926617): remove once enabled by default. |
| combined_enabled_features.push_back(media::kVaapiVp8TemporalLayerHWEncoding); |
| #endif |
| |
| #if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_VAAPI) |
| combined_enabled_features.push_back(media::kVaapiVideoEncodeLinux); |
| #endif |
| |
| #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) |
| combined_enabled_features.push_back(media::kChromeOSHWVBREncoding); |
| #endif |
| |
| const uint32_t target_bitrate = encode_bitrate.value_or( |
| GetDefaultTargetBitrate(video->Resolution(), video->FrameRate())); |
| // TODO(b/181797390): Reconsider if this peak bitrate is reasonable. |
| const media::Bitrate bitrate = |
| bitrate_mode == media::Bitrate::Mode::kVariable |
| ? media::Bitrate::VariableBitrate(target_bitrate, |
| /*peak_bps=*/target_bitrate * 2) |
| : media::Bitrate::ConstantBitrate(target_bitrate); |
| if (bitrate.mode() == media::Bitrate::Mode::kVariable && |
| VideoCodecProfileToVideoCodec(profile) != VideoCodec::kH264) { |
| LOG(ERROR) << "VBR is only supported for H264 encoding"; |
| return nullptr; |
| } |
| return new VideoEncoderTestEnvironment( |
| std::move(video), enable_bitstream_validator, output_folder, profile, |
| num_temporal_layers, num_spatial_layers, bitrate, save_output_bitstream, |
| reverse, frame_output_config, combined_enabled_features, |
| combined_disabled_features); |
| } |
| |
| VideoEncoderTestEnvironment::VideoEncoderTestEnvironment( |
| std::unique_ptr<media::test::Video> video, |
| bool enable_bitstream_validator, |
| const base::FilePath& output_folder, |
| VideoCodecProfile profile, |
| size_t num_temporal_layers, |
| size_t num_spatial_layers, |
| const Bitrate& bitrate, |
| bool save_output_bitstream, |
| bool reverse, |
| const FrameOutputConfig& frame_output_config, |
| const std::vector<base::test::FeatureRef>& enabled_features, |
| const std::vector<base::test::FeatureRef>& disabled_features) |
| : VideoTestEnvironment(enabled_features, disabled_features), |
| video_(std::move(video)), |
| enable_bitstream_validator_(enable_bitstream_validator), |
| output_folder_(output_folder), |
| profile_(profile), |
| num_temporal_layers_(num_temporal_layers), |
| num_spatial_layers_(num_spatial_layers), |
| bitrate_(AllocateDefaultBitrateForTesting(num_spatial_layers_, |
| num_temporal_layers_, |
| bitrate)), |
| spatial_layers_(GetDefaultSpatialLayers(bitrate_, |
| video_.get(), |
| num_spatial_layers_, |
| num_temporal_layers_)), |
| save_output_bitstream_(save_output_bitstream), |
| reverse_(reverse), |
| frame_output_config_(frame_output_config), |
| gpu_memory_buffer_factory_( |
| gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr)) {} |
| |
| VideoEncoderTestEnvironment::~VideoEncoderTestEnvironment() = default; |
| |
| media::test::Video* VideoEncoderTestEnvironment::Video() const { |
| return video_.get(); |
| } |
| |
| media::test::Video* VideoEncoderTestEnvironment::GenerateNV12Video() { |
| if (!nv12_video_) { |
| nv12_video_ = video_->ConvertToNV12(); |
| CHECK(nv12_video_); |
| } |
| return nv12_video_.get(); |
| } |
| |
| bool VideoEncoderTestEnvironment::IsBitstreamValidatorEnabled() const { |
| return enable_bitstream_validator_; |
| } |
| |
| const base::FilePath& VideoEncoderTestEnvironment::OutputFolder() const { |
| return output_folder_; |
| } |
| |
| VideoCodecProfile VideoEncoderTestEnvironment::Profile() const { |
| return profile_; |
| } |
| |
| const std::vector<VideoEncodeAccelerator::Config::SpatialLayer>& |
| VideoEncoderTestEnvironment::SpatialLayers() const { |
| return spatial_layers_; |
| } |
| |
| const VideoBitrateAllocation& VideoEncoderTestEnvironment::BitrateAllocation() |
| const { |
| return bitrate_; |
| } |
| |
| bool VideoEncoderTestEnvironment::SaveOutputBitstream() const { |
| return save_output_bitstream_; |
| } |
| |
| bool VideoEncoderTestEnvironment::Reverse() const { |
| return reverse_; |
| } |
| |
| const FrameOutputConfig& VideoEncoderTestEnvironment::ImageOutputConfig() |
| const { |
| return frame_output_config_; |
| } |
| |
| gpu::GpuMemoryBufferFactory* |
| VideoEncoderTestEnvironment::GetGpuMemoryBufferFactory() const { |
| return gpu_memory_buffer_factory_.get(); |
| } |
| |
| bool VideoEncoderTestEnvironment::IsKeplerUsed() const { |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| const VideoCodec codec = VideoCodecProfileToVideoCodec(Profile()); |
| if (codec != VideoCodec::kVP8) |
| return false; |
| const static std::string board = base::SysInfo::GetLsbReleaseBoard(); |
| if (board == "unknown") { |
| LOG(WARNING) << "Failed to get chromeos board name"; |
| return false; |
| } |
| const char* kKeplerBoards[] = {"buddy*", "guado*", "rikku*"}; |
| for (const char* b : kKeplerBoards) { |
| if (base::MatchPattern(board, b)) |
| return true; |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| return false; |
| } |
| } // namespace test |
| } // namespace media |