Allow StdoutMetricsExporter to merge stream and peers info in test_name.

Currently, metrics printed by StdoutMetricsExporter have lost info
about stream, sender/receiver and peers. They look like:

RESULT psnr_dB: foreman_cif_net_delay_0_0_plr_0_VP9= {38.697086,1.7471016} unitless_biggerIsBetter

Which makes it impossible to know the stream to which this PSNR
measurement was referring to.

This CL tries to restore the behaviour before [1], which changed test
names with:

std::string DefaultVideoQualityAnalyzer::GetTestCaseName(
    const std::string& stream_label) const {
  if (!absl::GetFlag(FLAGS_isolated_script_test_perf_output).empty()) {
    return test_label_ + "/" + stream_label;
  }
  return test_label_;
}

[1] - https://webrtc-review.googlesource.com/c/src/+/383720

Bug: None
Change-Id: Iad3cf5cb723e37c2a32fc510a2d484b796024ace
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/399280
Reviewed-by: Jeremy Leconte <jleconte@google.com>
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45185}
diff --git a/BUILD.gn b/BUILD.gn
index bc948d4..3357c2d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -540,7 +540,6 @@
       "api/rtc_event_log:rtc_event_log_factory",
       "api/task_queue",
       "api/task_queue:default_task_queue_factory",
-      "api/test/metrics",
       "api/video_codecs:video_decoder_factory_template",
       "api/video_codecs:video_decoder_factory_template_dav1d_adapter",
       "api/video_codecs:video_decoder_factory_template_libvpx_vp8_adapter",
diff --git a/api/test/metrics/BUILD.gn b/api/test/metrics/BUILD.gn
index 3f93d22..abb6453 100644
--- a/api/test/metrics/BUILD.gn
+++ b/api/test/metrics/BUILD.gn
@@ -12,14 +12,17 @@
 }
 
 group("metrics") {
+  testonly = true
   deps = [
     ":global_metrics_logger_and_exporter",
     ":metric",
     ":metrics_accumulator",
     ":metrics_exporter",
     ":metrics_logger",
-    ":stdout_metrics_exporter",
   ]
+  if (!build_with_chromium) {
+    deps += [ ":stdout_metrics_exporter" ]
+  }
 }
 
 if (rtc_include_tests) {
@@ -98,18 +101,26 @@
   ]
 }
 
-rtc_library("stdout_metrics_exporter") {
-  visibility = [ "*" ]
-  sources = [
-    "stdout_metrics_exporter.cc",
-    "stdout_metrics_exporter.h",
-  ]
-  deps = [
-    ":metric",
-    ":metrics_exporter",
-    "../..:array_view",
-    "../../../rtc_base:stringutils",
-  ]
+if (!build_with_chromium) {
+  # Hidden from the Chromium build because it depends on absl flags which
+  # cannot be compiled in Chromium.
+  rtc_library("stdout_metrics_exporter") {
+    visibility = [ "*" ]
+    testonly = true
+    sources = [
+      "stdout_metrics_exporter.cc",
+      "stdout_metrics_exporter.h",
+    ]
+    deps = [
+      ":metric",
+      ":metrics_exporter",
+      "../..:array_view",
+      "../../../rtc_base:stringutils",
+      "../../../test:test_flags",
+      "//third_party/abseil-cpp/absl/flags:flag",
+      "//third_party/abseil-cpp/absl/strings",
+    ]
+  }
 }
 
 rtc_library("chrome_perf_dashboard_metrics_exporter") {
@@ -222,8 +233,10 @@
     deps = [
       ":metric",
       ":stdout_metrics_exporter",
+      "../../../test:test_flags",
       "../../../test:test_support",
       "../../units:timestamp",
+      "//third_party/abseil-cpp/absl/flags:flag",
     ]
   }
 
diff --git a/api/test/metrics/stdout_metrics_exporter.cc b/api/test/metrics/stdout_metrics_exporter.cc
index 52ae291..336817f 100644
--- a/api/test/metrics/stdout_metrics_exporter.cc
+++ b/api/test/metrics/stdout_metrics_exporter.cc
@@ -15,9 +15,12 @@
 #include <optional>
 #include <string>
 
+#include "absl/flags/flag.h"
+#include "absl/strings/str_cat.h"
 #include "api/array_view.h"
 #include "api/test/metrics/metric.h"
 #include "rtc_base/strings/string_builder.h"
+#include "test/test_flags.h"
 
 namespace webrtc {
 namespace test {
@@ -66,6 +69,22 @@
   }
 }
 
+std::string TestCaseAndMetadata(const Metric& metric) {
+  if (absl::GetFlag(FLAGS_isolated_script_test_perf_output).empty()) {
+    if (metric.metric_metadata.contains("video_stream")) {
+      return absl::StrCat(metric.test_case, "/",
+                          metric.metric_metadata.at("video_stream"), "_",
+                          metric.metric_metadata.at("sender"), "_",
+                          metric.metric_metadata.at("receiver"));
+    }
+    if (metric.metric_metadata.contains("peer")) {
+      return absl::StrCat(metric.test_case, "/",
+                          metric.metric_metadata.at("peer"));
+    }
+  }
+  return metric.test_case;
+}
+
 }  // namespace
 
 StdoutMetricsExporter::StdoutMetricsExporter() : output_(stdout) {}
@@ -79,7 +98,8 @@
 
 void StdoutMetricsExporter::PrintMetric(const Metric& metric) {
   StringBuilder value_stream;
-  value_stream << metric.test_case << " / " << metric.name << "= {mean=";
+  value_stream << TestCaseAndMetadata(metric) << " / " << metric.name
+               << "= {mean=";
   if (metric.stats.mean.has_value()) {
     AppendWithPrecision(*metric.stats.mean, 8, value_stream);
   } else {
diff --git a/api/test/metrics/stdout_metrics_exporter_test.cc b/api/test/metrics/stdout_metrics_exporter_test.cc
index 27c1d84..93d25fb 100644
--- a/api/test/metrics/stdout_metrics_exporter_test.cc
+++ b/api/test/metrics/stdout_metrics_exporter_test.cc
@@ -13,9 +13,11 @@
 #include <string>
 #include <vector>
 
+#include "absl/flags/flag.h"
 #include "api/test/metrics/metric.h"
 #include "api/units/timestamp.h"
 #include "test/gtest.h"
+#include "test/test_flags.h"
 
 namespace webrtc {
 namespace test {
@@ -42,27 +44,51 @@
                 .stats = Metric::Stats{.mean = mean, .stddev = stddev}};
 }
 
-TEST(StdoutMetricsExporterTest, ExportMetricFormatCorrect) {
-  Metric metric1{
-      .name = "test_metric1",
-      .unit = Unit::kMilliseconds,
-      .improvement_direction = ImprovementDirection::kBiggerIsBetter,
-      .test_case = "test_case_name1",
-      .metric_metadata = DefaultMetadata(),
-      .time_series =
-          Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
-      .stats =
-          Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
-  Metric metric2{
-      .name = "test_metric2",
-      .unit = Unit::kKilobitsPerSecond,
-      .improvement_direction = ImprovementDirection::kSmallerIsBetter,
-      .test_case = "test_case_name2",
-      .metric_metadata = DefaultMetadata(),
-      .time_series =
-          Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
-      .stats = Metric::Stats{
-          .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
+class StdoutMetricsExporterTest : public ::testing::Test {
+ public:
+  StdoutMetricsExporterTest() {
+    original_isolated_script_test_perf_output_ =
+        absl::GetFlag(FLAGS_isolated_script_test_perf_output);
+
+    metric1_ = {
+        .name = "test_metric1",
+        .unit = Unit::kMilliseconds,
+        .improvement_direction = ImprovementDirection::kBiggerIsBetter,
+        .test_case = "test_case_name1",
+        .metric_metadata =
+            std::map<std::string, std::string>{{"video_stream", "alice_stream"},
+                                               {"sender", "alice"},
+                                               {"receiver", "bob"}},
+        .time_series =
+            Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
+        .stats = Metric::Stats{
+            .mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
+    metric2_ = {
+        .name = "test_metric2",
+        .unit = Unit::kKilobitsPerSecond,
+        .improvement_direction = ImprovementDirection::kSmallerIsBetter,
+        .test_case = "test_case_name2",
+        .metric_metadata =
+            std::map<std::string, std::string>{{"peer", "alice"}},
+        .time_series =
+            Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
+        .stats = Metric::Stats{
+            .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
+  }
+  ~StdoutMetricsExporterTest() {
+    absl::SetFlag(&FLAGS_isolated_script_test_perf_output,
+                  original_isolated_script_test_perf_output_);
+  }
+
+ protected:
+  std::string original_isolated_script_test_perf_output_;
+  Metric metric1_;
+  Metric metric2_;
+};
+
+TEST_F(StdoutMetricsExporterTest,
+       ExportMetricFormatCorrectWhenIsolatedScriptTestPerfOutputIsSet) {
+  absl::SetFlag(&FLAGS_isolated_script_test_perf_output, "/tmp/foo");
 
   testing::internal::CaptureStdout();
   StdoutMetricsExporter exporter;
@@ -73,7 +99,24 @@
       "RESULT: test_case_name2 / test_metric2= "
       "{mean=30, stddev=10} KilobitsPerSecond (SmallerIsBetter)\n";
 
-  EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
+  EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1_, metric2_}));
+  EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
+}
+
+TEST_F(StdoutMetricsExporterTest,
+       ExportMetricFormatCorrectWhenIsolatedScriptTestPerfOutputIsNotSet) {
+  absl::SetFlag(&FLAGS_isolated_script_test_perf_output, "");
+
+  testing::internal::CaptureStdout();
+  StdoutMetricsExporter exporter;
+
+  std::string expected =
+      "RESULT: test_case_name1/alice_stream_alice_bob / test_metric1= "
+      "{mean=15, stddev=5} Milliseconds (BiggerIsBetter)\n"
+      "RESULT: test_case_name2/alice / test_metric2= "
+      "{mean=30, stddev=10} KilobitsPerSecond (SmallerIsBetter)\n";
+
+  EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1_, metric2_}));
   EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
 }
 
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index e162584..9fbf777 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -140,12 +140,13 @@
       ":video_file_reader",
       ":video_file_writer",
       ":video_quality_analysis",
+      "../api:array_view",
       "../api:make_ref_counted",
       "../api:scoped_refptr",
       "../api/test/metrics:chrome_perf_dashboard_metrics_exporter",
       "../api/test/metrics:global_metrics_logger_and_exporter",
+      "../api/test/metrics:metric",
       "../api/test/metrics:metrics_exporter",
-      "../api/test/metrics:stdout_metrics_exporter",
       "../rtc_base:stringutils",
       "//third_party/abseil-cpp/absl/flags:flag",
       "//third_party/abseil-cpp/absl/flags:parse",
diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc
index 9c416c3..71367c6 100644
--- a/rtc_tools/frame_analyzer/frame_analyzer.cc
+++ b/rtc_tools/frame_analyzer/frame_analyzer.cc
@@ -8,10 +8,11 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-
+#include <cmath>
 #include <cstddef>
 #include <cstdint>
 #include <cstdio>
+#include <cstdlib>
 #include <memory>
 #include <string>
 #include <utility>
@@ -20,11 +21,12 @@
 #include "absl/flags/flag.h"
 #include "absl/flags/parse.h"
 #include "absl/strings/match.h"
+#include "api/array_view.h"
 #include "api/scoped_refptr.h"
 #include "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h"
 #include "api/test/metrics/global_metrics_logger_and_exporter.h"
+#include "api/test/metrics/metric.h"
 #include "api/test/metrics/metrics_exporter.h"
-#include "api/test/metrics/stdout_metrics_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"
@@ -75,6 +77,98 @@
   return directory + kPathDelimiter + filename;
 }
 
+// FrameAnalyzerMetricsExporter is a fork of
+// webrtc::test::StdoutMetricsExporter. The fork was required because:
+// 1. frame_analyzer must be compiled with build_with_chromium=true
+// 2. The api/metrics:stdout_metrics_exporter depends on absl/flags which cannot
+//    be build when build_with_chromium=true unless the target is an
+//    rtc_executable (which is not the case for
+//    api/metrics:stdout_metrics_exporter). So this fork allows to cut the
+//    dependency.
+
+// Returns positive integral part of the number.
+int64_t IntegralPart(double value) {
+  return std::lround(std::floor(std::abs(value)));
+}
+
+void AppendWithPrecision(double value,
+                         int digits_after_comma,
+                         webrtc::StringBuilder& out) {
+  int64_t multiplier = std::lround(std::pow(10, digits_after_comma));
+  int64_t integral_part = IntegralPart(value);
+  double decimal_part = std::abs(value) - integral_part;
+
+  // If decimal part has leading zeros then when it will be multiplied on
+  // `multiplier`, leading zeros will be lost. To preserve them we add "1"
+  // so then leading digit will be greater than 0 and won't be removed.
+  //
+  // During conversion to the string leading digit has to be stripped.
+  //
+  // Also due to rounding it may happen that leading digit may be incremented,
+  // like with `digits_after_comma` 3 number 1.9995 will be rounded to 2. In
+  // such case this increment has to be propagated to the `integral_part`.
+  int64_t decimal_holder = std::lround((1 + decimal_part) * multiplier);
+  if (decimal_holder >= 2 * multiplier) {
+    // Rounding incremented added leading digit, so we need to transfer 1 to
+    // integral part.
+    integral_part++;
+    decimal_holder -= multiplier;
+  }
+  // Remove trailing zeros.
+  while (decimal_holder % 10 == 0) {
+    decimal_holder /= 10;
+  }
+
+  // Print serialized number to output.
+  if (value < 0) {
+    out << "-";
+  }
+  out << integral_part;
+  if (decimal_holder != 1) {
+    out << "." << std::to_string(decimal_holder).substr(1, digits_after_comma);
+  }
+}
+
+class FrameAnalyzerMetricsExporter : public webrtc::test::MetricsExporter {
+ public:
+  FrameAnalyzerMetricsExporter() : output_(stdout) {}
+  ~FrameAnalyzerMetricsExporter() override = default;
+
+  FrameAnalyzerMetricsExporter(const FrameAnalyzerMetricsExporter&) = delete;
+  FrameAnalyzerMetricsExporter& operator=(const FrameAnalyzerMetricsExporter&) =
+      delete;
+
+  bool Export(webrtc::ArrayView<const webrtc::test::Metric> metrics) override {
+    for (const webrtc::test::Metric& metric : metrics) {
+      PrintMetric(metric);
+    }
+    return true;
+  }
+
+ private:
+  void PrintMetric(const webrtc::test::Metric& metric) {
+    webrtc::StringBuilder value_stream;
+    value_stream << metric.test_case << " / " << metric.name << "= {mean=";
+    if (metric.stats.mean.has_value()) {
+      AppendWithPrecision(*metric.stats.mean, 8, value_stream);
+    } else {
+      value_stream << "-";
+    }
+    value_stream << ", stddev=";
+    if (metric.stats.stddev.has_value()) {
+      AppendWithPrecision(*metric.stats.stddev, 8, value_stream);
+    } else {
+      value_stream << "-";
+    }
+    value_stream << "} " << ToString(metric.unit) << " ("
+                 << ToString(metric.improvement_direction) << ")";
+
+    fprintf(output_, "RESULT: %s\n", value_stream.str().c_str());
+  }
+
+  FILE* const output_;
+};
+
 }  // namespace
 
 /*
@@ -168,7 +262,7 @@
                                      *webrtc::test::GetGlobalMetricsLogger());
 
   std::vector<std::unique_ptr<webrtc::test::MetricsExporter>> exporters;
-  exporters.push_back(std::make_unique<webrtc::test::StdoutMetricsExporter>());
+  exporters.push_back(std::make_unique<FrameAnalyzerMetricsExporter>());
   std::string chartjson_result_file =
       absl::GetFlag(FLAGS_chartjson_result_file);
   if (!chartjson_result_file.empty()) {