summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc')
-rw-r--r--third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc811
1 files changed, 811 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
new file mode 100644
index 0000000000..1c8fe97e84
--- /dev/null
+++ b/third_party/libwebrtc/modules/video_coding/codecs/test/video_codec_test.cc
@@ -0,0 +1,811 @@
+/*
+ * 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 "api/video_codecs/video_codec.h"
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/flags/flag.h"
+#include "absl/functional/any_invocable.h"
+#include "api/test/create_video_codec_tester.h"
+#include "api/test/metrics/global_metrics_logger_and_exporter.h"
+#include "api/test/video_codec_tester.h"
+#include "api/test/videocodec_test_stats.h"
+#include "api/units/data_rate.h"
+#include "api/units/frequency.h"
+#include "api/video/encoded_image.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/resolution.h"
+#include "api/video/video_frame.h"
+#include "api/video_codecs/scalability_mode.h"
+#include "api/video_codecs/video_decoder.h"
+#include "api/video_codecs/video_encoder.h"
+#include "media/engine/internal_decoder_factory.h"
+#include "media/engine/internal_encoder_factory.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/svc/scalability_mode_util.h"
+#if defined(WEBRTC_ANDROID)
+#include "modules/video_coding/codecs/test/android_codec_factory_helper.h"
+#endif
+#include "rtc_base/logging.h"
+#include "test/gtest.h"
+#include "test/test_flags.h"
+#include "test/testsupport/file_utils.h"
+#include "test/testsupport/frame_reader.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+using ::testing::Combine;
+using ::testing::Values;
+using PacingMode = VideoCodecTester::PacingSettings::PacingMode;
+
+struct VideoInfo {
+ std::string name;
+ Resolution resolution;
+ Frequency framerate;
+};
+
+struct LayerId {
+ int spatial_idx;
+ int temporal_idx;
+
+ bool operator==(const LayerId& o) const {
+ return spatial_idx == o.spatial_idx && temporal_idx == o.temporal_idx;
+ }
+
+ bool operator<(const LayerId& o) const {
+ if (spatial_idx < o.spatial_idx)
+ return true;
+ if (spatial_idx == o.spatial_idx && temporal_idx < o.temporal_idx)
+ return true;
+ return false;
+ }
+};
+
+struct EncodingSettings {
+ ScalabilityMode scalability_mode;
+ struct LayerSettings {
+ Resolution resolution;
+ Frequency framerate;
+ DataRate bitrate;
+ };
+ std::map<LayerId, LayerSettings> layer_settings;
+
+ bool IsSameSettings(const EncodingSettings& other) const {
+ if (scalability_mode != other.scalability_mode) {
+ return false;
+ }
+
+ for (auto [layer_id, layer] : layer_settings) {
+ const auto& other_layer = other.layer_settings.at(layer_id);
+ if (layer.resolution != other_layer.resolution) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool IsSameRate(const EncodingSettings& other) const {
+ for (auto [layer_id, layer] : layer_settings) {
+ const auto& other_layer = other.layer_settings.at(layer_id);
+ if (layer.bitrate != other_layer.bitrate ||
+ layer.framerate != other_layer.framerate) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+const VideoInfo kFourPeople_1280x720_30 = {
+ .name = "FourPeople_1280x720_30",
+ .resolution = {.width = 1280, .height = 720},
+ .framerate = Frequency::Hertz(30)};
+
+class TestRawVideoSource : public VideoCodecTester::RawVideoSource {
+ public:
+ static constexpr Frequency k90kHz = Frequency::Hertz(90000);
+
+ TestRawVideoSource(VideoInfo video_info,
+ const std::map<int, EncodingSettings>& frame_settings,
+ int num_frames)
+ : video_info_(video_info),
+ frame_settings_(frame_settings),
+ num_frames_(num_frames),
+ frame_num_(0),
+ // Start with non-zero timestamp to force using frame RTP timestamps in
+ // IvfFrameWriter.
+ timestamp_rtp_(90000) {
+ // Ensure settings for the first frame are provided.
+ RTC_CHECK_GT(frame_settings_.size(), 0u);
+ RTC_CHECK_EQ(frame_settings_.begin()->first, 0);
+
+ frame_reader_ = CreateYuvFrameReader(
+ ResourcePath(video_info_.name, "yuv"), video_info_.resolution,
+ YuvFrameReaderImpl::RepeatMode::kPingPong);
+ RTC_CHECK(frame_reader_);
+ }
+
+ // Pulls next frame. Frame RTP timestamp is set accordingly to
+ // `EncodingSettings::framerate`.
+ absl::optional<VideoFrame> PullFrame() override {
+ if (frame_num_ >= num_frames_) {
+ return absl::nullopt; // End of stream.
+ }
+
+ const EncodingSettings& encoding_settings =
+ std::prev(frame_settings_.upper_bound(frame_num_))->second;
+
+ Resolution resolution =
+ encoding_settings.layer_settings.begin()->second.resolution;
+ Frequency framerate =
+ encoding_settings.layer_settings.begin()->second.framerate;
+
+ int pulled_frame;
+ auto buffer = frame_reader_->PullFrame(
+ &pulled_frame, resolution,
+ {.num = static_cast<int>(framerate.millihertz()),
+ .den = static_cast<int>(video_info_.framerate.millihertz())});
+ RTC_CHECK(buffer) << "Cannot pull frame " << frame_num_;
+
+ auto frame = VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_timestamp_rtp(timestamp_rtp_)
+ .set_timestamp_us((timestamp_rtp_ / k90kHz).us())
+ .build();
+
+ pulled_frames_[timestamp_rtp_] = pulled_frame;
+ timestamp_rtp_ += k90kHz / framerate;
+ ++frame_num_;
+
+ return frame;
+ }
+
+ // Reads frame specified by `timestamp_rtp`, scales it to `resolution` and
+ // returns. Frame with the given `timestamp_rtp` is expected to be pulled
+ // before.
+ VideoFrame GetFrame(uint32_t timestamp_rtp, Resolution resolution) override {
+ RTC_CHECK(pulled_frames_.find(timestamp_rtp) != pulled_frames_.end())
+ << "Frame with RTP timestamp " << timestamp_rtp
+ << " was not pulled before";
+ auto buffer =
+ frame_reader_->ReadFrame(pulled_frames_[timestamp_rtp], resolution);
+ return VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_timestamp_rtp(timestamp_rtp)
+ .build();
+ }
+
+ protected:
+ VideoInfo video_info_;
+ std::unique_ptr<FrameReader> frame_reader_;
+ const std::map<int, EncodingSettings>& frame_settings_;
+ int num_frames_;
+ int frame_num_;
+ uint32_t timestamp_rtp_;
+ std::map<uint32_t, int> pulled_frames_;
+};
+
+class TestEncoder : public VideoCodecTester::Encoder,
+ public EncodedImageCallback {
+ public:
+ TestEncoder(std::unique_ptr<VideoEncoder> encoder,
+ const std::string codec_type,
+ const std::map<int, EncodingSettings>& frame_settings)
+ : encoder_(std::move(encoder)),
+ codec_type_(codec_type),
+ frame_settings_(frame_settings),
+ frame_num_(0) {
+ // Ensure settings for the first frame is provided.
+ RTC_CHECK_GT(frame_settings_.size(), 0u);
+ RTC_CHECK_EQ(frame_settings_.begin()->first, 0);
+
+ encoder_->RegisterEncodeCompleteCallback(this);
+ }
+
+ void Initialize() override {
+ const EncodingSettings& first_frame_settings = frame_settings_.at(0);
+ Configure(first_frame_settings);
+ SetRates(first_frame_settings);
+ }
+
+ void Encode(const VideoFrame& frame, EncodeCallback callback) override {
+ {
+ MutexLock lock(&mutex_);
+ callbacks_[frame.timestamp()] = std::move(callback);
+ }
+
+ if (auto fs = frame_settings_.find(frame_num_);
+ fs != frame_settings_.begin() && fs != frame_settings_.end()) {
+ if (!fs->second.IsSameSettings(std::prev(fs)->second)) {
+ Configure(fs->second);
+ } else if (!fs->second.IsSameRate(std::prev(fs)->second)) {
+ SetRates(fs->second);
+ }
+ }
+
+ encoder_->Encode(frame, nullptr);
+ ++frame_num_;
+ }
+
+ void Flush() override {
+ // TODO(webrtc:14852): For codecs which buffer frames we need a to
+ // flush them to get last frames. Add such functionality to VideoEncoder
+ // API. On Android it will map directly to `MediaCodec.flush()`.
+ encoder_->Release();
+ }
+
+ VideoEncoder* encoder() { return encoder_.get(); }
+
+ protected:
+ Result OnEncodedImage(const EncodedImage& encoded_image,
+ const CodecSpecificInfo* codec_specific_info) override {
+ MutexLock lock(&mutex_);
+ auto cb = callbacks_.find(encoded_image.RtpTimestamp());
+ RTC_CHECK(cb != callbacks_.end());
+ cb->second(encoded_image);
+
+ callbacks_.erase(callbacks_.begin(), cb);
+ return Result(Result::Error::OK);
+ }
+
+ void Configure(const EncodingSettings& es) {
+ VideoCodec vc;
+ const EncodingSettings::LayerSettings& layer_settings =
+ es.layer_settings.begin()->second;
+ vc.width = layer_settings.resolution.width;
+ vc.height = layer_settings.resolution.height;
+ const DataRate& bitrate = layer_settings.bitrate;
+ vc.startBitrate = bitrate.kbps();
+ vc.maxBitrate = bitrate.kbps();
+ vc.minBitrate = 0;
+ vc.maxFramerate = static_cast<uint32_t>(layer_settings.framerate.hertz());
+ vc.active = true;
+ vc.qpMax = 63;
+ vc.numberOfSimulcastStreams = 0;
+ vc.mode = webrtc::VideoCodecMode::kRealtimeVideo;
+ vc.SetFrameDropEnabled(true);
+ vc.SetScalabilityMode(es.scalability_mode);
+
+ vc.codecType = PayloadStringToCodecType(codec_type_);
+ if (vc.codecType == kVideoCodecVP8) {
+ *(vc.VP8()) = VideoEncoder::GetDefaultVp8Settings();
+ } else if (vc.codecType == kVideoCodecVP9) {
+ *(vc.VP9()) = VideoEncoder::GetDefaultVp9Settings();
+ } else if (vc.codecType == kVideoCodecH264) {
+ *(vc.H264()) = VideoEncoder::GetDefaultH264Settings();
+ }
+
+ VideoEncoder::Settings ves(
+ VideoEncoder::Capabilities(/*loss_notification=*/false),
+ /*number_of_cores=*/1,
+ /*max_payload_size=*/1440);
+
+ int result = encoder_->InitEncode(&vc, ves);
+ ASSERT_EQ(result, WEBRTC_VIDEO_CODEC_OK);
+
+ SetRates(es);
+ }
+
+ void SetRates(const EncodingSettings& es) {
+ VideoEncoder::RateControlParameters rc;
+ int num_spatial_layers =
+ ScalabilityModeToNumSpatialLayers(es.scalability_mode);
+ int num_temporal_layers =
+ ScalabilityModeToNumSpatialLayers(es.scalability_mode);
+ for (int sidx = 0; sidx < num_spatial_layers; ++sidx) {
+ for (int tidx = 0; tidx < num_temporal_layers; ++tidx) {
+ auto layer_settings =
+ es.layer_settings.find({.spatial_idx = sidx, .temporal_idx = tidx});
+ RTC_CHECK(layer_settings != es.layer_settings.end())
+ << "Bitrate for layer S=" << sidx << " T=" << tidx << " is not set";
+ rc.bitrate.SetBitrate(sidx, tidx, layer_settings->second.bitrate.bps());
+ }
+ }
+
+ rc.framerate_fps =
+ es.layer_settings.begin()->second.framerate.millihertz() / 1000.0;
+ encoder_->SetRates(rc);
+ }
+
+ std::unique_ptr<VideoEncoder> encoder_;
+ const std::string codec_type_;
+ const std::map<int, EncodingSettings>& frame_settings_;
+ int frame_num_;
+ std::map<uint32_t, EncodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
+ Mutex mutex_;
+};
+
+class TestDecoder : public VideoCodecTester::Decoder,
+ public DecodedImageCallback {
+ public:
+ TestDecoder(std::unique_ptr<VideoDecoder> decoder,
+ const std::string codec_type)
+ : decoder_(std::move(decoder)), codec_type_(codec_type) {
+ decoder_->RegisterDecodeCompleteCallback(this);
+ }
+
+ void Initialize() override {
+ VideoDecoder::Settings ds;
+ ds.set_codec_type(PayloadStringToCodecType(codec_type_));
+ ds.set_number_of_cores(1);
+ ds.set_max_render_resolution({1280, 720});
+
+ bool result = decoder_->Configure(ds);
+ ASSERT_TRUE(result);
+ }
+
+ void Decode(const EncodedImage& frame, DecodeCallback callback) override {
+ {
+ MutexLock lock(&mutex_);
+ callbacks_[frame.RtpTimestamp()] = std::move(callback);
+ }
+
+ decoder_->Decode(frame, /*render_time_ms=*/0);
+ }
+
+ void Flush() override {
+ // TODO(webrtc:14852): For codecs which buffer frames we need a to
+ // flush them to get last frames. Add such functionality to VideoDecoder
+ // API. On Android it will map directly to `MediaCodec.flush()`.
+ decoder_->Release();
+ }
+
+ VideoDecoder* decoder() { return decoder_.get(); }
+
+ protected:
+ int Decoded(VideoFrame& decoded_frame) override {
+ MutexLock lock(&mutex_);
+ auto cb = callbacks_.find(decoded_frame.timestamp());
+ RTC_CHECK(cb != callbacks_.end());
+ cb->second(decoded_frame);
+
+ callbacks_.erase(callbacks_.begin(), cb);
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ std::unique_ptr<VideoDecoder> decoder_;
+ const std::string codec_type_;
+ std::map<uint32_t, DecodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
+ Mutex mutex_;
+};
+
+std::unique_ptr<TestRawVideoSource> CreateVideoSource(
+ const VideoInfo& video,
+ const std::map<int, EncodingSettings>& frame_settings,
+ int num_frames) {
+ return std::make_unique<TestRawVideoSource>(video, frame_settings,
+ num_frames);
+}
+
+std::unique_ptr<TestEncoder> CreateEncoder(
+ std::string type,
+ std::string impl,
+ const std::map<int, EncodingSettings>& frame_settings) {
+ std::unique_ptr<VideoEncoderFactory> factory;
+ if (impl == "builtin") {
+ factory = std::make_unique<InternalEncoderFactory>();
+ } else if (impl == "mediacodec") {
+#if defined(WEBRTC_ANDROID)
+ InitializeAndroidObjects();
+ factory = CreateAndroidEncoderFactory();
+#endif
+ }
+ std::unique_ptr<VideoEncoder> encoder =
+ factory->CreateVideoEncoder(SdpVideoFormat(type));
+ if (encoder == nullptr) {
+ return nullptr;
+ }
+ return std::make_unique<TestEncoder>(std::move(encoder), type,
+ frame_settings);
+}
+
+std::unique_ptr<TestDecoder> CreateDecoder(std::string type, std::string impl) {
+ std::unique_ptr<VideoDecoderFactory> factory;
+ if (impl == "builtin") {
+ factory = std::make_unique<InternalDecoderFactory>();
+ } else if (impl == "mediacodec") {
+#if defined(WEBRTC_ANDROID)
+ InitializeAndroidObjects();
+ factory = CreateAndroidDecoderFactory();
+#endif
+ }
+ std::unique_ptr<VideoDecoder> decoder =
+ factory->CreateVideoDecoder(SdpVideoFormat(type));
+ if (decoder == nullptr) {
+ return nullptr;
+ }
+ return std::make_unique<TestDecoder>(std::move(decoder), type);
+}
+
+void SetTargetRates(const std::map<int, EncodingSettings>& frame_settings,
+ std::vector<VideoCodecStats::Frame>& frames) {
+ for (VideoCodecStats::Frame& f : frames) {
+ const EncodingSettings& encoding_settings =
+ std::prev(frame_settings.upper_bound(f.frame_num))->second;
+ LayerId layer_id = {.spatial_idx = f.spatial_idx,
+ .temporal_idx = f.temporal_idx};
+ RTC_CHECK(encoding_settings.layer_settings.find(layer_id) !=
+ encoding_settings.layer_settings.end())
+ << "Frame frame_num=" << f.frame_num
+ << " belongs to spatial_idx=" << f.spatial_idx
+ << " temporal_idx=" << f.temporal_idx
+ << " but settings for this layer are not provided.";
+ const EncodingSettings::LayerSettings& layer_settings =
+ encoding_settings.layer_settings.at(layer_id);
+ f.target_bitrate = layer_settings.bitrate;
+ f.target_framerate = layer_settings.framerate;
+ }
+}
+
+std::string TestOutputPath() {
+ std::string output_path =
+ OutputPath() +
+ ::testing::UnitTest::GetInstance()->current_test_info()->name();
+ 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(
+ std::string codec_type,
+ std::string codec_impl,
+ const VideoInfo& video_info,
+ const std::map<int, EncodingSettings>& frame_settings,
+ int num_frames,
+ bool save_codec_input,
+ bool save_codec_output) {
+ std::unique_ptr<TestRawVideoSource> video_source =
+ CreateVideoSource(video_info, frame_settings, num_frames);
+
+ std::unique_ptr<TestEncoder> encoder =
+ CreateEncoder(codec_type, codec_impl, frame_settings);
+ if (encoder == nullptr) {
+ return nullptr;
+ }
+
+ std::unique_ptr<TestDecoder> decoder = CreateDecoder(codec_type, codec_impl);
+ if (decoder == nullptr) {
+ // If platform decoder is not available try built-in one.
+ if (codec_impl == "builtin") {
+ return nullptr;
+ }
+
+ decoder = CreateDecoder(codec_type, "builtin");
+ if (decoder == nullptr) {
+ return nullptr;
+ }
+ }
+
+ RTC_LOG(LS_INFO) << "Encoder implementation: "
+ << encoder->encoder()->GetEncoderInfo().implementation_name;
+ RTC_LOG(LS_INFO) << "Decoder implementation: "
+ << decoder->decoder()->GetDecoderInfo().implementation_name;
+
+ VideoCodecTester::EncoderSettings encoder_settings;
+ encoder_settings.pacing.mode =
+ encoder->encoder()->GetEncoderInfo().is_hardware_accelerated
+ ? PacingMode::kRealTime
+ : PacingMode::kNoPacing;
+
+ VideoCodecTester::DecoderSettings decoder_settings;
+ decoder_settings.pacing.mode =
+ decoder->decoder()->GetDecoderInfo().is_hardware_accelerated
+ ? PacingMode::kRealTime
+ : PacingMode::kNoPacing;
+
+ std::string output_path = TestOutputPath();
+ if (save_codec_input) {
+ encoder_settings.encoder_input_base_path = output_path + "_enc_input";
+ decoder_settings.decoder_input_base_path = output_path + "_dec_input";
+ }
+ if (save_codec_output) {
+ encoder_settings.encoder_output_base_path = output_path + "_enc_output";
+ decoder_settings.decoder_output_base_path = output_path + "_dec_output";
+ }
+
+ std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
+ return tester->RunEncodeDecodeTest(video_source.get(), encoder.get(),
+ decoder.get(), encoder_settings,
+ decoder_settings);
+}
+
+std::unique_ptr<VideoCodecStats> RunEncodeTest(
+ std::string codec_type,
+ std::string codec_impl,
+ const VideoInfo& video_info,
+ const std::map<int, EncodingSettings>& frame_settings,
+ int num_frames,
+ bool save_codec_input,
+ bool save_codec_output) {
+ std::unique_ptr<TestRawVideoSource> video_source =
+ CreateVideoSource(video_info, frame_settings, num_frames);
+
+ std::unique_ptr<TestEncoder> encoder =
+ CreateEncoder(codec_type, codec_impl, frame_settings);
+ if (encoder == nullptr) {
+ return nullptr;
+ }
+
+ RTC_LOG(LS_INFO) << "Encoder implementation: "
+ << encoder->encoder()->GetEncoderInfo().implementation_name;
+
+ VideoCodecTester::EncoderSettings encoder_settings;
+ encoder_settings.pacing.mode =
+ encoder->encoder()->GetEncoderInfo().is_hardware_accelerated
+ ? PacingMode::kRealTime
+ : PacingMode::kNoPacing;
+
+ std::string output_path = TestOutputPath();
+ if (save_codec_input) {
+ encoder_settings.encoder_input_base_path = output_path + "_enc_input";
+ }
+ if (save_codec_output) {
+ encoder_settings.encoder_output_base_path = output_path + "_enc_output";
+ }
+
+ std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
+ return tester->RunEncodeTest(video_source.get(), encoder.get(),
+ encoder_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,
+ /*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) {
+ auto [codec_type, codec_impl, video_info, coding_settings] = GetParam();
+ auto [width, height, framerate_fps, bitrate_kbps, psnr] = coding_settings;
+
+ std::map<int, EncodingSettings> frame_settings = {
+ {0,
+ {.scalability_mode = ScalabilityMode::kL1T1,
+ .layer_settings = {
+ {LayerId{.spatial_idx = 0, .temporal_idx = 0},
+ {.resolution = {.width = width, .height = height},
+ .framerate = Frequency::MilliHertz(1000 * framerate_fps),
+ .bitrate = DataRate::KilobitsPerSec(bitrate_kbps)}}}}}};
+
+ int duration_s = 10;
+ int num_frames = duration_s * framerate_fps;
+
+ std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest(
+ codec_type, codec_impl, video_info, frame_settings, num_frames,
+ /*save_codec_input=*/false, /*save_codec_output=*/false);
+
+ VideoCodecStats::Stream stream;
+ if (stats != nullptr) {
+ std::vector<VideoCodecStats::Frame> frames = stats->Slice();
+ SetTargetRates(frame_settings, frames);
+ stream = stats->Aggregate(frames);
+ if (absl::GetFlag(FLAGS_webrtc_quick_perf_test)) {
+ EXPECT_GE(stream.psnr.y.GetAverage(), psnr);
+ }
+ }
+
+ stream.LogMetrics(
+ GetGlobalMetricsLogger(),
+ ::testing::UnitTest::GetInstance()->current_test_info()->name(),
+ /*metadata=*/
+ {{"codec_type", codec_type},
+ {"codec_impl", codec_impl},
+ {"video_name", video_info.name}});
+}
+
+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, 28),
+ std::make_tuple(320, 180, 30, 64, 30),
+ std::make_tuple(320, 180, 30, 128, 33),
+ std::make_tuple(320, 180, 30, 256, 36),
+ std::make_tuple(640, 360, 30, 128, 31),
+ 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, 32),
+ 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();
+
+ int duration_s = 10; // Duration of fixed rate interval.
+ int first_frame = duration_s * video_info.framerate.millihertz() / 1000;
+ int num_frames = 2 * duration_s * video_info.framerate.millihertz() / 1000;
+
+ std::map<int, EncodingSettings> frame_settings = {
+ {0,
+ {.layer_settings = {{LayerId{.spatial_idx = 0, .temporal_idx = 0},
+ {.resolution = {.width = 640, .height = 360},
+ .framerate = video_info.framerate,
+ .bitrate = DataRate::KilobitsPerSec(
+ bitrate_kbps.first)}}}}},
+ {first_frame,
+ {.layer_settings = {
+ {LayerId{.spatial_idx = 0, .temporal_idx = 0},
+ {.resolution = {.width = 640, .height = 360},
+ .framerate = video_info.framerate,
+ .bitrate = DataRate::KilobitsPerSec(bitrate_kbps.second)}}}}}};
+
+ std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
+ codec_type, codec_impl, video_info, frame_settings, num_frames,
+ /*save_codec_input=*/false, /*save_codec_output=*/false);
+
+ VideoCodecStats::Stream stream;
+ if (stats != nullptr) {
+ std::vector<VideoCodecStats::Frame> frames =
+ stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});
+ SetTargetRates(frame_settings, frames);
+ stream = stats->Aggregate(frames);
+ 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(),
+ /*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)}});
+}
+
+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();
+
+ int duration_s = 10; // Duration of fixed rate interval.
+ int first_frame = static_cast<int>(duration_s * framerate_fps.first);
+ int num_frames = static_cast<int>(
+ duration_s * (framerate_fps.first + framerate_fps.second));
+
+ std::map<int, EncodingSettings> frame_settings = {
+ {0,
+ {.layer_settings = {{LayerId{.spatial_idx = 0, .temporal_idx = 0},
+ {.resolution = {.width = 640, .height = 360},
+ .framerate = Frequency::MilliHertz(
+ 1000 * framerate_fps.first),
+ .bitrate = DataRate::KilobitsPerSec(512)}}}}},
+ {first_frame,
+ {.layer_settings = {
+ {LayerId{.spatial_idx = 0, .temporal_idx = 0},
+ {.resolution = {.width = 640, .height = 360},
+ .framerate = Frequency::MilliHertz(1000 * framerate_fps.second),
+ .bitrate = DataRate::KilobitsPerSec(512)}}}}}};
+
+ std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
+ codec_type, codec_impl, video_info, frame_settings, num_frames,
+ /*save_codec_input=*/false, /*save_codec_output=*/false);
+
+ VideoCodecStats::Stream stream;
+ if (stats != nullptr) {
+ std::vector<VideoCodecStats::Frame> frames =
+ stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});
+ SetTargetRates(frame_settings, frames);
+ stream = stats->Aggregate(frames);
+ 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(),
+ /*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)}});
+}
+
+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);
+
+} // namespace test
+
+} // namespace webrtc