|  | /* | 
|  | *  Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstdint> | 
|  | #include <iterator> | 
|  | #include <limits> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/flags/flag.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/environment/environment_factory.h" | 
|  | #include "api/test/metrics/global_metrics_logger_and_exporter.h" | 
|  | #include "api/units/data_rate.h" | 
|  | #include "api/units/frequency.h" | 
|  | #include "api/video/resolution.h" | 
|  | #include "api/video_codecs/builtin_video_decoder_factory.h" | 
|  | #include "api/video_codecs/builtin_video_encoder_factory.h" | 
|  | #include "api/video_codecs/scalability_mode.h" | 
|  | #include "api/video_codecs/sdp_video_format.h" | 
|  | #include "api/video_codecs/video_decoder_factory.h" | 
|  | #include "api/video_codecs/video_encoder_factory.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | #include "modules/video_coding/codecs/test/android_codec_factory_helper.h" | 
|  | #endif | 
|  | #include "modules/video_coding/svc/scalability_mode_util.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/strings/string_builder.h" | 
|  | #include "test/create_test_field_trials.h" | 
|  | #include "test/gtest.h" | 
|  | #include "test/test_flags.h" | 
|  | #include "test/testsupport/file_utils.h" | 
|  | #include "test/video_codec_tester.h" | 
|  |  | 
|  | ABSL_FLAG(std::string, | 
|  | input_path, | 
|  | webrtc::test::ResourcePath("FourPeople_1280x720_30", "yuv"), | 
|  | "Path to input video file."); | 
|  | ABSL_FLAG(int, input_width, 1280, "Input video width."); | 
|  | ABSL_FLAG(int, input_height, 720, "Input video height."); | 
|  | ABSL_FLAG(double, input_framerate_fps, 30, "Input video framerate, fps."); | 
|  | ABSL_FLAG(std::string, | 
|  | encoder, | 
|  | "libaom-av1", | 
|  | "Encoder: libaom-av1, libvpx-vp9, libvpx-vp8, openh264, hw-vp8, " | 
|  | "hw-vp9, hw-av1, hw-h264, hw-h265"); | 
|  | ABSL_FLAG(std::string, | 
|  | decoder, | 
|  | "dav1d", | 
|  | "Decoder: dav1d, libvpx-vp9, libvpx-vp8, ffmpeg-h264, hw-vp8, " | 
|  | "hw-vp9, hw-av1, hw-h264, hw-h265"); | 
|  | ABSL_FLAG(std::string, scalability_mode, "L1T1", "Scalability mode."); | 
|  | ABSL_FLAG(std::optional<int>, width, std::nullopt, "Encode width."); | 
|  | ABSL_FLAG(std::optional<int>, height, std::nullopt, "Encode height."); | 
|  | ABSL_FLAG(std::vector<std::string>, | 
|  | bitrate_kbps, | 
|  | {"1024"}, | 
|  | "Encode target bitrate per layer (l0t0,l0t1,...l1t0,l1t1 and so on) " | 
|  | "in kbps."); | 
|  | ABSL_FLAG(std::optional<double>, | 
|  | framerate_fps, | 
|  | std::nullopt, | 
|  | "Encode target frame rate of the top temporal layer in fps."); | 
|  | ABSL_FLAG(bool, screencast, false, "Enable screen encoding mode."); | 
|  | ABSL_FLAG(bool, frame_drop, true, "Enable frame dropping."); | 
|  | ABSL_FLAG(int, | 
|  | key_interval, | 
|  | std::numeric_limits<int>::max(), | 
|  | "Keyframe interval in frames."); | 
|  | ABSL_FLAG(int, num_frames, 300, "Number of frames to encode and/or decode."); | 
|  | ABSL_FLAG(std::string, test_name, "", "Test name."); | 
|  | ABSL_FLAG(bool, dump_decoder_input, false, "Dump decoder input."); | 
|  | ABSL_FLAG(bool, dump_decoder_output, false, "Dump decoder output."); | 
|  | ABSL_FLAG(bool, dump_encoder_input, false, "Dump encoder input."); | 
|  | ABSL_FLAG(bool, dump_encoder_output, false, "Dump encoder output."); | 
|  | ABSL_FLAG(bool, write_csv, false, "Write metrics to a CSV file."); | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  |  | 
|  | namespace { | 
|  | using ::testing::Combine; | 
|  | using ::testing::Values; | 
|  | using VideoSourceSettings = VideoCodecTester::VideoSourceSettings; | 
|  | using EncodingSettings = VideoCodecTester::EncodingSettings; | 
|  | using VideoCodecStats = VideoCodecTester::VideoCodecStats; | 
|  | using Filter = VideoCodecStats::Filter; | 
|  | using PacingMode = VideoCodecTester::PacingSettings::PacingMode; | 
|  |  | 
|  | struct VideoInfo { | 
|  | std::string name; | 
|  | Resolution resolution; | 
|  | Frequency framerate; | 
|  | }; | 
|  |  | 
|  | VideoInfo kFourPeople_1280x720_30 = { | 
|  | .name = "FourPeople_1280x720_30", | 
|  | .resolution = {.width = 1280, .height = 720}, | 
|  | .framerate = Frequency::Hertz(30)}; | 
|  |  | 
|  | constexpr Frequency k90kHz = Frequency::Hertz(90000); | 
|  |  | 
|  | VideoSourceSettings ToSourceSettings(VideoInfo video_info) { | 
|  | return VideoSourceSettings{.file_path = ResourcePath(video_info.name, "yuv"), | 
|  | .resolution = video_info.resolution, | 
|  | .framerate = video_info.framerate}; | 
|  | } | 
|  |  | 
|  | std::string CodecNameToCodecType(std::string name) { | 
|  | if (name.find("av1") != std::string::npos) { | 
|  | return "AV1"; | 
|  | } | 
|  | if (name.find("vp9") != std::string::npos) { | 
|  | return "VP9"; | 
|  | } | 
|  | if (name.find("vp8") != std::string::npos) { | 
|  | return "VP8"; | 
|  | } | 
|  | if (name.find("h264") != std::string::npos) { | 
|  | return "H264"; | 
|  | } | 
|  | if (name.find("h265") != std::string::npos) { | 
|  | return "H265"; | 
|  | } | 
|  | RTC_CHECK_NOTREACHED(); | 
|  | } | 
|  |  | 
|  | // TODO(webrtc:14852): Make Create[Encoder,Decoder]Factory to work with codec | 
|  | // name directly. | 
|  | std::string CodecNameToCodecImpl(std::string name) { | 
|  | if (name.find("hw") != std::string::npos) { | 
|  | return "mediacodec"; | 
|  | } | 
|  | return "builtin"; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoEncoderFactory> CreateEncoderFactory(std::string impl) { | 
|  | if (impl == "builtin") { | 
|  | return CreateBuiltinVideoEncoderFactory(); | 
|  | } | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | InitializeAndroidObjects(); | 
|  | return CreateAndroidEncoderFactory(); | 
|  | #else | 
|  | return nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoDecoderFactory> CreateDecoderFactory(std::string impl) { | 
|  | if (impl == "builtin") { | 
|  | return CreateBuiltinVideoDecoderFactory(); | 
|  | } | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | InitializeAndroidObjects(); | 
|  | return CreateAndroidDecoderFactory(); | 
|  | #else | 
|  | return nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | std::string TestName() { | 
|  | std::string test_name = absl::GetFlag(FLAGS_test_name); | 
|  | if (!test_name.empty()) { | 
|  | return test_name; | 
|  | } | 
|  | return ::testing::UnitTest::GetInstance()->current_test_info()->name(); | 
|  | } | 
|  |  | 
|  | std::string TestOutputPath() { | 
|  | std::string output_path = | 
|  | (StringBuilder() << OutputPath() << TestName()).str(); | 
|  | std::string output_dir = DirName(output_path); | 
|  | bool result = CreateDir(output_dir); | 
|  | RTC_CHECK(result) << "Cannot create " << output_dir; | 
|  | return output_path; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest( | 
|  | const Environment& env, | 
|  | std::string encoder_impl, | 
|  | std::string decoder_impl, | 
|  | const VideoSourceSettings& source_settings, | 
|  | const std::map<uint32_t, EncodingSettings>& encoding_settings) { | 
|  | const SdpVideoFormat& sdp_video_format = | 
|  | encoding_settings.begin()->second.sdp_video_format; | 
|  |  | 
|  | std::unique_ptr<VideoEncoderFactory> encoder_factory = | 
|  | CreateEncoderFactory(encoder_impl); | 
|  | if (!encoder_factory | 
|  | ->QueryCodecSupport(sdp_video_format, | 
|  | /*scalability_mode=*/std::nullopt) | 
|  | .is_supported) { | 
|  | RTC_LOG(LS_WARNING) << "No " << encoder_impl << " encoder for video format " | 
|  | << sdp_video_format.ToString(); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoDecoderFactory> decoder_factory = | 
|  | CreateDecoderFactory(decoder_impl); | 
|  | if (!decoder_factory | 
|  | ->QueryCodecSupport(sdp_video_format, | 
|  | /*reference_scaling=*/false) | 
|  | .is_supported) { | 
|  | RTC_LOG(LS_WARNING) << "No " << decoder_impl << " decoder for video format " | 
|  | << sdp_video_format.ToString() | 
|  | << ". Trying built-in decoder."; | 
|  | // TODO(ssilkin): No H264 support in ffmpeg on ARM. Consider trying HW | 
|  | // decoder. | 
|  | decoder_factory = CreateDecoderFactory("builtin"); | 
|  | if (!decoder_factory | 
|  | ->QueryCodecSupport(sdp_video_format, | 
|  | /*reference_scaling=*/false) | 
|  | .is_supported) { | 
|  | RTC_LOG(LS_WARNING) << "No " << decoder_impl | 
|  | << " decoder for video format " | 
|  | << sdp_video_format.ToString(); | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string output_path = TestOutputPath(); | 
|  |  | 
|  | VideoCodecTester::EncoderSettings encoder_settings; | 
|  | encoder_settings.pacing_settings.mode = | 
|  | encoder_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime; | 
|  | if (absl::GetFlag(FLAGS_dump_encoder_input)) { | 
|  | encoder_settings.encoder_input_base_path = output_path + "_enc_input"; | 
|  | } | 
|  | if (absl::GetFlag(FLAGS_dump_encoder_output)) { | 
|  | encoder_settings.encoder_output_base_path = output_path + "_enc_output"; | 
|  | } | 
|  |  | 
|  | VideoCodecTester::DecoderSettings decoder_settings; | 
|  | decoder_settings.pacing_settings.mode = | 
|  | decoder_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime; | 
|  | if (absl::GetFlag(FLAGS_dump_decoder_input)) { | 
|  | decoder_settings.decoder_input_base_path = output_path + "_dec_input"; | 
|  | } | 
|  | if (absl::GetFlag(FLAGS_dump_decoder_output)) { | 
|  | decoder_settings.decoder_output_base_path = output_path + "_dec_output"; | 
|  | } | 
|  |  | 
|  | return VideoCodecTester::RunEncodeDecodeTest( | 
|  | env, source_settings, encoder_factory.get(), decoder_factory.get(), | 
|  | encoder_settings, decoder_settings, encoding_settings); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> RunEncodeTest( | 
|  | const Environment& env, | 
|  | std::string encoder_impl, | 
|  | const VideoSourceSettings& source_settings, | 
|  | const std::map<uint32_t, EncodingSettings>& encoding_settings) { | 
|  | const SdpVideoFormat& sdp_video_format = | 
|  | encoding_settings.begin()->second.sdp_video_format; | 
|  |  | 
|  | std::unique_ptr<VideoEncoderFactory> encoder_factory = | 
|  | CreateEncoderFactory(encoder_impl); | 
|  | if (!encoder_factory | 
|  | ->QueryCodecSupport(sdp_video_format, | 
|  | /*scalability_mode=*/std::nullopt) | 
|  | .is_supported) { | 
|  | RTC_LOG(LS_WARNING) << "No encoder for video format " | 
|  | << sdp_video_format.ToString(); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | std::string output_path = TestOutputPath(); | 
|  | VideoCodecTester::EncoderSettings encoder_settings; | 
|  | encoder_settings.pacing_settings.mode = | 
|  | encoder_impl == "builtin" ? PacingMode::kNoPacing : PacingMode::kRealTime; | 
|  | if (absl::GetFlag(FLAGS_dump_encoder_input)) { | 
|  | encoder_settings.encoder_input_base_path = output_path + "_enc_input"; | 
|  | } | 
|  | if (absl::GetFlag(FLAGS_dump_encoder_output)) { | 
|  | encoder_settings.encoder_output_base_path = output_path + "_enc_output"; | 
|  | } | 
|  |  | 
|  | return VideoCodecTester::RunEncodeTest(env, source_settings, | 
|  | encoder_factory.get(), | 
|  | encoder_settings, encoding_settings); | 
|  | } | 
|  |  | 
|  | class SpatialQualityTest : public ::testing::TestWithParam<std::tuple< | 
|  | /*codec_type=*/std::string, | 
|  | /*codec_impl=*/std::string, | 
|  | VideoInfo, | 
|  | std::tuple</*width=*/int, | 
|  | /*height=*/int, | 
|  | /*framerate_fps=*/double, | 
|  | /*bitrate_kbps=*/int, | 
|  | /*expected_min_psnr=*/double>>> { | 
|  | public: | 
|  | static std::string TestParamsToString( | 
|  | const ::testing::TestParamInfo<SpatialQualityTest::ParamType>& info) { | 
|  | auto [codec_type, codec_impl, video_info, coding_settings] = info.param; | 
|  | auto [width, height, framerate_fps, bitrate_kbps, psnr] = coding_settings; | 
|  | return std::string(codec_type + codec_impl + video_info.name + | 
|  | std::to_string(width) + "x" + std::to_string(height) + | 
|  | "p" + | 
|  | std::to_string(static_cast<int>(1000 * framerate_fps)) + | 
|  | "mhz" + std::to_string(bitrate_kbps) + "kbps"); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_P(SpatialQualityTest, SpatialQuality) { | 
|  | const Environment env = CreateEnvironment(); | 
|  | auto [codec_type, codec_impl, video_info, coding_settings] = GetParam(); | 
|  | auto [width, height, framerate_fps, bitrate_kbps, expected_min_psnr] = | 
|  | coding_settings; | 
|  | int duration_s = 10; | 
|  | int num_frames = duration_s * framerate_fps; | 
|  |  | 
|  | VideoSourceSettings source_settings = ToSourceSettings(video_info); | 
|  |  | 
|  | EncodingSettings encoding_settings = VideoCodecTester::CreateEncodingSettings( | 
|  | env, codec_type, /*scalability_name=*/"L1T1", width, height, | 
|  | {DataRate::KilobitsPerSec(bitrate_kbps)}, | 
|  | Frequency::Hertz(framerate_fps)); | 
|  |  | 
|  | std::map<uint32_t, EncodingSettings> frame_settings = | 
|  | VideoCodecTester::CreateFrameSettings(encoding_settings, num_frames); | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest( | 
|  | env, codec_impl, codec_impl, source_settings, frame_settings); | 
|  |  | 
|  | VideoCodecStats::Stream stream; | 
|  | if (stats != nullptr) { | 
|  | stream = stats->Aggregate(Filter{}); | 
|  | if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) { | 
|  | EXPECT_GE(stream.psnr.y.GetAverage(), expected_min_psnr); | 
|  | } | 
|  | } | 
|  |  | 
|  | stream.LogMetrics( | 
|  | GetGlobalMetricsLogger(), | 
|  | ::testing::UnitTest::GetInstance()->current_test_info()->name(), | 
|  | /*prefix=*/"", | 
|  | /*metadata=*/ | 
|  | {{"video_name", video_info.name}, | 
|  | {"codec_type", codec_type}, | 
|  | {"codec_impl", codec_impl}}); | 
|  |  | 
|  | if (absl::GetFlag(FLAGS_write_csv)) { | 
|  | stats->LogMetrics((StringBuilder() << TestOutputPath() << ".csv").str(), | 
|  | stats->Slice(Filter{}, /*merge=*/false), /*metadata=*/ | 
|  | {{"test_name", TestName()}}); | 
|  | } | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | All, | 
|  | SpatialQualityTest, | 
|  | Combine(Values("AV1", "VP9", "VP8", "H264", "H265"), | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | Values("builtin", "mediacodec"), | 
|  | #else | 
|  | Values("builtin"), | 
|  | #endif | 
|  | Values(kFourPeople_1280x720_30), | 
|  | Values(std::make_tuple(320, 180, 30, 32, 26), | 
|  | std::make_tuple(320, 180, 30, 64, 29), | 
|  | std::make_tuple(320, 180, 30, 128, 32), | 
|  | std::make_tuple(320, 180, 30, 256, 36), | 
|  | std::make_tuple(640, 360, 30, 128, 29), | 
|  | std::make_tuple(640, 360, 30, 256, 33), | 
|  | std::make_tuple(640, 360, 30, 384, 35), | 
|  | std::make_tuple(640, 360, 30, 512, 36), | 
|  | std::make_tuple(1280, 720, 30, 256, 30), | 
|  | std::make_tuple(1280, 720, 30, 512, 34), | 
|  | std::make_tuple(1280, 720, 30, 1024, 37), | 
|  | std::make_tuple(1280, 720, 30, 2048, 39))), | 
|  | SpatialQualityTest::TestParamsToString); | 
|  |  | 
|  | class BitrateAdaptationTest | 
|  | : public ::testing::TestWithParam< | 
|  | std::tuple</*codec_type=*/std::string, | 
|  | /*codec_impl=*/std::string, | 
|  | VideoInfo, | 
|  | std::pair</*bitrate_kbps=*/int, /*bitrate_kbps=*/int>>> { | 
|  | public: | 
|  | static std::string TestParamsToString( | 
|  | const ::testing::TestParamInfo<BitrateAdaptationTest::ParamType>& info) { | 
|  | auto [codec_type, codec_impl, video_info, bitrate_kbps] = info.param; | 
|  | return std::string(codec_type + codec_impl + video_info.name + | 
|  | std::to_string(bitrate_kbps.first) + "kbps" + | 
|  | std::to_string(bitrate_kbps.second) + "kbps"); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_P(BitrateAdaptationTest, BitrateAdaptation) { | 
|  | auto [codec_type, codec_impl, video_info, bitrate_kbps] = GetParam(); | 
|  | const Environment env = CreateEnvironment(); | 
|  |  | 
|  | int duration_s = 10;  // Duration of fixed rate interval. | 
|  | int num_frames = | 
|  | static_cast<int>(duration_s * video_info.framerate.hertz<double>()); | 
|  |  | 
|  | VideoSourceSettings source_settings = ToSourceSettings(video_info); | 
|  |  | 
|  | EncodingSettings encoding_settings = VideoCodecTester::CreateEncodingSettings( | 
|  | env, codec_type, /*scalability_name=*/"L1T1", | 
|  | /*width=*/640, /*height=*/360, | 
|  | {DataRate::KilobitsPerSec(bitrate_kbps.first)}, | 
|  | /*framerate=*/Frequency::Hertz(30)); | 
|  |  | 
|  | EncodingSettings encoding_settings2 = | 
|  | VideoCodecTester::CreateEncodingSettings( | 
|  | env, codec_type, /*scalability_name=*/"L1T1", | 
|  | /*width=*/640, /*height=*/360, | 
|  | {DataRate::KilobitsPerSec(bitrate_kbps.second)}, | 
|  | /*framerate=*/Frequency::Hertz(30)); | 
|  |  | 
|  | std::map<uint32_t, EncodingSettings> frame_settings = | 
|  | VideoCodecTester::CreateFrameSettings(encoding_settings, num_frames); | 
|  |  | 
|  | uint32_t timestamp_rtp = | 
|  | frame_settings.rbegin()->first + k90kHz / Frequency::Hertz(30); | 
|  | std::map<uint32_t, EncodingSettings> frame_settings2 = | 
|  | VideoCodecTester::CreateFrameSettings(encoding_settings2, num_frames, | 
|  | timestamp_rtp); | 
|  |  | 
|  | frame_settings.merge(frame_settings2); | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> stats = | 
|  | RunEncodeTest(env, codec_impl, source_settings, frame_settings); | 
|  |  | 
|  | VideoCodecStats::Stream stream; | 
|  | if (stats != nullptr) { | 
|  | stream = stats->Aggregate({.min_timestamp_rtp = timestamp_rtp}); | 
|  | if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) { | 
|  | EXPECT_NEAR(stream.bitrate_mismatch_pct.GetAverage(), 0, 10); | 
|  | EXPECT_NEAR(stream.framerate_mismatch_pct.GetAverage(), 0, 10); | 
|  | } | 
|  | } | 
|  |  | 
|  | stream.LogMetrics( | 
|  | GetGlobalMetricsLogger(), | 
|  | ::testing::UnitTest::GetInstance()->current_test_info()->name(), | 
|  | /*prefix=*/"", | 
|  | /*metadata=*/ | 
|  | {{"codec_type", codec_type}, | 
|  | {"codec_impl", codec_impl}, | 
|  | {"video_name", video_info.name}, | 
|  | {"rate_profile", std::to_string(bitrate_kbps.first) + "," + | 
|  | std::to_string(bitrate_kbps.second)}}); | 
|  |  | 
|  | if (absl::GetFlag(FLAGS_write_csv)) { | 
|  | stats->LogMetrics((StringBuilder() << TestOutputPath() << ".csv").str(), | 
|  | stats->Slice(Filter{}, /*merge=*/false), /*metadata=*/ | 
|  | {{"test_name", TestName()}}); | 
|  | } | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(All, | 
|  | BitrateAdaptationTest, | 
|  | Combine(Values("AV1", "VP9", "VP8", "H264", "H265"), | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | Values("builtin", "mediacodec"), | 
|  | #else | 
|  | Values("builtin"), | 
|  | #endif | 
|  | Values(kFourPeople_1280x720_30), | 
|  | Values(std::pair(1024, 512), | 
|  | std::pair(512, 1024))), | 
|  | BitrateAdaptationTest::TestParamsToString); | 
|  |  | 
|  | class FramerateAdaptationTest | 
|  | : public ::testing::TestWithParam<std::tuple</*codec_type=*/std::string, | 
|  | /*codec_impl=*/std::string, | 
|  | VideoInfo, | 
|  | std::pair<double, double>>> { | 
|  | public: | 
|  | static std::string TestParamsToString( | 
|  | const ::testing::TestParamInfo<FramerateAdaptationTest::ParamType>& | 
|  | info) { | 
|  | auto [codec_type, codec_impl, video_info, framerate_fps] = info.param; | 
|  | return std::string( | 
|  | codec_type + codec_impl + video_info.name + | 
|  | std::to_string(static_cast<int>(1000 * framerate_fps.first)) + "mhz" + | 
|  | std::to_string(static_cast<int>(1000 * framerate_fps.second)) + "mhz"); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_P(FramerateAdaptationTest, FramerateAdaptation) { | 
|  | auto [codec_type, codec_impl, video_info, framerate_fps] = GetParam(); | 
|  | const Environment env = CreateEnvironment(); | 
|  |  | 
|  | int duration_s = 10;  // Duration of fixed rate interval. | 
|  |  | 
|  | VideoSourceSettings source_settings = ToSourceSettings(video_info); | 
|  |  | 
|  | EncodingSettings encoding_settings = VideoCodecTester::CreateEncodingSettings( | 
|  | env, codec_type, /*scalability_name=*/"L1T1", | 
|  | /*width=*/640, /*height=*/360, | 
|  | /*bitrate=*/{DataRate::KilobitsPerSec(512)}, | 
|  | Frequency::Hertz(framerate_fps.first)); | 
|  |  | 
|  | EncodingSettings encoding_settings2 = | 
|  | VideoCodecTester::CreateEncodingSettings( | 
|  | env, codec_type, /*scalability_name=*/"L1T1", | 
|  | /*width=*/640, /*height=*/360, | 
|  | /*bitrate=*/{DataRate::KilobitsPerSec(512)}, | 
|  | Frequency::Hertz(framerate_fps.second)); | 
|  |  | 
|  | int num_frames = static_cast<int>(duration_s * framerate_fps.first); | 
|  | std::map<uint32_t, EncodingSettings> frame_settings = | 
|  | VideoCodecTester::CreateFrameSettings(encoding_settings, num_frames); | 
|  |  | 
|  | uint32_t timestamp_rtp = frame_settings.rbegin()->first + | 
|  | k90kHz / Frequency::Hertz(framerate_fps.first); | 
|  |  | 
|  | num_frames = static_cast<int>(duration_s * framerate_fps.second); | 
|  | std::map<uint32_t, EncodingSettings> frame_settings2 = | 
|  | VideoCodecTester::CreateFrameSettings(encoding_settings2, num_frames, | 
|  | timestamp_rtp); | 
|  |  | 
|  | frame_settings.merge(frame_settings2); | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> stats = | 
|  | RunEncodeTest(env, codec_impl, source_settings, frame_settings); | 
|  |  | 
|  | VideoCodecStats::Stream stream; | 
|  | if (stats != nullptr) { | 
|  | stream = stats->Aggregate({.min_timestamp_rtp = timestamp_rtp}); | 
|  | if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) { | 
|  | EXPECT_NEAR(stream.bitrate_mismatch_pct.GetAverage(), 0, 10); | 
|  | EXPECT_NEAR(stream.framerate_mismatch_pct.GetAverage(), 0, 10); | 
|  | } | 
|  | } | 
|  |  | 
|  | stream.LogMetrics( | 
|  | GetGlobalMetricsLogger(), | 
|  | ::testing::UnitTest::GetInstance()->current_test_info()->name(), | 
|  | /*prefix=*/"", | 
|  | /*metadata=*/ | 
|  | {{"codec_type", codec_type}, | 
|  | {"codec_impl", codec_impl}, | 
|  | {"video_name", video_info.name}, | 
|  | {"rate_profile", std::to_string(framerate_fps.first) + "," + | 
|  | std::to_string(framerate_fps.second)}}); | 
|  |  | 
|  | if (absl::GetFlag(FLAGS_write_csv)) { | 
|  | stats->LogMetrics((StringBuilder() << TestOutputPath() << ".csv").str(), | 
|  | stats->Slice(Filter{}, /*merge=*/false), /*metadata=*/ | 
|  | {{"test_name", TestName()}}); | 
|  | } | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(All, | 
|  | FramerateAdaptationTest, | 
|  | Combine(Values("AV1", "VP9", "VP8", "H264", "H265"), | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | Values("builtin", "mediacodec"), | 
|  | #else | 
|  | Values("builtin"), | 
|  | #endif | 
|  | Values(kFourPeople_1280x720_30), | 
|  | Values(std::pair(30, 15), std::pair(15, 30))), | 
|  | FramerateAdaptationTest::TestParamsToString); | 
|  |  | 
|  | TEST(VideoCodecTest, DISABLED_EncodeDecode) { | 
|  | const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr()); | 
|  |  | 
|  | VideoSourceSettings source_settings{ | 
|  | .file_path = absl::GetFlag(FLAGS_input_path), | 
|  | .resolution = {.width = absl::GetFlag(FLAGS_input_width), | 
|  | .height = absl::GetFlag(FLAGS_input_height)}, | 
|  | .framerate = | 
|  | Frequency::Hertz<double>(absl::GetFlag(FLAGS_input_framerate_fps))}; | 
|  |  | 
|  | std::vector<std::string> bitrate_str = absl::GetFlag(FLAGS_bitrate_kbps); | 
|  | std::vector<DataRate> bitrate; | 
|  | std::transform(bitrate_str.begin(), bitrate_str.end(), | 
|  | std::back_inserter(bitrate), [](const std::string& str) { | 
|  | return DataRate::KilobitsPerSec(std::stoi(str)); | 
|  | }); | 
|  |  | 
|  | Frequency framerate = Frequency::Hertz<double>( | 
|  | absl::GetFlag(FLAGS_framerate_fps) | 
|  | .value_or(absl::GetFlag(FLAGS_input_framerate_fps))); | 
|  |  | 
|  | EncodingSettings encoding_settings = VideoCodecTester::CreateEncodingSettings( | 
|  | env, CodecNameToCodecType(absl::GetFlag(FLAGS_encoder)), | 
|  | absl::GetFlag(FLAGS_scalability_mode), | 
|  | absl::GetFlag(FLAGS_width).value_or(absl::GetFlag(FLAGS_input_width)), | 
|  | absl::GetFlag(FLAGS_height).value_or(absl::GetFlag(FLAGS_input_height)), | 
|  | {bitrate}, framerate, absl::GetFlag(FLAGS_screencast), | 
|  | absl::GetFlag(FLAGS_frame_drop)); | 
|  |  | 
|  | int num_frames = absl::GetFlag(FLAGS_num_frames); | 
|  | int key_interval = absl::GetFlag(FLAGS_key_interval); | 
|  | uint32_t timestamp_rtp = 90000; | 
|  | std::map<uint32_t, EncodingSettings> frame_settings; | 
|  | for (int frame_num = 0; frame_num < num_frames; ++frame_num) { | 
|  | encoding_settings.keyframe = | 
|  | (key_interval > 0 && (frame_num % key_interval) == 0); | 
|  | frame_settings.emplace(timestamp_rtp, encoding_settings); | 
|  | timestamp_rtp += k90kHz / framerate; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<VideoCodecStats> stats; | 
|  | std::string decoder = absl::GetFlag(FLAGS_decoder); | 
|  | if (decoder == "null") { | 
|  | stats = | 
|  | RunEncodeTest(env, CodecNameToCodecImpl(absl::GetFlag(FLAGS_encoder)), | 
|  | source_settings, frame_settings); | 
|  | } else { | 
|  | // TODO(webrtc:14852): Pass encoder and decoder names directly, and update | 
|  | // logged test name (implies lossing history in the chromeperf dashboard). | 
|  | // Sync with changes in Stream::LogMetrics (see TODOs there). | 
|  | stats = RunEncodeDecodeTest( | 
|  | env, CodecNameToCodecImpl(absl::GetFlag(FLAGS_encoder)), | 
|  | CodecNameToCodecImpl(decoder), source_settings, frame_settings); | 
|  | } | 
|  | ASSERT_NE(nullptr, stats); | 
|  |  | 
|  | // Log unsliced metrics. | 
|  | VideoCodecStats::Stream stream = stats->Aggregate(Filter{}); | 
|  | stream.LogMetrics(GetGlobalMetricsLogger(), TestName(), /*prefix=*/"", | 
|  | /*metadata=*/{}); | 
|  |  | 
|  | // Log metrics sliced on spatial and temporal layer. | 
|  | ScalabilityMode scalability_mode = | 
|  | *ScalabilityModeFromString(absl::GetFlag(FLAGS_scalability_mode)); | 
|  | int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode); | 
|  | int num_temporal_layers = | 
|  | ScalabilityModeToNumTemporalLayers(scalability_mode); | 
|  | for (int sidx = 0; sidx < num_spatial_layers; ++sidx) { | 
|  | for (int tidx = 0; tidx < num_temporal_layers; ++tidx) { | 
|  | std::string metric_name_prefix = | 
|  | (StringBuilder() << "s" << sidx << "t" << tidx << "_").str(); | 
|  | stream = stats->Aggregate( | 
|  | {.layer_id = {{.spatial_idx = sidx, .temporal_idx = tidx}}}); | 
|  | stream.LogMetrics(GetGlobalMetricsLogger(), TestName(), | 
|  | metric_name_prefix, | 
|  | /*metadata=*/{}); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (absl::GetFlag(FLAGS_write_csv)) { | 
|  | stats->LogMetrics((StringBuilder() << TestOutputPath() << ".csv").str(), | 
|  | stats->Slice(Filter{}, /*merge=*/false), /*metadata=*/ | 
|  | {{"test_name", TestName()}}); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  |  | 
|  | }  // namespace webrtc |