Migrate video_quality_analysis on new perf metrics logger API

Bug: b/246095034
Change-Id: Ibdaae04ccd874024ce8db5c1f015479c713264a0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/276624
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38198}
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index bb4b8c0..21dc736 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -110,12 +110,14 @@
     "../api:array_view",
     "../api:make_ref_counted",
     "../api:scoped_refptr",
+    "../api/numerics",
+    "../api/test/metrics:metric",
+    "../api/test/metrics:metrics_logger",
     "../api/video:video_frame",
     "../api/video:video_rtp_headers",
     "../common_video",
     "../rtc_base:checks",
     "../rtc_base:logging",
-    "../test:perf_test",
     "//third_party/libyuv",
   ]
   absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
@@ -138,6 +140,7 @@
     ":video_quality_analysis",
     "../api:make_ref_counted",
     "../api:scoped_refptr",
+    "../api/test/metrics:global_metrics_logger_and_exporter",
     "../rtc_base:stringutils",
     "../test:perf_test",
     "//third_party/abseil-cpp/absl/flags:flag",
@@ -526,6 +529,8 @@
         ":video_file_writer",
         ":video_quality_analysis",
         "../api:scoped_refptr",
+        "../api/test/metrics:metric",
+        "../api/test/metrics:metrics_logger",
         "../api/video:video_frame",
         "../api/video:video_rtp_headers",
         "../common_video",
@@ -533,6 +538,7 @@
         "../rtc_base:checks",
         "../rtc_base:null_socket_server",
         "../rtc_base:threading",
+        "../system_wrappers",
         "../test:fileutils",
         "../test:test_main",
         "../test:test_support",
diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc
index 70af305..2224f48 100644
--- a/rtc_tools/frame_analyzer/frame_analyzer.cc
+++ b/rtc_tools/frame_analyzer/frame_analyzer.cc
@@ -19,6 +19,7 @@
 #include "absl/flags/parse.h"
 #include "absl/strings/match.h"
 #include "api/scoped_refptr.h"
+#include "api/test/metrics/global_metrics_logger_and_exporter.h"
 #include "rtc_base/strings/string_builder.h"
 #include "rtc_tools/frame_analyzer/video_color_aligner.h"
 #include "rtc_tools/frame_analyzer/video_geometry_aligner.h"
@@ -159,7 +160,8 @@
   results.decode_errors_ref = 0;
   results.decode_errors_test = 0;
 
-  webrtc::test::PrintAnalysisResults(absl::GetFlag(FLAGS_label), &results);
+  webrtc::test::PrintAnalysisResults(absl::GetFlag(FLAGS_label), results,
+                                     *webrtc::test::GetGlobalMetricsLogger());
 
   std::string chartjson_result_file =
       absl::GetFlag(FLAGS_chartjson_result_file);
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis.cc b/rtc_tools/frame_analyzer/video_quality_analysis.cc
index 6989f3f..1832438 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis.cc
+++ b/rtc_tools/frame_analyzer/video_quality_analysis.cc
@@ -14,9 +14,10 @@
 #include <array>
 #include <cstddef>
 
+#include "api/numerics/samples_stats_counter.h"
+#include "api/test/metrics/metric.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
-#include "test/testsupport/perf_test.h"
 #include "third_party/libyuv/include/libyuv/compare.h"
 
 namespace webrtc {
@@ -117,39 +118,42 @@
   return static_cast<int>(number_ref_frames - clusters.size());
 }
 
-void PrintAnalysisResults(const std::string& label, ResultsContainer* results) {
-  PrintAnalysisResults(stdout, label, results);
-}
+void PrintAnalysisResults(const std::string& label,
+                          ResultsContainer& results,
+                          MetricsLogger& logger) {
+  if (results.frames.size() > 0u) {
+    logger.LogSingleValueMetric("Unique_frames_count", label,
+                                results.frames.size(), Unit::kUnitless,
+                                ImprovementDirection::kNeitherIsBetter);
 
-void PrintAnalysisResults(FILE* output,
-                          const std::string& label,
-                          ResultsContainer* results) {
-  SetPerfResultsOutput(output);
-
-  if (results->frames.size() > 0u) {
-    PrintResult("Unique_frames_count", "", label, results->frames.size(),
-                "score", false);
-
-    std::vector<double> psnr_values;
-    std::vector<double> ssim_values;
-    for (const auto& frame : results->frames) {
-      psnr_values.push_back(frame.psnr_value);
-      ssim_values.push_back(frame.ssim_value);
+    SamplesStatsCounter psnr_values;
+    SamplesStatsCounter ssim_values;
+    for (const auto& frame : results.frames) {
+      psnr_values.AddSample(frame.psnr_value);
+      ssim_values.AddSample(frame.ssim_value);
     }
 
-    PrintResultList("PSNR", "", label, psnr_values, "dB", false);
-    PrintResultList("SSIM", "", label, ssim_values, "score", false);
+    logger.LogMetric("PSNR_dB", label, psnr_values, Unit::kUnitless,
+                     ImprovementDirection::kNeitherIsBetter);
+    logger.LogMetric("SSIM", label, ssim_values, Unit::kUnitless,
+                     ImprovementDirection::kNeitherIsBetter);
   }
 
-  PrintResult("Max_repeated", "", label, results->max_repeated_frames, "",
-              false);
-  PrintResult("Max_skipped", "", label, results->max_skipped_frames, "", false);
-  PrintResult("Total_skipped", "", label, results->total_skipped_frames, "",
-              false);
-  PrintResult("Decode_errors_reference", "", label, results->decode_errors_ref,
-              "", false);
-  PrintResult("Decode_errors_test", "", label, results->decode_errors_test, "",
-              false);
+  logger.LogSingleValueMetric("Max_repeated", label,
+                              results.max_repeated_frames, Unit::kUnitless,
+                              ImprovementDirection::kNeitherIsBetter);
+  logger.LogSingleValueMetric("Max_skipped", label, results.max_skipped_frames,
+                              Unit::kUnitless,
+                              ImprovementDirection::kNeitherIsBetter);
+  logger.LogSingleValueMetric("Total_skipped", label,
+                              results.total_skipped_frames, Unit::kUnitless,
+                              ImprovementDirection::kNeitherIsBetter);
+  logger.LogSingleValueMetric("Decode_errors_reference", label,
+                              results.decode_errors_ref, Unit::kUnitless,
+                              ImprovementDirection::kNeitherIsBetter);
+  logger.LogSingleValueMetric("Decode_errors_test", label,
+                              results.decode_errors_test, Unit::kUnitless,
+                              ImprovementDirection::kNeitherIsBetter);
 }
 
 }  // namespace test
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis.h b/rtc_tools/frame_analyzer/video_quality_analysis.h
index c5f3cb6..701b585 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis.h
+++ b/rtc_tools/frame_analyzer/video_quality_analysis.h
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "api/scoped_refptr.h"
+#include "api/test/metrics/metrics_logger.h"
 #include "api/video/video_frame_buffer.h"
 #include "rtc_tools/video_file_reader.h"
 
@@ -69,12 +70,9 @@
 // Prints the result from the analysis in Chromium performance
 // numbers compatible format to stdout. If the results object contains no frames
 // no output will be written.
-void PrintAnalysisResults(const std::string& label, ResultsContainer* results);
-
-// Similar to the above, but will print to the specified file handle.
-void PrintAnalysisResults(FILE* output,
-                          const std::string& label,
-                          ResultsContainer* results);
+void PrintAnalysisResults(const std::string& label,
+                          ResultsContainer& results,
+                          MetricsLogger& logger);
 
 struct Cluster {
   // Corresponding reference frame index for this cluster.
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc b/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
index 826cca0..d0227fb 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
+++ b/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
@@ -7,102 +7,133 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-
-// This test doesn't actually verify the output since it's just printed
-// to stdout by void functions, but it's still useful as it executes the code.
-
 #include "rtc_tools/frame_analyzer/video_quality_analysis.h"
 
-#include <stdio.h>
-
-#include <cstddef>
-#include <fstream>
 #include <string>
+#include <vector>
 
+#include "api/test/metrics/metric.h"
+#include "api/test/metrics/metrics_logger.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gmock.h"
 #include "test/gtest.h"
 #include "test/testsupport/file_utils.h"
 
 namespace webrtc {
 namespace test {
-
 namespace {
 
-void VerifyLogOutput(const std::string& log_filename,
-                     const std::vector<std::string>& expected_out) {
-  std::ifstream logf(log_filename);
-  std::string line;
+using ::testing::IsSupersetOf;
 
-  std::size_t i;
-  for (i = 0; i < expected_out.size() && getline(logf, line); ++i) {
-    ASSERT_EQ(expected_out.at(i), line);
-  }
-  ASSERT_TRUE(i == expected_out.size()) << "Not enough input data";
-}
-
-}  // namespace
-
-// Setup a log file to write the output to instead of stdout because we don't
-// want those numbers to be picked up as perf numbers.
-class VideoQualityAnalysisTest : public ::testing::Test {
- protected:
-  void SetUp() {
-    std::string log_filename = TempFilename(webrtc::test::OutputPath(),
-                                            "VideoQualityAnalysisTest.log");
-    logfile_ = fopen(log_filename.c_str(), "w");
-    ASSERT_TRUE(logfile_ != NULL);
-  }
-  void TearDown() { ASSERT_EQ(0, fclose(logfile_)); }
-  FILE* logfile_;
+// Metric fields to assert on
+struct MetricValidationInfo {
+  std::string test_case;
+  std::string name;
+  Unit unit;
+  ImprovementDirection improvement_direction;
+  double mean;
 };
 
-TEST_F(VideoQualityAnalysisTest, PrintAnalysisResultsEmpty) {
-  ResultsContainer result;
-  PrintAnalysisResults(logfile_, "Empty", &result);
+bool operator==(const MetricValidationInfo& a, const MetricValidationInfo& b) {
+  return a.name == b.name && a.test_case == b.test_case && a.unit == b.unit &&
+         a.improvement_direction == b.improvement_direction;
 }
 
-TEST_F(VideoQualityAnalysisTest, PrintAnalysisResultsOneFrame) {
+std::ostream& operator<<(std::ostream& os, const MetricValidationInfo& m) {
+  os << "{ test_case=" << m.test_case << "; name=" << m.name
+     << "; unit=" << test::ToString(m.unit)
+     << "; improvement_direction=" << test::ToString(m.improvement_direction)
+     << " }";
+  return os;
+}
+
+std::vector<MetricValidationInfo> ToValidationInfo(
+    const std::vector<Metric>& metrics) {
+  std::vector<MetricValidationInfo> out;
+  for (const Metric& m : metrics) {
+    out.push_back(
+        MetricValidationInfo{.test_case = m.test_case,
+                             .name = m.name,
+                             .unit = m.unit,
+                             .improvement_direction = m.improvement_direction,
+                             .mean = *m.stats.mean});
+  }
+  return out;
+}
+
+TEST(VideoQualityAnalysisTest, PrintAnalysisResultsEmpty) {
+  ResultsContainer result;
+  DefaultMetricsLogger logger(Clock::GetRealTimeClock());
+  PrintAnalysisResults("Empty", result, logger);
+}
+
+TEST(VideoQualityAnalysisTest, PrintAnalysisResultsOneFrame) {
   ResultsContainer result;
   result.frames.push_back(AnalysisResult(0, 35.0, 0.9));
-  PrintAnalysisResults(logfile_, "OneFrame", &result);
+  DefaultMetricsLogger logger(Clock::GetRealTimeClock());
+  PrintAnalysisResults("OneFrame", result, logger);
 }
 
-TEST_F(VideoQualityAnalysisTest, PrintAnalysisResultsThreeFrames) {
+TEST(VideoQualityAnalysisTest, PrintAnalysisResultsThreeFrames) {
   ResultsContainer result;
   result.frames.push_back(AnalysisResult(0, 35.0, 0.9));
   result.frames.push_back(AnalysisResult(1, 34.0, 0.8));
   result.frames.push_back(AnalysisResult(2, 33.0, 0.7));
-  PrintAnalysisResults(logfile_, "ThreeFrames", &result);
+  DefaultMetricsLogger logger(Clock::GetRealTimeClock());
+  PrintAnalysisResults("ThreeFrames", result, logger);
 }
 
-TEST_F(VideoQualityAnalysisTest,
-       PrintMaxRepeatedAndSkippedFramesSkippedFrames) {
+TEST(VideoQualityAnalysisTest, PrintMaxRepeatedAndSkippedFramesSkippedFrames) {
   ResultsContainer result;
 
-  std::string log_filename =
-      TempFilename(webrtc::test::OutputPath(), "log.log");
-  FILE* logfile = fopen(log_filename.c_str(), "w");
-  ASSERT_TRUE(logfile != NULL);
-
   result.max_repeated_frames = 2;
   result.max_skipped_frames = 2;
   result.total_skipped_frames = 3;
   result.decode_errors_ref = 0;
   result.decode_errors_test = 0;
 
-  PrintAnalysisResults(logfile, "NormalStatsFile", &result);
-  ASSERT_EQ(0, fclose(logfile));
+  DefaultMetricsLogger logger(Clock::GetRealTimeClock());
+  PrintAnalysisResults("NormalStatsFile", result, logger);
 
-  std::vector<std::string> expected_out = {
-      "RESULT Max_repeated: NormalStatsFile= 2 ",
-      "RESULT Max_skipped: NormalStatsFile= 2 ",
-      "RESULT Total_skipped: NormalStatsFile= 3 ",
-      "RESULT Decode_errors_reference: NormalStatsFile= 0 ",
-      "RESULT Decode_errors_test: NormalStatsFile= 0 "};
-  VerifyLogOutput(log_filename, expected_out);
+  std::vector<MetricValidationInfo> metrics =
+      ToValidationInfo(logger.GetCollectedMetrics());
+  EXPECT_THAT(
+      metrics,
+      IsSupersetOf(
+          {MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Max_repeated",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 2},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Max_skipped",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 2},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Total_skipped",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 3},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Decode_errors_reference",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 0},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Decode_errors_test",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 0}}));
 }
 
-TEST_F(VideoQualityAnalysisTest,
-       PrintMaxRepeatedAndSkippedFramesDecodeErrorInTest) {
+TEST(VideoQualityAnalysisTest,
+     PrintMaxRepeatedAndSkippedFramesDecodeErrorInTest) {
   ResultsContainer result;
 
   std::string log_filename =
@@ -115,38 +146,67 @@
   result.total_skipped_frames = 0;
   result.decode_errors_ref = 0;
   result.decode_errors_test = 3;
-  PrintAnalysisResults(logfile, "NormalStatsFile", &result);
-  ASSERT_EQ(0, fclose(logfile));
 
-  std::vector<std::string> expected_out = {
-      "RESULT Max_repeated: NormalStatsFile= 1 ",
-      "RESULT Max_skipped: NormalStatsFile= 0 ",
-      "RESULT Total_skipped: NormalStatsFile= 0 ",
-      "RESULT Decode_errors_reference: NormalStatsFile= 0 ",
-      "RESULT Decode_errors_test: NormalStatsFile= 3 "};
-  VerifyLogOutput(log_filename, expected_out);
+  DefaultMetricsLogger logger(Clock::GetRealTimeClock());
+  PrintAnalysisResults("NormalStatsFile", result, logger);
+
+  std::vector<MetricValidationInfo> metrics =
+      ToValidationInfo(logger.GetCollectedMetrics());
+  EXPECT_THAT(
+      metrics,
+      IsSupersetOf(
+          {MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Max_repeated",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 1},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Max_skipped",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 0},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Total_skipped",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 0},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Decode_errors_reference",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 0},
+           MetricValidationInfo{
+               .test_case = "NormalStatsFile",
+               .name = "Decode_errors_test",
+               .unit = Unit::kUnitless,
+               .improvement_direction = ImprovementDirection::kNeitherIsBetter,
+               .mean = 3}}));
 }
 
-TEST_F(VideoQualityAnalysisTest, CalculateFrameClustersOneValue) {
+TEST(VideoQualityAnalysisTest, CalculateFrameClustersOneValue) {
   const std::vector<Cluster> result = CalculateFrameClusters({1});
   EXPECT_EQ(1u, result.size());
   EXPECT_EQ(1u, result[0].index);
   EXPECT_EQ(1, result[0].number_of_repeated_frames);
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxRepeatedFramesOneValue) {
+TEST(VideoQualityAnalysisTest, GetMaxRepeatedFramesOneValue) {
   EXPECT_EQ(1, GetMaxRepeatedFrames(CalculateFrameClusters({1})));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxSkippedFramesOneValue) {
+TEST(VideoQualityAnalysisTest, GetMaxSkippedFramesOneValue) {
   EXPECT_EQ(0, GetMaxSkippedFrames(CalculateFrameClusters({1})));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesOneValue) {
+TEST(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesOneValue) {
   EXPECT_EQ(0, GetTotalNumberOfSkippedFrames(CalculateFrameClusters({1})));
 }
 
-TEST_F(VideoQualityAnalysisTest, CalculateFrameClustersOneOneTwo) {
+TEST(VideoQualityAnalysisTest, CalculateFrameClustersOneOneTwo) {
   const std::vector<Cluster> result = CalculateFrameClusters({1, 1, 2});
   EXPECT_EQ(2u, result.size());
   EXPECT_EQ(1u, result[0].index);
@@ -155,34 +215,35 @@
   EXPECT_EQ(1, result[1].number_of_repeated_frames);
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxRepeatedFramesOneOneTwo) {
+TEST(VideoQualityAnalysisTest, GetMaxRepeatedFramesOneOneTwo) {
   EXPECT_EQ(2, GetMaxRepeatedFrames(CalculateFrameClusters({1, 1, 2})));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxSkippedFramesOneOneTwo) {
+TEST(VideoQualityAnalysisTest, GetMaxSkippedFramesOneOneTwo) {
   EXPECT_EQ(0, GetMaxSkippedFrames(CalculateFrameClusters({1, 1, 2})));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesOneOneTwo) {
+TEST(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesOneOneTwo) {
   EXPECT_EQ(0,
             GetTotalNumberOfSkippedFrames(CalculateFrameClusters({1, 1, 2})));
 }
 
-TEST_F(VideoQualityAnalysisTest, CalculateFrameClustersEmpty) {
+TEST(VideoQualityAnalysisTest, CalculateFrameClustersEmpty) {
   EXPECT_TRUE(CalculateFrameClusters({}).empty());
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxRepeatedFramesEmpty) {
+TEST(VideoQualityAnalysisTest, GetMaxRepeatedFramesEmpty) {
   EXPECT_EQ(0, GetMaxRepeatedFrames({}));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetMaxSkippedFramesEmpty) {
+TEST(VideoQualityAnalysisTest, GetMaxSkippedFramesEmpty) {
   EXPECT_EQ(0, GetMaxSkippedFrames({}));
 }
 
-TEST_F(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesEmpty) {
+TEST(VideoQualityAnalysisTest, GetTotalNumberOfSkippedFramesEmpty) {
   EXPECT_EQ(0, GetTotalNumberOfSkippedFrames({}));
 }
 
+}  // namespace
 }  // namespace test
 }  // namespace webrtc