summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/jitter
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/test/jitter
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 '')
-rw-r--r--third_party/libwebrtc/test/jitter/BUILD.gn64
-rw-r--r--third_party/libwebrtc/test/jitter/OWNERS1
-rw-r--r--third_party/libwebrtc/test/jitter/delay_variation_calculator.cc130
-rw-r--r--third_party/libwebrtc/test/jitter/delay_variation_calculator.h94
-rw-r--r--third_party/libwebrtc/test/jitter/delay_variation_calculator_unittest.cc108
-rw-r--r--third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.cc70
-rw-r--r--third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.h53
-rw-r--r--third_party/libwebrtc/test/jitter/logging_delay_variation_calculator_unittest.cc56
8 files changed, 576 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/jitter/BUILD.gn b/third_party/libwebrtc/test/jitter/BUILD.gn
new file mode 100644
index 0000000000..ad9c58ac42
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright (c) 2023 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.
+
+import("../../webrtc.gni")
+
+rtc_library("delay_variation_calculator") {
+ visibility = [ "*" ]
+ sources = [
+ "delay_variation_calculator.cc",
+ "delay_variation_calculator.h",
+ ]
+ deps = [
+ "../../api/numerics",
+ "../../api/test/metrics:metrics_logger",
+ "../../api/units:data_size",
+ "../../api/units:frequency",
+ "../../api/units:time_delta",
+ "../../api/units:timestamp",
+ "../../api/video:video_frame_type",
+ "../../rtc_base:logging",
+ "../../rtc_base:rtc_numerics",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+
+rtc_library("logging_delay_variation_calculator") {
+ visibility = [ "*" ]
+ sources = [
+ "logging_delay_variation_calculator.cc",
+ "logging_delay_variation_calculator.h",
+ ]
+ deps = [
+ ":delay_variation_calculator",
+ "../../api/test/metrics:global_metrics_logger_and_exporter",
+ "../../api/units:data_size",
+ "../../api/video:video_frame_type",
+ "../../rtc_base:logging",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("jitter_unittests") {
+ testonly = true
+ sources = [
+ "delay_variation_calculator_unittest.cc",
+ "logging_delay_variation_calculator_unittest.cc",
+ ]
+ deps = [
+ ":delay_variation_calculator",
+ ":logging_delay_variation_calculator",
+ "../../api/numerics",
+ "../../api/test/metrics:metrics_logger",
+ "../../api/units:timestamp",
+ "../../system_wrappers",
+ "../../test:test_support",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/test/jitter/OWNERS b/third_party/libwebrtc/test/jitter/OWNERS
new file mode 100644
index 0000000000..fe76628409
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/OWNERS
@@ -0,0 +1 @@
+brandtr@webrtc.org
diff --git a/third_party/libwebrtc/test/jitter/delay_variation_calculator.cc b/third_party/libwebrtc/test/jitter/delay_variation_calculator.cc
new file mode 100644
index 0000000000..092bd7ca82
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/delay_variation_calculator.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2023 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 "test/jitter/delay_variation_calculator.h"
+
+#include <string>
+
+#include "absl/types/optional.h"
+#include "api/units/frequency.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+constexpr Frequency k90000Hz = Frequency::Hertz(90000);
+} // namespace
+
+void DelayVariationCalculator::Insert(
+ uint32_t rtp_timestamp,
+ Timestamp arrival_time,
+ DataSize size,
+ absl::optional<int> spatial_layer,
+ absl::optional<int> temporal_layer,
+ absl::optional<VideoFrameType> frame_type) {
+ Frame frame{.rtp_timestamp = rtp_timestamp,
+ .unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp),
+ .arrival_time = arrival_time,
+ .size = size,
+ .spatial_layer = spatial_layer,
+ .temporal_layer = temporal_layer,
+ .frame_type = frame_type};
+ // Using RTP timestamp as the time series sample identifier allows for
+ // cross-correlating time series logged by different objects at different
+ // arrival timestamps.
+ Timestamp sample_time =
+ Timestamp::Millis((frame.unwrapped_rtp_timestamp / k90000Hz).ms());
+ MetadataT sample_metadata = BuildMetadata(frame);
+ if (!prev_frame_) {
+ InsertFirstFrame(frame, sample_time, sample_metadata);
+ } else {
+ InsertFrame(frame, sample_time, sample_metadata);
+ }
+ prev_frame_ = frame;
+}
+
+void DelayVariationCalculator::InsertFirstFrame(const Frame& frame,
+ Timestamp sample_time,
+ MetadataT sample_metadata) {
+ const auto s = [=](double sample_value) {
+ return SamplesStatsCounter::StatsSample{.value = sample_value,
+ .time = sample_time,
+ .metadata = sample_metadata};
+ };
+ time_series_.rtp_timestamps.AddSample(
+ s(static_cast<double>(frame.rtp_timestamp)));
+ time_series_.arrival_times_ms.AddSample(s(frame.arrival_time.ms<double>()));
+ time_series_.sizes_bytes.AddSample(s(frame.size.bytes<double>()));
+ time_series_.inter_departure_times_ms.AddSample(s(0.0));
+ time_series_.inter_arrival_times_ms.AddSample(s(0.0));
+ time_series_.inter_delay_variations_ms.AddSample(s(0.0));
+ time_series_.inter_size_variations_bytes.AddSample(s(0.0));
+}
+
+void DelayVariationCalculator::InsertFrame(const Frame& frame,
+ Timestamp sample_time,
+ MetadataT sample_metadata) {
+ int64_t inter_rtp_time =
+ frame.unwrapped_rtp_timestamp - prev_frame_->unwrapped_rtp_timestamp;
+ TimeDelta inter_departure_time = inter_rtp_time / k90000Hz;
+ TimeDelta inter_arrival_time = frame.arrival_time - prev_frame_->arrival_time;
+ TimeDelta inter_delay_variation = inter_arrival_time - inter_departure_time;
+ double inter_size_variation_bytes =
+ frame.size.bytes<double>() - prev_frame_->size.bytes<double>();
+ const auto s = [=](double sample_value) {
+ return SamplesStatsCounter::StatsSample{.value = sample_value,
+ .time = sample_time,
+ .metadata = sample_metadata};
+ };
+ const auto ms = [](const TimeDelta& td) { return td.ms<double>(); };
+ time_series_.rtp_timestamps.AddSample(
+ s(static_cast<double>(frame.rtp_timestamp)));
+ time_series_.arrival_times_ms.AddSample(s(frame.arrival_time.ms<double>()));
+ time_series_.sizes_bytes.AddSample(s(frame.size.bytes<double>()));
+ time_series_.inter_departure_times_ms.AddSample(s(ms(inter_departure_time)));
+ time_series_.inter_arrival_times_ms.AddSample(s(ms(inter_arrival_time)));
+ time_series_.inter_delay_variations_ms.AddSample(
+ s(ms(inter_delay_variation)));
+ time_series_.inter_size_variations_bytes.AddSample(
+ s(inter_size_variation_bytes));
+}
+
+DelayVariationCalculator::MetadataT DelayVariationCalculator::BuildMetadata(
+ const Frame& frame) {
+ MetadataT metadata;
+ if (prev_frame_) {
+ if (prev_frame_->spatial_layer) {
+ metadata["sl_prev"] = std::to_string(*prev_frame_->spatial_layer);
+ }
+ if (prev_frame_->temporal_layer) {
+ metadata["tl_prev"] = std::to_string(*prev_frame_->temporal_layer);
+ }
+ if (prev_frame_->frame_type) {
+ metadata["frame_type_prev"] =
+ VideoFrameTypeToString(*prev_frame_->frame_type);
+ }
+ }
+ if (frame.spatial_layer) {
+ metadata["sl"] = std::to_string(*frame.spatial_layer);
+ }
+ if (frame.temporal_layer) {
+ metadata["tl"] = std::to_string(*frame.temporal_layer);
+ }
+ if (frame.frame_type) {
+ metadata["frame_type"] = VideoFrameTypeToString(*frame.frame_type);
+ }
+ return metadata;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/jitter/delay_variation_calculator.h b/third_party/libwebrtc/test/jitter/delay_variation_calculator.h
new file mode 100644
index 0000000000..6400f82ad7
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/delay_variation_calculator.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef TEST_JITTER_DELAY_VARIATION_CALCULATOR_H_
+#define TEST_JITTER_DELAY_VARIATION_CALCULATOR_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "absl/types/optional.h"
+#include "api/numerics/samples_stats_counter.h"
+#include "api/test/metrics/metrics_logger.h"
+#include "api/units/data_size.h"
+#include "api/units/timestamp.h"
+#include "api/video/video_frame_type.h"
+#include "rtc_base/numerics/sequence_number_unwrapper.h"
+
+namespace webrtc {
+namespace test {
+
+// Helper class for calculating different delay variation statistics for "RTP
+// frame arrival events". One use case is gathering statistics from
+// RtcEventLogs. Another use case is online logging of data in test calls.
+class DelayVariationCalculator {
+ public:
+ struct TimeSeries {
+ // Time series of RTP timestamps `t(n)` for each frame `n`.
+ SamplesStatsCounter rtp_timestamps;
+ // Time series of local arrival timestamps `r(n)` for each frame.
+ SamplesStatsCounter arrival_times_ms;
+ // Time series of sizes `s(n)` for each frame.
+ SamplesStatsCounter sizes_bytes;
+ // Time series of `d_t(n) = t(n) - t(n-1)` for each frame.
+ SamplesStatsCounter inter_departure_times_ms;
+ // Time series of `d_r(n) = r(n) - r(n-1)` for each frame.
+ SamplesStatsCounter inter_arrival_times_ms;
+ // Time series of `d_r(n) - d_t(n) = (r(n) - r(n-1)) - (t(n) - t(n-1))`
+ // for each frame.
+ SamplesStatsCounter inter_delay_variations_ms;
+ // Time series of `s(n) - s(n-1)`, for each frame.
+ SamplesStatsCounter inter_size_variations_bytes;
+ };
+
+ DelayVariationCalculator() = default;
+ ~DelayVariationCalculator() = default;
+
+ void Insert(uint32_t rtp_timestamp,
+ Timestamp arrival_time,
+ DataSize size,
+ absl::optional<int> spatial_layer = absl::nullopt,
+ absl::optional<int> temporal_layer = absl::nullopt,
+ absl::optional<VideoFrameType> frame_type = absl::nullopt);
+
+ const TimeSeries& time_series() const { return time_series_; }
+
+ private:
+ struct Frame {
+ uint32_t rtp_timestamp;
+ int64_t unwrapped_rtp_timestamp;
+ Timestamp arrival_time;
+ DataSize size;
+ absl::optional<int> spatial_layer;
+ absl::optional<int> temporal_layer;
+ absl::optional<VideoFrameType> frame_type;
+ };
+ using MetadataT = std::map<std::string, std::string>;
+
+ void InsertFirstFrame(const Frame& frame,
+ Timestamp sample_time,
+ MetadataT sample_metadata);
+ void InsertFrame(const Frame& frame,
+ Timestamp sample_time,
+ MetadataT sample_metadata);
+
+ MetadataT BuildMetadata(const Frame& frame);
+
+ RtpTimestampUnwrapper unwrapper_;
+ absl::optional<Frame> prev_frame_ = absl::nullopt;
+ TimeSeries time_series_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_JITTER_DELAY_VARIATION_CALCULATOR_H_
diff --git a/third_party/libwebrtc/test/jitter/delay_variation_calculator_unittest.cc b/third_party/libwebrtc/test/jitter/delay_variation_calculator_unittest.cc
new file mode 100644
index 0000000000..41db376ed6
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/delay_variation_calculator_unittest.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 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 "test/jitter/delay_variation_calculator.h"
+
+#include "api/numerics/samples_stats_counter.h"
+#include "api/units/timestamp.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+MATCHER_P(HasLength, s, "") {
+ bool c1 = arg.rtp_timestamps.NumSamples() == s;
+ bool c2 = arg.arrival_times_ms.NumSamples() == s;
+ bool c3 = arg.sizes_bytes.NumSamples() == s;
+ bool c4 = arg.inter_departure_times_ms.NumSamples() == s;
+ bool c5 = arg.inter_arrival_times_ms.NumSamples() == s;
+ bool c6 = arg.inter_delay_variations_ms.NumSamples() == s;
+ bool c7 = arg.inter_size_variations_bytes.NumSamples() == s;
+ *result_listener << "c: " << c1 << c2 << c3 << c4 << c5 << c6 << c7;
+ return c1 && c2 && c3 && c4 && c5 && c6 && c7;
+}
+
+TEST(DelayVariationCalculatorTest, NoTimeSeriesWithoutFrame) {
+ DelayVariationCalculator calc;
+
+ EXPECT_THAT(calc.time_series(), HasLength(0));
+}
+
+TEST(DelayVariationCalculatorTest, PartialTimeSeriesWithOneFrame) {
+ DelayVariationCalculator calc;
+
+ calc.Insert(3000, Timestamp::Millis(33), DataSize::Bytes(100));
+
+ DelayVariationCalculator::TimeSeries ts = calc.time_series();
+ ASSERT_THAT(ts, HasLength(1));
+ auto v0 = [](const SamplesStatsCounter& c) {
+ return c.GetTimedSamples()[0].value;
+ };
+ EXPECT_EQ(v0(ts.rtp_timestamps), 3000);
+ EXPECT_EQ(v0(ts.arrival_times_ms), 33);
+ EXPECT_EQ(v0(ts.sizes_bytes), 100);
+ EXPECT_EQ(v0(ts.inter_departure_times_ms), 0);
+ EXPECT_EQ(v0(ts.inter_arrival_times_ms), 0);
+ EXPECT_EQ(v0(ts.inter_delay_variations_ms), 0);
+ EXPECT_EQ(v0(ts.inter_size_variations_bytes), 0);
+}
+
+TEST(DelayVariationCalculatorTest, TimeSeriesWithTwoFrames) {
+ DelayVariationCalculator calc;
+
+ calc.Insert(3000, Timestamp::Millis(33), DataSize::Bytes(100));
+ calc.Insert(6000, Timestamp::Millis(66), DataSize::Bytes(100));
+
+ DelayVariationCalculator::TimeSeries ts = calc.time_series();
+ ASSERT_THAT(ts, HasLength(2));
+ auto v1 = [](const SamplesStatsCounter& c) {
+ return c.GetTimedSamples()[1].value;
+ };
+ EXPECT_EQ(v1(ts.rtp_timestamps), 6000);
+ EXPECT_EQ(v1(ts.arrival_times_ms), 66);
+ EXPECT_EQ(v1(ts.sizes_bytes), 100);
+ EXPECT_EQ(v1(ts.inter_departure_times_ms), 33.333);
+ EXPECT_EQ(v1(ts.inter_arrival_times_ms), 33);
+ EXPECT_EQ(v1(ts.inter_delay_variations_ms), -0.333);
+ EXPECT_EQ(v1(ts.inter_size_variations_bytes), 0);
+}
+
+TEST(DelayVariationCalculatorTest, MetadataRecordedForAllTimeSeriesAndFrames) {
+ DelayVariationCalculator calc;
+
+ calc.Insert(3000, Timestamp::Millis(33), DataSize::Bytes(100), /*sl=*/0,
+ /*tl=*/0, VideoFrameType::kVideoFrameKey);
+ calc.Insert(6000, Timestamp::Millis(66), DataSize::Bytes(100), /*sl=*/0,
+ /*tl=*/1, VideoFrameType::kVideoFrameDelta);
+
+ DelayVariationCalculator::TimeSeries ts = calc.time_series();
+ ASSERT_THAT(ts, HasLength(2));
+ auto v = [](const SamplesStatsCounter* c, int n, std::string key) {
+ return c->GetTimedSamples()[n].metadata.at(key);
+ };
+ for (const auto* c :
+ {&ts.rtp_timestamps, &ts.arrival_times_ms, &ts.sizes_bytes,
+ &ts.inter_departure_times_ms, &ts.inter_arrival_times_ms,
+ &ts.inter_delay_variations_ms, &ts.inter_size_variations_bytes}) {
+ EXPECT_EQ(v(c, 0, "sl"), "0");
+ EXPECT_EQ(v(c, 0, "tl"), "0");
+ EXPECT_EQ(v(c, 0, "frame_type"), "key");
+ EXPECT_EQ(v(c, 1, "sl_prev"), "0");
+ EXPECT_EQ(v(c, 1, "tl_prev"), "0");
+ EXPECT_EQ(v(c, 1, "frame_type_prev"), "key");
+ EXPECT_EQ(v(c, 1, "sl"), "0");
+ EXPECT_EQ(v(c, 1, "tl"), "1");
+ EXPECT_EQ(v(c, 1, "frame_type"), "delta");
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.cc b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.cc
new file mode 100644
index 0000000000..aa58b0fa01
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 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 "test/jitter/logging_delay_variation_calculator.h"
+
+#include "api/test/metrics/global_metrics_logger_and_exporter.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace test {
+
+void LoggingDelayVariationCalculator::Insert(
+ uint32_t rtp_timestamp,
+ Timestamp arrival_time,
+ DataSize size,
+ absl::optional<int> spatial_layer,
+ absl::optional<int> temporal_layer,
+ absl::optional<VideoFrameType> frame_type) {
+ calc_.Insert(rtp_timestamp, arrival_time, size, spatial_layer, temporal_layer,
+ frame_type);
+}
+
+void LoggingDelayVariationCalculator::LogMetrics() const {
+ const DelayVariationCalculator::TimeSeries& time_series = calc_.time_series();
+
+ if (!time_series.rtp_timestamps.IsEmpty()) {
+ logger_->LogMetric("rtp_timestamp", log_type_, time_series.rtp_timestamps,
+ Unit::kUnitless, ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.arrival_times_ms.IsEmpty()) {
+ logger_->LogMetric("arrival_time", log_type_, time_series.arrival_times_ms,
+ Unit::kMilliseconds,
+ ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.sizes_bytes.IsEmpty()) {
+ logger_->LogMetric("size_bytes", log_type_, time_series.sizes_bytes,
+ Unit::kBytes, ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.inter_departure_times_ms.IsEmpty()) {
+ logger_->LogMetric(
+ "inter_departure_time", log_type_, time_series.inter_departure_times_ms,
+ Unit::kMilliseconds, ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.inter_arrival_times_ms.IsEmpty()) {
+ logger_->LogMetric("inter_arrival_time", log_type_,
+ time_series.inter_arrival_times_ms, Unit::kMilliseconds,
+ ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.inter_delay_variations_ms.IsEmpty()) {
+ logger_->LogMetric("inter_delay_variation", log_type_,
+ time_series.inter_delay_variations_ms,
+ Unit::kMilliseconds,
+ ImprovementDirection::kNeitherIsBetter);
+ }
+ if (!time_series.inter_size_variations_bytes.IsEmpty()) {
+ logger_->LogMetric("inter_size_variation", log_type_,
+ time_series.inter_size_variations_bytes, Unit::kBytes,
+ ImprovementDirection::kNeitherIsBetter);
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.h b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.h
new file mode 100644
index 0000000000..f15d2aec77
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef TEST_JITTER_LOGGING_DELAY_VARIATION_CALCULATOR_H_
+#define TEST_JITTER_LOGGING_DELAY_VARIATION_CALCULATOR_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "api/test/metrics/global_metrics_logger_and_exporter.h"
+#include "api/units/data_size.h"
+#include "test/jitter/delay_variation_calculator.h"
+
+namespace webrtc {
+namespace test {
+
+// This class logs the results from a `DelayVariationCalculator`
+// to a metrics logger. For ease of integration, logging happens at
+// object destruction.
+class LoggingDelayVariationCalculator {
+ public:
+ LoggingDelayVariationCalculator(
+ absl::string_view log_type,
+ DefaultMetricsLogger* logger = GetGlobalMetricsLogger())
+ : log_type_(log_type), logger_(logger) {}
+ ~LoggingDelayVariationCalculator() { LogMetrics(); }
+
+ void Insert(uint32_t rtp_timestamp,
+ Timestamp arrival_time,
+ DataSize size,
+ absl::optional<int> spatial_layer = absl::nullopt,
+ absl::optional<int> temporal_layer = absl::nullopt,
+ absl::optional<VideoFrameType> frame_type = absl::nullopt);
+
+ private:
+ void LogMetrics() const;
+
+ const std::string log_type_;
+ DefaultMetricsLogger* const logger_;
+ DelayVariationCalculator calc_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_JITTER_LOGGING_DELAY_VARIATION_CALCULATOR_H_
diff --git a/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator_unittest.cc b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator_unittest.cc
new file mode 100644
index 0000000000..d28a235804
--- /dev/null
+++ b/third_party/libwebrtc/test/jitter/logging_delay_variation_calculator_unittest.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 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 "test/jitter/logging_delay_variation_calculator.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "api/test/metrics/metrics_logger.h"
+#include "api/units/timestamp.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+TEST(LoggingDelayVariationCalculator, TimeSeriesWithTwoFrames) {
+ SimulatedClock clock(123456);
+ DefaultMetricsLogger logger(&clock);
+ {
+ LoggingDelayVariationCalculator calc("TimeSeriesWithTwoFrames", &logger);
+ calc.Insert(3000, Timestamp::Millis(33), DataSize::Bytes(100));
+ calc.Insert(6000, Timestamp::Millis(66), DataSize::Bytes(100));
+ // Metrics are logged when `calc` goes out of scope.
+ }
+
+ std::vector<Metric> metrics = logger.GetCollectedMetrics();
+ std::map<std::string, double> last_sample_by_metric_name;
+ std::transform(metrics.begin(), metrics.end(),
+ std::inserter(last_sample_by_metric_name,
+ last_sample_by_metric_name.end()),
+ [](const Metric& metric) {
+ EXPECT_FALSE(metric.time_series.samples.empty());
+ return std::make_pair(
+ metric.name, metric.time_series.samples.back().value);
+ });
+ EXPECT_EQ(last_sample_by_metric_name["rtp_timestamp"], 6000.0);
+ EXPECT_EQ(last_sample_by_metric_name["arrival_time"], 66.0);
+ EXPECT_EQ(last_sample_by_metric_name["size_bytes"], 100.0);
+ EXPECT_EQ(last_sample_by_metric_name["inter_departure_time"], 33.333);
+ EXPECT_EQ(last_sample_by_metric_name["inter_arrival_time"], 33.0);
+ EXPECT_EQ(last_sample_by_metric_name["inter_delay_variation"], -0.333);
+ EXPECT_EQ(last_sample_by_metric_name["inter_size_variation"], 0.0);
+}
+
+} // namespace test
+} // namespace webrtc