summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/system_wrappers/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/system_wrappers/source
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/system_wrappers/source')
-rw-r--r--third_party/libwebrtc/system_wrappers/source/DEPS6
-rw-r--r--third_party/libwebrtc/system_wrappers/source/clock.cc102
-rw-r--r--third_party/libwebrtc/system_wrappers/source/clock_unittest.cc34
-rw-r--r--third_party/libwebrtc/system_wrappers/source/cpu_features.cc116
-rw-r--r--third_party/libwebrtc/system_wrappers/source/cpu_features_android.cc19
-rw-r--r--third_party/libwebrtc/system_wrappers/source/cpu_features_linux.cc97
-rw-r--r--third_party/libwebrtc/system_wrappers/source/cpu_info.cc72
-rw-r--r--third_party/libwebrtc/system_wrappers/source/denormal_disabler.cc111
-rw-r--r--third_party/libwebrtc/system_wrappers/source/denormal_disabler_unittest.cc226
-rw-r--r--third_party/libwebrtc/system_wrappers/source/field_trial.cc181
-rw-r--r--third_party/libwebrtc/system_wrappers/source/field_trial_unittest.cc107
-rw-r--r--third_party/libwebrtc/system_wrappers/source/metrics.cc331
-rw-r--r--third_party/libwebrtc/system_wrappers/source/metrics_default_unittest.cc174
-rw-r--r--third_party/libwebrtc/system_wrappers/source/metrics_unittest.cc136
-rw-r--r--third_party/libwebrtc/system_wrappers/source/ntp_time_unittest.cc280
-rw-r--r--third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator.cc153
-rw-r--r--third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc277
-rw-r--r--third_party/libwebrtc/system_wrappers/source/sleep.cc36
18 files changed, 2458 insertions, 0 deletions
diff --git a/third_party/libwebrtc/system_wrappers/source/DEPS b/third_party/libwebrtc/system_wrappers/source/DEPS
new file mode 100644
index 0000000000..ac7f5a234f
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+ # TODO(bugs.webrtc.org/10335): Remove rule when global string is removed.
+ "field_trial\.cc": [
+ "+experiments/registered_field_trials.h",
+ ],
+}
diff --git a/third_party/libwebrtc/system_wrappers/source/clock.cc b/third_party/libwebrtc/system_wrappers/source/clock.cc
new file mode 100644
index 0000000000..f7460b831c
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/clock.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013 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 "system_wrappers/include/clock.h"
+
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+namespace {
+
+int64_t NtpOffsetUsCalledOnce() {
+ constexpr int64_t kNtpJan1970Sec = 2208988800;
+ int64_t clock_time = rtc::TimeMicros();
+ int64_t utc_time = rtc::TimeUTCMicros();
+ return utc_time - clock_time + kNtpJan1970Sec * rtc::kNumMicrosecsPerSec;
+}
+
+NtpTime TimeMicrosToNtp(int64_t time_us) {
+ static int64_t ntp_offset_us = NtpOffsetUsCalledOnce();
+
+ int64_t time_ntp_us = time_us + ntp_offset_us;
+ RTC_DCHECK_GE(time_ntp_us, 0); // Time before year 1900 is unsupported.
+
+ // Convert seconds to uint32 through uint64 for a well-defined cast.
+ // A wrap around, which will happen in 2036, is expected for NTP time.
+ uint32_t ntp_seconds =
+ static_cast<uint64_t>(time_ntp_us / rtc::kNumMicrosecsPerSec);
+
+ // Scale fractions of the second to NTP resolution.
+ constexpr int64_t kNtpFractionsInSecond = 1LL << 32;
+ int64_t us_fractions = time_ntp_us % rtc::kNumMicrosecsPerSec;
+ uint32_t ntp_fractions =
+ us_fractions * kNtpFractionsInSecond / rtc::kNumMicrosecsPerSec;
+
+ return NtpTime(ntp_seconds, ntp_fractions);
+}
+
+} // namespace
+
+class RealTimeClock : public Clock {
+ public:
+ RealTimeClock() = default;
+
+ Timestamp CurrentTime() override {
+ return Timestamp::Micros(rtc::TimeMicros());
+ }
+
+ NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override {
+ return TimeMicrosToNtp(timestamp.us());
+ }
+};
+
+Clock* Clock::GetRealTimeClockRaw() {
+ static Clock* const clock = new RealTimeClock();
+ return clock;
+}
+
+SimulatedClock::SimulatedClock(int64_t initial_time_us)
+ : time_us_(initial_time_us) {}
+
+SimulatedClock::SimulatedClock(Timestamp initial_time)
+ : SimulatedClock(initial_time.us()) {}
+
+SimulatedClock::~SimulatedClock() {}
+
+Timestamp SimulatedClock::CurrentTime() {
+ return Timestamp::Micros(time_us_.load(std::memory_order_relaxed));
+}
+
+NtpTime SimulatedClock::ConvertTimestampToNtpTime(Timestamp timestamp) {
+ int64_t now_us = timestamp.us();
+ uint32_t seconds = (now_us / 1'000'000) + kNtpJan1970;
+ uint32_t fractions = static_cast<uint32_t>(
+ (now_us % 1'000'000) * kMagicNtpFractionalUnit / 1'000'000);
+ return NtpTime(seconds, fractions);
+}
+
+void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
+ AdvanceTime(TimeDelta::Millis(milliseconds));
+}
+
+void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
+ AdvanceTime(TimeDelta::Micros(microseconds));
+}
+
+// TODO(bugs.webrtc.org(12102): It's desirable to let a single thread own
+// advancement of the clock. We could then replace this read-modify-write
+// operation with just a thread checker. But currently, that breaks a couple of
+// tests, in particular, RepeatingTaskTest.ClockIntegration and
+// CallStatsTest.LastProcessedRtt.
+void SimulatedClock::AdvanceTime(TimeDelta delta) {
+ time_us_.fetch_add(delta.us(), std::memory_order_relaxed);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/clock_unittest.cc b/third_party/libwebrtc/system_wrappers/source/clock_unittest.cc
new file mode 100644
index 0000000000..f7b0ed7a47
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/clock_unittest.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012 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 "system_wrappers/include/clock.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(ClockTest, NtpTime) {
+ Clock* clock = Clock::GetRealTimeClock();
+
+ // To ensure the test runs correctly even on a heavily loaded system, do not
+ // compare the seconds/fractions and millisecond values directly. Instead,
+ // we check that the NTP time is between the "milliseconds" values returned
+ // right before and right after the call.
+ // The comparison includes 1 ms of margin to account for the rounding error in
+ // the conversion.
+ int64_t milliseconds_lower_bound = clock->CurrentNtpInMilliseconds();
+ NtpTime ntp_time = clock->CurrentNtpTime();
+ int64_t milliseconds_upper_bound = clock->CurrentNtpInMilliseconds();
+ EXPECT_GT(milliseconds_lower_bound / 1000, kNtpJan1970);
+ EXPECT_LE(milliseconds_lower_bound - 1, ntp_time.ToMs());
+ EXPECT_GE(milliseconds_upper_bound + 1, ntp_time.ToMs());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/cpu_features.cc b/third_party/libwebrtc/system_wrappers/source/cpu_features.cc
new file mode 100644
index 0000000000..b676339eea
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/cpu_features.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+
+// Parts of this file derived from Chromium's base/cpu.cc.
+
+#include "rtc_base/system/arch.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+#include "system_wrappers/include/field_trial.h"
+
+#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+namespace webrtc {
+
+// No CPU feature is available => straight C path.
+int GetCPUInfoNoASM(CPUFeature feature) {
+ (void)feature;
+ return 0;
+}
+
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+
+#if defined(WEBRTC_ENABLE_AVX2)
+// xgetbv returns the value of an Intel Extended Control Register (XCR).
+// Currently only XCR0 is defined by Intel so `xcr` should always be zero.
+static uint64_t xgetbv(uint32_t xcr) {
+#if defined(_MSC_VER)
+ return _xgetbv(xcr);
+#else
+ uint32_t eax, edx;
+
+ __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
+ return (static_cast<uint64_t>(edx) << 32) | eax;
+#endif // _MSC_VER
+}
+#endif // WEBRTC_ENABLE_AVX2
+
+#ifndef _MSC_VER
+// Intrinsic for "cpuid".
+#if defined(__pic__) && defined(__i386__)
+static inline void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile(
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
+ "=d"(cpu_info[3])
+ : "a"(info_type));
+}
+#else
+static inline void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile("cpuid\n"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
+ "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+#endif
+#endif // _MSC_VER
+#endif // WEBRTC_ARCH_X86_FAMILY
+
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+// Actual feature detection for x86.
+int GetCPUInfo(CPUFeature feature) {
+ int cpu_info[4];
+ __cpuid(cpu_info, 1);
+ if (feature == kSSE2) {
+ return 0 != (cpu_info[3] & 0x04000000);
+ }
+ if (feature == kSSE3) {
+ return 0 != (cpu_info[2] & 0x00000001);
+ }
+#if defined(WEBRTC_ENABLE_AVX2)
+ if (feature == kAVX2 &&
+ !webrtc::field_trial::IsEnabled("WebRTC-Avx2SupportKillSwitch")) {
+ int cpu_info7[4];
+ __cpuid(cpu_info7, 0);
+ int num_ids = cpu_info7[0];
+ if (num_ids < 7) {
+ return 0;
+ }
+ // Interpret CPU feature information.
+ __cpuid(cpu_info7, 7);
+
+ // AVX instructions can be used when
+ // a) AVX are supported by the CPU,
+ // b) XSAVE is supported by the CPU,
+ // c) XSAVE is enabled by the kernel.
+ // Compiling with MSVC and /arch:AVX2 surprisingly generates BMI2
+ // instructions (see crbug.com/1315519).
+ return (cpu_info[2] & 0x10000000) != 0 /* AVX */ &&
+ (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
+ (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
+ (xgetbv(0) & 0x00000006) == 6 /* XSAVE enabled by kernel */ &&
+ (cpu_info7[1] & 0x00000020) != 0 /* AVX2 */ &&
+ (cpu_info7[1] & 0x00000100) != 0 /* BMI2 */;
+ }
+#endif // WEBRTC_ENABLE_AVX2
+ return 0;
+}
+#else
+// Default to straight C for other platforms.
+int GetCPUInfo(CPUFeature feature) {
+ (void)feature;
+ return 0;
+}
+#endif
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/cpu_features_android.cc b/third_party/libwebrtc/system_wrappers/source/cpu_features_android.cc
new file mode 100644
index 0000000000..95cc609b09
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/cpu_features_android.cc
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2012 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 <cpu-features.h>
+
+namespace webrtc {
+
+uint64_t GetCPUFeaturesARM(void) {
+ return android_getCpuFeatures();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/cpu_features_linux.cc b/third_party/libwebrtc/system_wrappers/source/cpu_features_linux.cc
new file mode 100644
index 0000000000..2d79dde111
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/cpu_features_linux.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016 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 <features.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __GLIBC_PREREQ
+#define WEBRTC_GLIBC_PREREQ(a, b) __GLIBC_PREREQ(a, b)
+#else
+#define WEBRTC_GLIBC_PREREQ(a, b) 0
+#endif
+
+#if WEBRTC_GLIBC_PREREQ(2, 16)
+#include <sys/auxv.h>
+#else
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <unistd.h>
+#endif
+
+#include "rtc_base/system/arch.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+
+#if defined(WEBRTC_ARCH_ARM_FAMILY)
+#include <asm/hwcap.h>
+
+namespace webrtc {
+
+uint64_t GetCPUFeaturesARM(void) {
+ uint64_t result = 0;
+ int architecture = 0;
+ uint64_t hwcap = 0;
+ const char* platform = NULL;
+#if WEBRTC_GLIBC_PREREQ(2, 16)
+ hwcap = getauxval(AT_HWCAP);
+ platform = (const char*)getauxval(AT_PLATFORM);
+#else
+ ElfW(auxv_t) auxv;
+ int fd = open("/proc/self/auxv", O_RDONLY);
+ if (fd >= 0) {
+ while (hwcap == 0 || platform == NULL) {
+ if (read(fd, &auxv, sizeof(auxv)) < (ssize_t)sizeof(auxv)) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ switch (auxv.a_type) {
+ case AT_HWCAP:
+ hwcap = auxv.a_un.a_val;
+ break;
+ case AT_PLATFORM:
+ platform = (const char*)auxv.a_un.a_val;
+ break;
+ }
+ }
+ close(fd);
+ }
+#endif // WEBRTC_GLIBC_PREREQ(2, 16)
+#if defined(__aarch64__)
+ (void)platform;
+ architecture = 8;
+ if ((hwcap & HWCAP_FP) != 0)
+ result |= kCPUFeatureVFPv3;
+ if ((hwcap & HWCAP_ASIMD) != 0)
+ result |= kCPUFeatureNEON;
+#else
+ if (platform != NULL) {
+ /* expect a string in the form "v6l" or "v7l", etc.
+ */
+ if (platform[0] == 'v' && '0' <= platform[1] && platform[1] <= '9' &&
+ (platform[2] == 'l' || platform[2] == 'b')) {
+ architecture = platform[1] - '0';
+ }
+ }
+ if ((hwcap & HWCAP_VFPv3) != 0)
+ result |= kCPUFeatureVFPv3;
+ if ((hwcap & HWCAP_NEON) != 0)
+ result |= kCPUFeatureNEON;
+#endif
+ if (architecture >= 7)
+ result |= kCPUFeatureARMv7;
+ if (architecture >= 6)
+ result |= kCPUFeatureLDREXSTREX;
+ return result;
+}
+
+} // namespace webrtc
+#endif // WEBRTC_ARCH_ARM_FAMILY
diff --git a/third_party/libwebrtc/system_wrappers/source/cpu_info.cc b/third_party/libwebrtc/system_wrappers/source/cpu_info.cc
new file mode 100644
index 0000000000..94aed09c48
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/cpu_info.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 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 "system_wrappers/include/cpu_info.h"
+
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+#elif defined(WEBRTC_LINUX) || defined(WEBRTC_BSD)
+#include <unistd.h>
+#elif defined(WEBRTC_MAC)
+#include <sys/sysctl.h>
+#elif defined(WEBRTC_FUCHSIA)
+#include <zircon/syscalls.h>
+#endif
+
+#include "rtc_base/logging.h"
+
+namespace internal {
+static int DetectNumberOfCores() {
+ int number_of_cores;
+
+#if defined(WEBRTC_WIN)
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ number_of_cores = static_cast<int>(si.dwNumberOfProcessors);
+#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) || defined(WEBRTC_BSD)
+ number_of_cores = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
+ if (number_of_cores <= 0) {
+ RTC_LOG(LS_ERROR) << "Failed to get number of cores";
+ number_of_cores = 1;
+ }
+#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
+ int name[] = {CTL_HW, HW_AVAILCPU};
+ size_t size = sizeof(number_of_cores);
+ if (0 != sysctl(name, 2, &number_of_cores, &size, NULL, 0)) {
+ RTC_LOG(LS_ERROR) << "Failed to get number of cores";
+ number_of_cores = 1;
+ }
+#elif defined(WEBRTC_FUCHSIA)
+ number_of_cores = zx_system_get_num_cpus();
+#else
+ RTC_LOG(LS_ERROR) << "No function to get number of cores";
+ number_of_cores = 1;
+#endif
+
+ RTC_LOG(LS_INFO) << "Available number of cores: " << number_of_cores;
+
+ RTC_CHECK_GT(number_of_cores, 0);
+ return number_of_cores;
+}
+} // namespace internal
+
+namespace webrtc {
+
+uint32_t CpuInfo::DetectNumberOfCores() {
+ // Statically cache the number of system cores available since if the process
+ // is running in a sandbox, we may only be able to read the value once (before
+ // the sandbox is initialized) and not thereafter.
+ // For more information see crbug.com/176522.
+ static const uint32_t logical_cpus =
+ static_cast<uint32_t>(internal::DetectNumberOfCores());
+ return logical_cpus;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/denormal_disabler.cc b/third_party/libwebrtc/system_wrappers/source/denormal_disabler.cc
new file mode 100644
index 0000000000..bb9c05643c
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/denormal_disabler.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2021 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 "system_wrappers/include/denormal_disabler.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+
+#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(__clang__)
+#define WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED
+#endif
+
+#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED) || \
+ defined(WEBRTC_ARCH_ARM_FAMILY)
+#define WEBRTC_DENORMAL_DISABLER_SUPPORTED
+#endif
+
+constexpr int kUnspecifiedStatusWord = -1;
+
+#if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED)
+
+// Control register bit mask to disable denormals on the hardware.
+#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED)
+// On x86 two bits are used: flush-to-zero (FTZ) and denormals-are-zero (DAZ).
+constexpr int kDenormalBitMask = 0x8040;
+#elif defined(WEBRTC_ARCH_ARM_FAMILY)
+// On ARM one bit is used: flush-to-zero (FTZ).
+constexpr int kDenormalBitMask = 1 << 24;
+#endif
+
+// Reads the relevant CPU control register and returns its value for supported
+// architectures and compilers. Otherwise returns `kUnspecifiedStatusWord`.
+int ReadStatusWord() {
+ int result = kUnspecifiedStatusWord;
+#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED)
+ asm volatile("stmxcsr %0" : "=m"(result));
+#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS)
+ asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result));
+#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS)
+ asm volatile("mrs %x[result], FPCR" : [result] "=r"(result));
+#endif
+ return result;
+}
+
+// Writes `status_word` in the relevant CPU control register if the architecture
+// and the compiler are supported.
+void SetStatusWord(int status_word) {
+#if defined(WEBRTC_DENORMAL_DISABLER_X86_SUPPORTED)
+ asm volatile("ldmxcsr %0" : : "m"(status_word));
+#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_32_BITS)
+ asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(status_word));
+#elif defined(WEBRTC_ARCH_ARM_FAMILY) && defined(WEBRTC_ARCH_64_BITS)
+ asm volatile("msr FPCR, %x[src]" : : [src] "r"(status_word));
+#endif
+}
+
+// Returns true if the status word indicates that denormals are enabled.
+constexpr bool DenormalsEnabled(int status_word) {
+ return (status_word & kDenormalBitMask) != kDenormalBitMask;
+}
+
+#endif // defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED)
+
+} // namespace
+
+#if defined(WEBRTC_DENORMAL_DISABLER_SUPPORTED)
+DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/true) {}
+
+DenormalDisabler::DenormalDisabler(bool enabled)
+ : status_word_(enabled ? ReadStatusWord() : kUnspecifiedStatusWord),
+ disabling_activated_(enabled && DenormalsEnabled(status_word_)) {
+ if (disabling_activated_) {
+ RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord);
+ SetStatusWord(status_word_ | kDenormalBitMask);
+ RTC_DCHECK(!DenormalsEnabled(ReadStatusWord()));
+ }
+}
+
+bool DenormalDisabler::IsSupported() {
+ return true;
+}
+
+DenormalDisabler::~DenormalDisabler() {
+ if (disabling_activated_) {
+ RTC_DCHECK_NE(status_word_, kUnspecifiedStatusWord);
+ SetStatusWord(status_word_);
+ }
+}
+#else
+DenormalDisabler::DenormalDisabler() : DenormalDisabler(/*enabled=*/false) {}
+
+DenormalDisabler::DenormalDisabler(bool enabled)
+ : status_word_(kUnspecifiedStatusWord), disabling_activated_(false) {}
+
+bool DenormalDisabler::IsSupported() {
+ return false;
+}
+
+DenormalDisabler::~DenormalDisabler() = default;
+#endif
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/denormal_disabler_unittest.cc b/third_party/libwebrtc/system_wrappers/source/denormal_disabler_unittest.cc
new file mode 100644
index 0000000000..a2849f853f
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/denormal_disabler_unittest.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2021 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 "system_wrappers/include/denormal_disabler.h"
+
+#include <cmath>
+#include <limits>
+#include <vector>
+
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr float kSmallest = std::numeric_limits<float>::min();
+
+// Float values such that, if used as divisors of `kSmallest`, the division
+// produces a denormal or zero depending on whether denormals are enabled.
+constexpr float kDenormalDivisors[] = {123.125f, 97.0f, 32.0f, 5.0f, 1.5f};
+
+// Returns true if the result of `dividend` / `divisor` is a denormal.
+// `dividend` and `divisor` must not be denormals.
+bool DivisionIsDenormal(float dividend, float divisor) {
+ RTC_DCHECK_GE(std::fabsf(dividend), kSmallest);
+ RTC_DCHECK_GE(std::fabsf(divisor), kSmallest);
+ volatile float division = dividend / divisor;
+ return division != 0.0f && std::fabsf(division) < kSmallest;
+}
+
+} // namespace
+
+class DenormalDisablerParametrization : public ::testing::TestWithParam<bool> {
+};
+
+// Checks that +inf and -inf are not zeroed regardless of whether
+// architecture and compiler are supported.
+TEST_P(DenormalDisablerParametrization, InfNotZeroedExplicitlySetEnabled) {
+ DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
+ constexpr float kMax = std::numeric_limits<float>::max();
+ for (float x : {-2.0f, 2.0f}) {
+ SCOPED_TRACE(x);
+ volatile float multiplication = kMax * x;
+ EXPECT_TRUE(std::isinf(multiplication));
+ }
+}
+
+// Checks that a NaN is not zeroed regardless of whether architecture and
+// compiler are supported.
+TEST_P(DenormalDisablerParametrization, NanNotZeroedExplicitlySetEnabled) {
+ DenormalDisabler denormal_disabler(/*enabled=*/GetParam());
+ volatile float kNan = std::sqrt(-1.0f);
+ EXPECT_TRUE(std::isnan(kNan));
+}
+
+INSTANTIATE_TEST_SUITE_P(DenormalDisabler,
+ DenormalDisablerParametrization,
+ ::testing::Values(false, true),
+ [](const ::testing::TestParamInfo<bool>& info) {
+ return info.param ? "enabled" : "disabled";
+ });
+
+// Checks that +inf and -inf are not zeroed regardless of whether
+// architecture and compiler are supported.
+TEST(DenormalDisabler, InfNotZeroed) {
+ DenormalDisabler denormal_disabler;
+ constexpr float kMax = std::numeric_limits<float>::max();
+ for (float x : {-2.0f, 2.0f}) {
+ SCOPED_TRACE(x);
+ volatile float multiplication = kMax * x;
+ EXPECT_TRUE(std::isinf(multiplication));
+ }
+}
+
+// Checks that a NaN is not zeroed regardless of whether architecture and
+// compiler are supported.
+TEST(DenormalDisabler, NanNotZeroed) {
+ DenormalDisabler denormal_disabler;
+ volatile float kNan = std::sqrt(-1.0f);
+ EXPECT_TRUE(std::isnan(kNan));
+}
+
+// Checks that denormals are not zeroed if `DenormalDisabler` is disabled and
+// architecture and compiler are supported.
+TEST(DenormalDisabler, DoNotZeroDenormalsIfDisabled) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
+ << "Precondition not met: denormals must be enabled.";
+ DenormalDisabler denormal_disabler(/*enabled=*/false);
+ for (float x : kDenormalDivisors) {
+ SCOPED_TRACE(x);
+ EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
+ EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
+ }
+}
+
+// Checks that denormals are zeroed if `DenormalDisabler` is enabled if
+// architecture and compiler are supported.
+TEST(DenormalDisabler, ZeroDenormals) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ DenormalDisabler denormal_disabler;
+ for (float x : kDenormalDivisors) {
+ SCOPED_TRACE(x);
+ EXPECT_FALSE(DivisionIsDenormal(-kSmallest, x));
+ EXPECT_FALSE(DivisionIsDenormal(kSmallest, x));
+ }
+}
+
+// Checks that denormals are zeroed if `DenormalDisabler` is enabled if
+// architecture and compiler are supported.
+TEST(DenormalDisabler, ZeroDenormalsExplicitlyEnabled) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ DenormalDisabler denormal_disabler(/*enabled=*/true);
+ for (float x : kDenormalDivisors) {
+ SCOPED_TRACE(x);
+ EXPECT_FALSE(DivisionIsDenormal(-kSmallest, x));
+ EXPECT_FALSE(DivisionIsDenormal(kSmallest, x));
+ }
+}
+
+// Checks that the `DenormalDisabler` dtor re-enables denormals if previously
+// enabled and architecture and compiler are supported.
+TEST(DenormalDisabler, RestoreDenormalsEnabled) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
+ << "Precondition not met: denormals must be enabled.";
+ {
+ DenormalDisabler denormal_disabler;
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ }
+ EXPECT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+}
+
+// Checks that the `DenormalDisabler` dtor re-enables denormals if previously
+// enabled and architecture and compiler are supported.
+TEST(DenormalDisabler, RestoreDenormalsEnabledExplicitlyEnabled) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ ASSERT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]))
+ << "Precondition not met: denormals must be enabled.";
+ {
+ DenormalDisabler denormal_disabler(/*enabled=*/true);
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ }
+ EXPECT_TRUE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+}
+
+// Checks that the `DenormalDisabler` dtor keeps denormals disabled if
+// architecture and compiler are supported and if previously disabled - i.e.,
+// nested usage is supported.
+TEST(DenormalDisabler, ZeroDenormalsNested) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ DenormalDisabler d1;
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ {
+ DenormalDisabler d2;
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ }
+ EXPECT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+}
+
+// Checks that the `DenormalDisabler` dtor keeps denormals disabled if
+// architecture and compiler are supported and if previously disabled - i.e.,
+// nested usage is supported.
+TEST(DenormalDisabler, ZeroDenormalsNestedExplicitlyEnabled) {
+ if (!DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "Unsupported platform.";
+ }
+ DenormalDisabler d1(/*enabled=*/true);
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ {
+ DenormalDisabler d2(/*enabled=*/true);
+ ASSERT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+ }
+ EXPECT_FALSE(DivisionIsDenormal(kSmallest, kDenormalDivisors[0]));
+}
+
+// Checks that `DenormalDisabler` does not zero denormals if architecture and
+// compiler are not supported.
+TEST(DenormalDisabler, DoNotZeroDenormalsIfUnsupported) {
+ if (DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "This test should only run on platforms without support "
+ "for DenormalDisabler.";
+ }
+ DenormalDisabler denormal_disabler;
+ for (float x : kDenormalDivisors) {
+ SCOPED_TRACE(x);
+ EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
+ EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
+ }
+}
+
+// Checks that `DenormalDisabler` does not zero denormals if architecture and
+// compiler are not supported.
+TEST(DenormalDisabler, DoNotZeroDenormalsIfUnsupportedExplicitlyEnabled) {
+ if (DenormalDisabler::IsSupported()) {
+ GTEST_SKIP() << "This test should only run on platforms without support "
+ "for DenormalDisabler.";
+ }
+ DenormalDisabler denormal_disabler(/*enabled=*/true);
+ for (float x : kDenormalDivisors) {
+ SCOPED_TRACE(x);
+ EXPECT_TRUE(DivisionIsDenormal(-kSmallest, x));
+ EXPECT_TRUE(DivisionIsDenormal(kSmallest, x));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/field_trial.cc b/third_party/libwebrtc/system_wrappers/source/field_trial.cc
new file mode 100644
index 0000000000..8f15b4eb7a
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/field_trial.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2014 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 "system_wrappers/include/field_trial.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/string_view.h"
+#include "experiments/registered_field_trials.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/containers/flat_set.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_encode.h"
+
+// Simple field trial implementation, which allows client to
+// specify desired flags in InitFieldTrialsFromString.
+namespace webrtc {
+namespace field_trial {
+
+static const char* trials_init_string = NULL;
+
+namespace {
+
+constexpr char kPersistentStringSeparator = '/';
+
+flat_set<std::string>& TestKeys() {
+ static auto* test_keys = new flat_set<std::string>();
+ return *test_keys;
+}
+
+// Validates the given field trial string.
+// E.g.:
+// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/"
+// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial
+// and to group "Enabled100kbps" on WebRTCExperimentBar.
+//
+// E.g. invalid config:
+// "WebRTC-experiment1/Enabled" (note missing / separator at the end).
+bool FieldTrialsStringIsValidInternal(const absl::string_view trials) {
+ if (trials.empty())
+ return true;
+
+ size_t next_item = 0;
+ std::map<absl::string_view, absl::string_view> field_trials;
+ while (next_item < trials.length()) {
+ size_t name_end = trials.find(kPersistentStringSeparator, next_item);
+ if (name_end == trials.npos || next_item == name_end)
+ return false;
+ size_t group_name_end =
+ trials.find(kPersistentStringSeparator, name_end + 1);
+ if (group_name_end == trials.npos || name_end + 1 == group_name_end)
+ return false;
+ absl::string_view name = trials.substr(next_item, name_end - next_item);
+ absl::string_view group_name =
+ trials.substr(name_end + 1, group_name_end - name_end - 1);
+
+ next_item = group_name_end + 1;
+
+ // Fail if duplicate with different group name.
+ if (field_trials.find(name) != field_trials.end() &&
+ field_trials.find(name)->second != group_name) {
+ return false;
+ }
+
+ field_trials[name] = group_name;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool FieldTrialsStringIsValid(absl::string_view trials_string) {
+ return FieldTrialsStringIsValidInternal(trials_string);
+}
+
+void InsertOrReplaceFieldTrialStringsInMap(
+ std::map<std::string, std::string>* fieldtrial_map,
+ const absl::string_view trials_string) {
+ if (FieldTrialsStringIsValidInternal(trials_string)) {
+ std::vector<absl::string_view> tokens = rtc::split(trials_string, '/');
+ // Skip last token which is empty due to trailing '/'.
+ for (size_t idx = 0; idx < tokens.size() - 1; idx += 2) {
+ (*fieldtrial_map)[std::string(tokens[idx])] =
+ std::string(tokens[idx + 1]);
+ }
+ } else {
+ RTC_DCHECK_NOTREACHED() << "Invalid field trials string:" << trials_string;
+ }
+}
+
+std::string MergeFieldTrialsStrings(absl::string_view first,
+ absl::string_view second) {
+ std::map<std::string, std::string> fieldtrial_map;
+ InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, first);
+ InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, second);
+
+ // Merge into fieldtrial string.
+ std::string merged = "";
+ for (auto const& fieldtrial : fieldtrial_map) {
+ merged += fieldtrial.first + '/' + fieldtrial.second + '/';
+ }
+ return merged;
+}
+
+#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT
+std::string FindFullName(absl::string_view name) {
+#if WEBRTC_STRICT_FIELD_TRIALS
+ RTC_DCHECK(absl::c_linear_search(kRegisteredFieldTrials, name) ||
+ TestKeys().contains(name))
+ << name << " is not registered.";
+#endif
+
+ if (trials_init_string == NULL)
+ return std::string();
+
+ absl::string_view trials_string(trials_init_string);
+ if (trials_string.empty())
+ return std::string();
+
+ size_t next_item = 0;
+ while (next_item < trials_string.length()) {
+ // Find next name/value pair in field trial configuration string.
+ size_t field_name_end =
+ trials_string.find(kPersistentStringSeparator, next_item);
+ if (field_name_end == trials_string.npos || field_name_end == next_item)
+ break;
+ size_t field_value_end =
+ trials_string.find(kPersistentStringSeparator, field_name_end + 1);
+ if (field_value_end == trials_string.npos ||
+ field_value_end == field_name_end + 1)
+ break;
+ absl::string_view field_name =
+ trials_string.substr(next_item, field_name_end - next_item);
+ absl::string_view field_value = trials_string.substr(
+ field_name_end + 1, field_value_end - field_name_end - 1);
+ next_item = field_value_end + 1;
+
+ if (name == field_name)
+ return std::string(field_value);
+ }
+ return std::string();
+}
+#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT
+
+// Optionally initialize field trial from a string.
+void InitFieldTrialsFromString(const char* trials_string) {
+ RTC_LOG(LS_INFO) << "Setting field trial string:" << trials_string;
+ if (trials_string) {
+ RTC_DCHECK(FieldTrialsStringIsValidInternal(trials_string))
+ << "Invalid field trials string:" << trials_string;
+ };
+ trials_init_string = trials_string;
+}
+
+const char* GetFieldTrialString() {
+ return trials_init_string;
+}
+
+FieldTrialsAllowedInScopeForTesting::FieldTrialsAllowedInScopeForTesting(
+ flat_set<std::string> keys) {
+ TestKeys() = std::move(keys);
+}
+
+FieldTrialsAllowedInScopeForTesting::~FieldTrialsAllowedInScopeForTesting() {
+ TestKeys().clear();
+}
+
+} // namespace field_trial
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/field_trial_unittest.cc b/third_party/libwebrtc/system_wrappers/source/field_trial_unittest.cc
new file mode 100644
index 0000000000..ada6313e67
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/field_trial_unittest.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019 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 "system_wrappers/include/field_trial.h"
+
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+#include "test/testsupport/rtc_expect_death.h"
+
+namespace webrtc {
+namespace field_trial {
+#if GTEST_HAS_DEATH_TEST && RTC_DCHECK_IS_ON && !defined(WEBRTC_ANDROID) && \
+ !defined(WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT)
+TEST(FieldTrialValidationTest, AcceptsValidInputs) {
+ InitFieldTrialsFromString("");
+ InitFieldTrialsFromString("Audio/Enabled/");
+ InitFieldTrialsFromString("Audio/Enabled/Video/Disabled/");
+ EXPECT_TRUE(FieldTrialsStringIsValid(""));
+ EXPECT_TRUE(FieldTrialsStringIsValid("Audio/Enabled/"));
+ EXPECT_TRUE(FieldTrialsStringIsValid("Audio/Enabled/Video/Disabled/"));
+
+ // Duplicate trials with the same value is fine
+ InitFieldTrialsFromString("Audio/Enabled/Audio/Enabled/");
+ InitFieldTrialsFromString("Audio/Enabled/B/C/Audio/Enabled/");
+ EXPECT_TRUE(FieldTrialsStringIsValid("Audio/Enabled/Audio/Enabled/"));
+ EXPECT_TRUE(FieldTrialsStringIsValid("Audio/Enabled/B/C/Audio/Enabled/"));
+}
+
+TEST(FieldTrialValidationDeathTest, RejectsBadInputs) {
+ // Bad delimiters
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("Audio/EnabledVideo/Disabled/"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("Audio/Enabled//Video/Disabled/"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("/Audio/Enabled/Video/Disabled/"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("Audio/Enabled/Video/Disabled"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(
+ InitFieldTrialsFromString("Audio/Enabled/Video/Disabled/garbage"),
+ "Invalid field trials string:");
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio/EnabledVideo/Disabled/"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio/Enabled//Video/Disabled/"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("/Audio/Enabled/Video/Disabled/"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio/Enabled/Video/Disabled"));
+ EXPECT_FALSE(
+ FieldTrialsStringIsValid("Audio/Enabled/Video/Disabled/garbage"));
+
+ // Empty trial or group
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("Audio//"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("/Enabled/"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("//"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("//Enabled"),
+ "Invalid field trials string:");
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio//"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("/Enabled/"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("//"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("//Enabled"));
+
+ // Duplicate trials with different values is not fine
+ RTC_EXPECT_DEATH(InitFieldTrialsFromString("Audio/Enabled/Audio/Disabled/"),
+ "Invalid field trials string:");
+ RTC_EXPECT_DEATH(
+ InitFieldTrialsFromString("Audio/Enabled/B/C/Audio/Disabled/"),
+ "Invalid field trials string:");
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio/Enabled/Audio/Disabled/"));
+ EXPECT_FALSE(FieldTrialsStringIsValid("Audio/Enabled/B/C/Audio/Disabled/"));
+}
+
+TEST(FieldTrialMergingTest, MergesValidInput) {
+ EXPECT_EQ(MergeFieldTrialsStrings("Video/Enabled/", "Audio/Enabled/"),
+ "Audio/Enabled/Video/Enabled/");
+ EXPECT_EQ(MergeFieldTrialsStrings("Audio/Disabled/Video/Enabled/",
+ "Audio/Enabled/"),
+ "Audio/Enabled/Video/Enabled/");
+ EXPECT_EQ(
+ MergeFieldTrialsStrings("Audio/Enabled/Video/Enabled/", "Audio/Enabled/"),
+ "Audio/Enabled/Video/Enabled/");
+ EXPECT_EQ(
+ MergeFieldTrialsStrings("Audio/Enabled/Audio/Enabled/", "Video/Enabled/"),
+ "Audio/Enabled/Video/Enabled/");
+}
+
+TEST(FieldTrialMergingDeathTest, DchecksBadInput) {
+ RTC_EXPECT_DEATH(MergeFieldTrialsStrings("Audio/Enabled/", "garbage"),
+ "Invalid field trials string:");
+}
+
+TEST(FieldTrialMergingTest, HandlesEmptyInput) {
+ EXPECT_EQ(MergeFieldTrialsStrings("", "Audio/Enabled/"), "Audio/Enabled/");
+ EXPECT_EQ(MergeFieldTrialsStrings("Audio/Enabled/", ""), "Audio/Enabled/");
+ EXPECT_EQ(MergeFieldTrialsStrings("", ""), "");
+}
+#endif // GTEST_HAS_DEATH_TEST && RTC_DCHECK_IS_ON && !defined(WEBRTC_ANDROID)
+ // && !defined(WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT)
+
+} // namespace field_trial
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/metrics.cc b/third_party/libwebrtc/system_wrappers/source/metrics.cc
new file mode 100644
index 0000000000..39ca590070
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/metrics.cc
@@ -0,0 +1,331 @@
+// Copyright (c) 2014 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 "system_wrappers/include/metrics.h"
+
+#include <algorithm>
+
+#include "absl/strings/string_view.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/thread_annotations.h"
+
+// Default implementation of histogram methods for WebRTC clients that do not
+// want to provide their own implementation.
+
+namespace webrtc {
+namespace metrics {
+class Histogram;
+
+namespace {
+// Limit for the maximum number of sample values that can be stored.
+// TODO(asapersson): Consider using bucket count (and set up
+// linearly/exponentially spaced buckets) if samples are logged more frequently.
+const int kMaxSampleMapSize = 300;
+
+class RtcHistogram {
+ public:
+ RtcHistogram(absl::string_view name, int min, int max, int bucket_count)
+ : min_(min), max_(max), info_(name, min, max, bucket_count) {
+ RTC_DCHECK_GT(bucket_count, 0);
+ }
+
+ RtcHistogram(const RtcHistogram&) = delete;
+ RtcHistogram& operator=(const RtcHistogram&) = delete;
+
+ void Add(int sample) {
+ sample = std::min(sample, max_);
+ sample = std::max(sample, min_ - 1); // Underflow bucket.
+
+ MutexLock lock(&mutex_);
+ if (info_.samples.size() == kMaxSampleMapSize &&
+ info_.samples.find(sample) == info_.samples.end()) {
+ return;
+ }
+ ++info_.samples[sample];
+ }
+
+ // Returns a copy (or nullptr if there are no samples) and clears samples.
+ std::unique_ptr<SampleInfo> GetAndReset() {
+ MutexLock lock(&mutex_);
+ if (info_.samples.empty())
+ return nullptr;
+
+ SampleInfo* copy =
+ new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count);
+
+ std::swap(info_.samples, copy->samples);
+
+ return std::unique_ptr<SampleInfo>(copy);
+ }
+
+ const std::string& name() const { return info_.name; }
+
+ // Functions only for testing.
+ void Reset() {
+ MutexLock lock(&mutex_);
+ info_.samples.clear();
+ }
+
+ int NumEvents(int sample) const {
+ MutexLock lock(&mutex_);
+ const auto it = info_.samples.find(sample);
+ return (it == info_.samples.end()) ? 0 : it->second;
+ }
+
+ int NumSamples() const {
+ int num_samples = 0;
+ MutexLock lock(&mutex_);
+ for (const auto& sample : info_.samples) {
+ num_samples += sample.second;
+ }
+ return num_samples;
+ }
+
+ int MinSample() const {
+ MutexLock lock(&mutex_);
+ return (info_.samples.empty()) ? -1 : info_.samples.begin()->first;
+ }
+
+ std::map<int, int> Samples() const {
+ MutexLock lock(&mutex_);
+ return info_.samples;
+ }
+
+ private:
+ mutable Mutex mutex_;
+ const int min_;
+ const int max_;
+ SampleInfo info_ RTC_GUARDED_BY(mutex_);
+};
+
+class RtcHistogramMap {
+ public:
+ RtcHistogramMap() {}
+ ~RtcHistogramMap() {}
+
+ RtcHistogramMap(const RtcHistogramMap&) = delete;
+ RtcHistogramMap& operator=(const RtcHistogramMap&) = delete;
+
+ Histogram* GetCountsHistogram(absl::string_view name,
+ int min,
+ int max,
+ int bucket_count) {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ if (it != map_.end())
+ return reinterpret_cast<Histogram*>(it->second.get());
+
+ RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count);
+ map_.emplace(name, hist);
+ return reinterpret_cast<Histogram*>(hist);
+ }
+
+ Histogram* GetEnumerationHistogram(absl::string_view name, int boundary) {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ if (it != map_.end())
+ return reinterpret_cast<Histogram*>(it->second.get());
+
+ RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1);
+ map_.emplace(name, hist);
+ return reinterpret_cast<Histogram*>(hist);
+ }
+
+ void GetAndReset(std::map<std::string,
+ std::unique_ptr<SampleInfo>,
+ rtc::AbslStringViewCmp>* histograms) {
+ MutexLock lock(&mutex_);
+ for (const auto& kv : map_) {
+ std::unique_ptr<SampleInfo> info = kv.second->GetAndReset();
+ if (info)
+ histograms->insert(std::make_pair(kv.first, std::move(info)));
+ }
+ }
+
+ // Functions only for testing.
+ void Reset() {
+ MutexLock lock(&mutex_);
+ for (const auto& kv : map_)
+ kv.second->Reset();
+ }
+
+ int NumEvents(absl::string_view name, int sample) const {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ return (it == map_.end()) ? 0 : it->second->NumEvents(sample);
+ }
+
+ int NumSamples(absl::string_view name) const {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ return (it == map_.end()) ? 0 : it->second->NumSamples();
+ }
+
+ int MinSample(absl::string_view name) const {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ return (it == map_.end()) ? -1 : it->second->MinSample();
+ }
+
+ std::map<int, int> Samples(absl::string_view name) const {
+ MutexLock lock(&mutex_);
+ const auto& it = map_.find(name);
+ return (it == map_.end()) ? std::map<int, int>() : it->second->Samples();
+ }
+
+ private:
+ mutable Mutex mutex_;
+ std::map<std::string, std::unique_ptr<RtcHistogram>, rtc::AbslStringViewCmp>
+ map_ RTC_GUARDED_BY(mutex_);
+};
+
+// RtcHistogramMap is allocated upon call to Enable().
+// The histogram getter functions, which return pointer values to the histograms
+// in the map, are cached in WebRTC. Therefore, this memory is not freed by the
+// application (the memory will be reclaimed by the OS).
+static std::atomic<RtcHistogramMap*> g_rtc_histogram_map(nullptr);
+
+void CreateMap() {
+ RtcHistogramMap* map = g_rtc_histogram_map.load(std::memory_order_acquire);
+ if (map == nullptr) {
+ RtcHistogramMap* new_map = new RtcHistogramMap();
+ if (!g_rtc_histogram_map.compare_exchange_strong(map, new_map))
+ delete new_map;
+ }
+}
+
+// Set the first time we start using histograms. Used to make sure Enable() is
+// not called thereafter.
+#if RTC_DCHECK_IS_ON
+static std::atomic<int> g_rtc_histogram_called(0);
+#endif
+
+// Gets the map (or nullptr).
+RtcHistogramMap* GetMap() {
+#if RTC_DCHECK_IS_ON
+ g_rtc_histogram_called.store(1, std::memory_order_release);
+#endif
+ return g_rtc_histogram_map.load();
+}
+} // namespace
+
+#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT
+// Implementation of histogram methods in
+// webrtc/system_wrappers/interface/metrics.h.
+
+// Histogram with exponentially spaced buckets.
+// Creates (or finds) histogram.
+// The returned histogram pointer is cached (and used for adding samples in
+// subsequent calls).
+Histogram* HistogramFactoryGetCounts(absl::string_view name,
+ int min,
+ int max,
+ int bucket_count) {
+ // TODO(asapersson): Alternative implementation will be needed if this
+ // histogram type should be truly exponential.
+ return HistogramFactoryGetCountsLinear(name, min, max, bucket_count);
+}
+
+// Histogram with linearly spaced buckets.
+// Creates (or finds) histogram.
+// The returned histogram pointer is cached (and used for adding samples in
+// subsequent calls).
+Histogram* HistogramFactoryGetCountsLinear(absl::string_view name,
+ int min,
+ int max,
+ int bucket_count) {
+ RtcHistogramMap* map = GetMap();
+ if (!map)
+ return nullptr;
+
+ return map->GetCountsHistogram(name, min, max, bucket_count);
+}
+
+// Histogram with linearly spaced buckets.
+// Creates (or finds) histogram.
+// The returned histogram pointer is cached (and used for adding samples in
+// subsequent calls).
+Histogram* HistogramFactoryGetEnumeration(absl::string_view name,
+ int boundary) {
+ RtcHistogramMap* map = GetMap();
+ if (!map)
+ return nullptr;
+
+ return map->GetEnumerationHistogram(name, boundary);
+}
+
+// Our default implementation reuses the non-sparse histogram.
+Histogram* SparseHistogramFactoryGetEnumeration(absl::string_view name,
+ int boundary) {
+ return HistogramFactoryGetEnumeration(name, boundary);
+}
+
+// Fast path. Adds `sample` to cached `histogram_pointer`.
+void HistogramAdd(Histogram* histogram_pointer, int sample) {
+ RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer);
+ ptr->Add(sample);
+}
+
+#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT
+
+SampleInfo::SampleInfo(absl::string_view name,
+ int min,
+ int max,
+ size_t bucket_count)
+ : name(name), min(min), max(max), bucket_count(bucket_count) {}
+
+SampleInfo::~SampleInfo() {}
+
+// Implementation of global functions in metrics.h.
+void Enable() {
+ RTC_DCHECK(g_rtc_histogram_map.load() == nullptr);
+#if RTC_DCHECK_IS_ON
+ RTC_DCHECK_EQ(0, g_rtc_histogram_called.load(std::memory_order_acquire));
+#endif
+ CreateMap();
+}
+
+void GetAndReset(
+ std::map<std::string, std::unique_ptr<SampleInfo>, rtc::AbslStringViewCmp>*
+ histograms) {
+ histograms->clear();
+ RtcHistogramMap* map = GetMap();
+ if (map)
+ map->GetAndReset(histograms);
+}
+
+void Reset() {
+ RtcHistogramMap* map = GetMap();
+ if (map)
+ map->Reset();
+}
+
+int NumEvents(absl::string_view name, int sample) {
+ RtcHistogramMap* map = GetMap();
+ return map ? map->NumEvents(name, sample) : 0;
+}
+
+int NumSamples(absl::string_view name) {
+ RtcHistogramMap* map = GetMap();
+ return map ? map->NumSamples(name) : 0;
+}
+
+int MinSample(absl::string_view name) {
+ RtcHistogramMap* map = GetMap();
+ return map ? map->MinSample(name) : -1;
+}
+
+std::map<int, int> Samples(absl::string_view name) {
+ RtcHistogramMap* map = GetMap();
+ return map ? map->Samples(name) : std::map<int, int>();
+}
+
+} // namespace metrics
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/metrics_default_unittest.cc b/third_party/libwebrtc/system_wrappers/source/metrics_default_unittest.cc
new file mode 100644
index 0000000000..a27e9038a3
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/metrics_default_unittest.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2016 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 <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/string_utils.h"
+#include "system_wrappers/include/metrics.h"
+#include "test/gtest.h"
+
+#if RTC_METRICS_ENABLED
+namespace webrtc {
+
+namespace {
+const int kSample = 22;
+const char kName[] = "Name";
+
+int NumSamples(absl::string_view name,
+ const std::map<std::string,
+ std::unique_ptr<metrics::SampleInfo>,
+ rtc::AbslStringViewCmp>& histograms) {
+ const auto it = histograms.find(name);
+ if (it == histograms.end())
+ return 0;
+
+ int num_samples = 0;
+ for (const auto& sample : it->second->samples)
+ num_samples += sample.second;
+
+ return num_samples;
+}
+
+int NumEvents(absl::string_view name,
+ int sample,
+ const std::map<std::string,
+ std::unique_ptr<metrics::SampleInfo>,
+ rtc::AbslStringViewCmp>& histograms) {
+ const auto it = histograms.find(name);
+ if (it == histograms.end())
+ return 0;
+
+ const auto it_sample = it->second->samples.find(sample);
+ if (it_sample == it->second->samples.end())
+ return 0;
+
+ return it_sample->second;
+}
+} // namespace
+
+class MetricsDefaultTest : public ::testing::Test {
+ public:
+ MetricsDefaultTest() {}
+
+ protected:
+ void SetUp() override { metrics::Reset(); }
+};
+
+TEST_F(MetricsDefaultTest, Reset) {
+ RTC_HISTOGRAM_PERCENTAGE(kName, kSample);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ metrics::Reset();
+ EXPECT_EQ(0, metrics::NumSamples(kName));
+}
+
+TEST_F(MetricsDefaultTest, NumSamples) {
+ RTC_HISTOGRAM_PERCENTAGE(kName, 5);
+ RTC_HISTOGRAM_PERCENTAGE(kName, 5);
+ RTC_HISTOGRAM_PERCENTAGE(kName, 10);
+ EXPECT_EQ(3, metrics::NumSamples(kName));
+ EXPECT_EQ(0, metrics::NumSamples("NonExisting"));
+}
+
+TEST_F(MetricsDefaultTest, NumEvents) {
+ RTC_HISTOGRAM_PERCENTAGE(kName, 5);
+ RTC_HISTOGRAM_PERCENTAGE(kName, 5);
+ RTC_HISTOGRAM_PERCENTAGE(kName, 10);
+ EXPECT_EQ(2, metrics::NumEvents(kName, 5));
+ EXPECT_EQ(1, metrics::NumEvents(kName, 10));
+ EXPECT_EQ(0, metrics::NumEvents(kName, 11));
+ EXPECT_EQ(0, metrics::NumEvents("NonExisting", 5));
+}
+
+TEST_F(MetricsDefaultTest, MinSample) {
+ RTC_HISTOGRAM_PERCENTAGE(kName, kSample);
+ RTC_HISTOGRAM_PERCENTAGE(kName, kSample + 1);
+ EXPECT_EQ(kSample, metrics::MinSample(kName));
+ EXPECT_EQ(-1, metrics::MinSample("NonExisting"));
+}
+
+TEST_F(MetricsDefaultTest, Overflow) {
+ const std::string kName = "Overflow";
+ // Samples should end up in overflow bucket.
+ RTC_HISTOGRAM_PERCENTAGE(kName, 101);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, 101));
+ RTC_HISTOGRAM_PERCENTAGE(kName, 102);
+ EXPECT_EQ(2, metrics::NumSamples(kName));
+ EXPECT_EQ(2, metrics::NumEvents(kName, 101));
+}
+
+TEST_F(MetricsDefaultTest, Underflow) {
+ const std::string kName = "Underflow";
+ // Samples should end up in underflow bucket.
+ RTC_HISTOGRAM_COUNTS_10000(kName, 0);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, 0));
+ RTC_HISTOGRAM_COUNTS_10000(kName, -1);
+ EXPECT_EQ(2, metrics::NumSamples(kName));
+ EXPECT_EQ(2, metrics::NumEvents(kName, 0));
+}
+
+TEST_F(MetricsDefaultTest, GetAndReset) {
+ std::map<std::string, std::unique_ptr<metrics::SampleInfo>,
+ rtc::AbslStringViewCmp>
+ histograms;
+ metrics::GetAndReset(&histograms);
+ EXPECT_EQ(0u, histograms.size());
+ RTC_HISTOGRAM_PERCENTAGE("Histogram1", 4);
+ RTC_HISTOGRAM_PERCENTAGE("Histogram1", 5);
+ RTC_HISTOGRAM_PERCENTAGE("Histogram1", 5);
+ RTC_HISTOGRAM_PERCENTAGE("Histogram2", 10);
+ EXPECT_EQ(3, metrics::NumSamples("Histogram1"));
+ EXPECT_EQ(1, metrics::NumSamples("Histogram2"));
+
+ metrics::GetAndReset(&histograms);
+ EXPECT_EQ(2u, histograms.size());
+ EXPECT_EQ(0, metrics::NumSamples("Histogram1"));
+ EXPECT_EQ(0, metrics::NumSamples("Histogram2"));
+
+ EXPECT_EQ(3, NumSamples("Histogram1", histograms));
+ EXPECT_EQ(1, NumSamples("Histogram2", histograms));
+ EXPECT_EQ(1, NumEvents("Histogram1", 4, histograms));
+ EXPECT_EQ(2, NumEvents("Histogram1", 5, histograms));
+ EXPECT_EQ(1, NumEvents("Histogram2", 10, histograms));
+
+ // Add samples after reset.
+ metrics::GetAndReset(&histograms);
+ EXPECT_EQ(0u, histograms.size());
+ RTC_HISTOGRAM_PERCENTAGE("Histogram1", 50);
+ RTC_HISTOGRAM_PERCENTAGE("Histogram2", 8);
+ EXPECT_EQ(1, metrics::NumSamples("Histogram1"));
+ EXPECT_EQ(1, metrics::NumSamples("Histogram2"));
+ EXPECT_EQ(1, metrics::NumEvents("Histogram1", 50));
+ EXPECT_EQ(1, metrics::NumEvents("Histogram2", 8));
+}
+
+TEST_F(MetricsDefaultTest, TestMinMaxBucket) {
+ const std::string kName = "MinMaxCounts100";
+ RTC_HISTOGRAM_COUNTS_100(kName, 4);
+
+ std::map<std::string, std::unique_ptr<metrics::SampleInfo>,
+ rtc::AbslStringViewCmp>
+ histograms;
+ metrics::GetAndReset(&histograms);
+ EXPECT_EQ(1u, histograms.size());
+ EXPECT_EQ(kName, histograms.begin()->second->name);
+ EXPECT_EQ(1, histograms.begin()->second->min);
+ EXPECT_EQ(100, histograms.begin()->second->max);
+ EXPECT_EQ(50u, histograms.begin()->second->bucket_count);
+ EXPECT_EQ(1u, histograms.begin()->second->samples.size());
+}
+
+} // namespace webrtc
+#endif
diff --git a/third_party/libwebrtc/system_wrappers/source/metrics_unittest.cc b/third_party/libwebrtc/system_wrappers/source/metrics_unittest.cc
new file mode 100644
index 0000000000..cd3807748c
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/metrics_unittest.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 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 "system_wrappers/include/metrics.h"
+
+#include "absl/strings/string_view.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Pair;
+
+#if RTC_METRICS_ENABLED
+namespace webrtc {
+namespace {
+const int kSample = 22;
+
+void AddSparseSample(absl::string_view name, int sample) {
+ RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample);
+}
+void AddSampleWithVaryingName(int index, absl::string_view name, int sample) {
+ RTC_HISTOGRAMS_COUNTS_100(index, name, sample);
+}
+} // namespace
+
+class MetricsTest : public ::testing::Test {
+ public:
+ MetricsTest() {}
+
+ protected:
+ void SetUp() override { metrics::Reset(); }
+};
+
+TEST_F(MetricsTest, InitiallyNoSamples) {
+ EXPECT_EQ(0, metrics::NumSamples("NonExisting"));
+ EXPECT_EQ(0, metrics::NumEvents("NonExisting", kSample));
+ EXPECT_THAT(metrics::Samples("NonExisting"), IsEmpty());
+}
+
+TEST_F(MetricsTest, RtcHistogramPercent_AddSample) {
+ const std::string kName = "Percentage";
+ RTC_HISTOGRAM_PERCENTAGE(kName, kSample);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, kSample));
+ EXPECT_THAT(metrics::Samples(kName), ElementsAre(Pair(kSample, 1)));
+}
+
+TEST_F(MetricsTest, RtcHistogramEnumeration_AddSample) {
+ const std::string kName = "Enumeration";
+ RTC_HISTOGRAM_ENUMERATION(kName, kSample, kSample + 1);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, kSample));
+ EXPECT_THAT(metrics::Samples(kName), ElementsAre(Pair(kSample, 1)));
+}
+
+TEST_F(MetricsTest, RtcHistogramBoolean_AddSample) {
+ const std::string kName = "Boolean";
+ const int kSample = 0;
+ RTC_HISTOGRAM_BOOLEAN(kName, kSample);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, kSample));
+ EXPECT_THAT(metrics::Samples(kName), ElementsAre(Pair(kSample, 1)));
+}
+
+TEST_F(MetricsTest, RtcHistogramCountsSparse_AddSample) {
+ const std::string kName = "CountsSparse100";
+ RTC_HISTOGRAM_COUNTS_SPARSE_100(kName, kSample);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, kSample));
+ EXPECT_THAT(metrics::Samples(kName), ElementsAre(Pair(kSample, 1)));
+}
+
+TEST_F(MetricsTest, RtcHistogramCounts_AddSample) {
+ const std::string kName = "Counts100";
+ RTC_HISTOGRAM_COUNTS_100(kName, kSample);
+ EXPECT_EQ(1, metrics::NumSamples(kName));
+ EXPECT_EQ(1, metrics::NumEvents(kName, kSample));
+ EXPECT_THAT(metrics::Samples(kName), ElementsAre(Pair(kSample, 1)));
+}
+
+TEST_F(MetricsTest, RtcHistogramCounts_AddMultipleSamples) {
+ const std::string kName = "Counts200";
+ const int kNumSamples = 10;
+ std::map<int, int> samples;
+ for (int i = 1; i <= kNumSamples; ++i) {
+ RTC_HISTOGRAM_COUNTS_200(kName, i);
+ EXPECT_EQ(1, metrics::NumEvents(kName, i));
+ EXPECT_EQ(i, metrics::NumSamples(kName));
+ samples[i] = 1;
+ }
+ EXPECT_EQ(samples, metrics::Samples(kName));
+}
+
+TEST_F(MetricsTest, RtcHistogramsCounts_AddSample) {
+ AddSampleWithVaryingName(0, "Name1", kSample);
+ AddSampleWithVaryingName(1, "Name2", kSample + 1);
+ AddSampleWithVaryingName(2, "Name3", kSample + 2);
+ EXPECT_EQ(1, metrics::NumSamples("Name1"));
+ EXPECT_EQ(1, metrics::NumSamples("Name2"));
+ EXPECT_EQ(1, metrics::NumSamples("Name3"));
+ EXPECT_EQ(1, metrics::NumEvents("Name1", kSample + 0));
+ EXPECT_EQ(1, metrics::NumEvents("Name2", kSample + 1));
+ EXPECT_EQ(1, metrics::NumEvents("Name3", kSample + 2));
+ EXPECT_THAT(metrics::Samples("Name1"), ElementsAre(Pair(kSample + 0, 1)));
+ EXPECT_THAT(metrics::Samples("Name2"), ElementsAre(Pair(kSample + 1, 1)));
+ EXPECT_THAT(metrics::Samples("Name3"), ElementsAre(Pair(kSample + 2, 1)));
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+using MetricsDeathTest = MetricsTest;
+TEST_F(MetricsDeathTest, RtcHistogramsCounts_InvalidIndex) {
+ EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(-1, "Name", kSample), "");
+ EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(3, "Name", kSample), "");
+ EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(3u, "Name", kSample), "");
+}
+#endif
+
+TEST_F(MetricsTest, RtcHistogramSparse_NonConstantNameWorks) {
+ AddSparseSample("Sparse1", kSample);
+ AddSparseSample("Sparse2", kSample);
+ EXPECT_EQ(1, metrics::NumSamples("Sparse1"));
+ EXPECT_EQ(1, metrics::NumSamples("Sparse2"));
+ EXPECT_THAT(metrics::Samples("Sparse1"), ElementsAre(Pair(kSample, 1)));
+ EXPECT_THAT(metrics::Samples("Sparse2"), ElementsAre(Pair(kSample, 1)));
+}
+
+} // namespace webrtc
+#endif
diff --git a/third_party/libwebrtc/system_wrappers/source/ntp_time_unittest.cc b/third_party/libwebrtc/system_wrappers/source/ntp_time_unittest.cc
new file mode 100644
index 0000000000..0705531e37
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/ntp_time_unittest.cc
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2014 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 "system_wrappers/include/ntp_time.h"
+
+#include <random>
+
+#include "system_wrappers/include/clock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr uint32_t kNtpSec = 0x12345678;
+constexpr uint32_t kNtpFrac = 0x23456789;
+
+constexpr int64_t kOneSecQ32x32 = uint64_t{1} << 32;
+constexpr int64_t kOneMsQ32x32 = 4294967;
+
+TEST(NtpTimeTest, NoValueMeansInvalid) {
+ NtpTime ntp;
+ EXPECT_FALSE(ntp.Valid());
+}
+
+TEST(NtpTimeTest, CanResetValue) {
+ NtpTime ntp(kNtpSec, kNtpFrac);
+ EXPECT_TRUE(ntp.Valid());
+ ntp.Reset();
+ EXPECT_FALSE(ntp.Valid());
+}
+
+TEST(NtpTimeTest, CanGetWhatIsSet) {
+ NtpTime ntp;
+ ntp.Set(kNtpSec, kNtpFrac);
+ EXPECT_EQ(kNtpSec, ntp.seconds());
+ EXPECT_EQ(kNtpFrac, ntp.fractions());
+}
+
+TEST(NtpTimeTest, SetIsSameAs2ParameterConstructor) {
+ NtpTime ntp1(kNtpSec, kNtpFrac);
+ NtpTime ntp2;
+ EXPECT_NE(ntp1, ntp2);
+
+ ntp2.Set(kNtpSec, kNtpFrac);
+ EXPECT_EQ(ntp1, ntp2);
+}
+
+TEST(NtpTimeTest, ToMsMeansToNtpMilliseconds) {
+ SimulatedClock clock(0x123456789abc);
+
+ NtpTime ntp = clock.CurrentNtpTime();
+ EXPECT_EQ(ntp.ToMs(), clock.CurrentNtpInMilliseconds());
+}
+
+TEST(NtpTimeTest, CanExplicitlyConvertToAndFromUint64) {
+ uint64_t untyped_time = 0x123456789;
+ NtpTime time(untyped_time);
+ EXPECT_EQ(untyped_time, static_cast<uint64_t>(time));
+ EXPECT_EQ(NtpTime(0x12345678, 0x90abcdef), NtpTime(0x1234567890abcdef));
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToQ32x32NearZero) {
+ // Zero
+ EXPECT_EQ(Int64MsToQ32x32(0), 0);
+
+ // Zero + 1 millisecond
+ EXPECT_EQ(Int64MsToQ32x32(1), kOneMsQ32x32);
+
+ // Zero - 1 millisecond
+ EXPECT_EQ(Int64MsToQ32x32(-1), -kOneMsQ32x32);
+
+ // Zero + 1 second
+ EXPECT_EQ(Int64MsToQ32x32(1000), kOneSecQ32x32);
+
+ // Zero - 1 second
+ EXPECT_EQ(Int64MsToQ32x32(-1000), -kOneSecQ32x32);
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToUQ32x32NearZero) {
+ // Zero
+ EXPECT_EQ(Int64MsToUQ32x32(0), uint64_t{0});
+
+ // Zero + 1 millisecond
+ EXPECT_EQ(Int64MsToUQ32x32(1), uint64_t{kOneMsQ32x32});
+
+ // Zero - 1 millisecond
+ EXPECT_EQ(Int64MsToUQ32x32(-1), uint64_t{0}); // Clamped
+
+ // Zero + 1 second
+ EXPECT_EQ(Int64MsToUQ32x32(1000), uint64_t{kOneSecQ32x32});
+
+ // Zero - 1 second
+ EXPECT_EQ(Int64MsToUQ32x32(-1000), uint64_t{0}); // Clamped
+}
+
+TEST(NtpTimeTest, VerifyQ32x32ToInt64MsNearZero) {
+ // Zero
+ EXPECT_EQ(Q32x32ToInt64Ms(0), 0);
+
+ // Zero + 1 millisecond
+ EXPECT_EQ(Q32x32ToInt64Ms(kOneMsQ32x32), 1);
+
+ // Zero - 1 millisecond
+ EXPECT_EQ(Q32x32ToInt64Ms(-kOneMsQ32x32), -1);
+
+ // Zero + 1 second
+ EXPECT_EQ(Q32x32ToInt64Ms(kOneSecQ32x32), 1000);
+
+ // Zero - 1 second
+ EXPECT_EQ(Q32x32ToInt64Ms(-kOneSecQ32x32), -1000);
+}
+
+TEST(NtpTimeTest, VerifyUQ32x32ToInt64MsNearZero) {
+ // Zero
+ EXPECT_EQ(UQ32x32ToInt64Ms(0), 0);
+
+ // Zero + 1 millisecond
+ EXPECT_EQ(UQ32x32ToInt64Ms(kOneMsQ32x32), 1);
+
+ // Zero + 1 second
+ EXPECT_EQ(UQ32x32ToInt64Ms(kOneSecQ32x32), 1000);
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToQ32x32NearMax) {
+ constexpr int64_t kMaxQ32x32 = std::numeric_limits<int64_t>::max();
+ constexpr int64_t kBoundaryMs = (kMaxQ32x32 >> 32) * 1000 + 999;
+
+ // Max
+ const int64_t boundary_q32x32 = Int64MsToQ32x32(kBoundaryMs);
+ EXPECT_LE(boundary_q32x32, kMaxQ32x32);
+ EXPECT_GT(boundary_q32x32, kMaxQ32x32 - kOneMsQ32x32);
+
+ // Max + 1 millisecond
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs + 1), kMaxQ32x32); // Clamped
+
+ // Max - 1 millisecond
+ EXPECT_LE(Int64MsToQ32x32(kBoundaryMs - 1), kMaxQ32x32 - kOneMsQ32x32);
+
+ // Max + 1 second
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs + 1000), kMaxQ32x32); // Clamped
+
+ // Max - 1 second
+ EXPECT_LE(Int64MsToQ32x32(kBoundaryMs - 1000), kMaxQ32x32 - kOneSecQ32x32);
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToUQ32x32NearMax) {
+ constexpr uint64_t kMaxUQ32x32 = std::numeric_limits<uint64_t>::max();
+ constexpr int64_t kBoundaryMs = (kMaxUQ32x32 >> 32) * 1000 + 999;
+
+ // Max
+ const uint64_t boundary_uq32x32 = Int64MsToUQ32x32(kBoundaryMs);
+ EXPECT_LE(boundary_uq32x32, kMaxUQ32x32);
+ EXPECT_GT(boundary_uq32x32, kMaxUQ32x32 - kOneMsQ32x32);
+
+ // Max + 1 millisecond
+ EXPECT_EQ(Int64MsToUQ32x32(kBoundaryMs + 1), kMaxUQ32x32); // Clamped
+
+ // Max - 1 millisecond
+ EXPECT_LE(Int64MsToUQ32x32(kBoundaryMs - 1), kMaxUQ32x32 - kOneMsQ32x32);
+
+ // Max + 1 second
+ EXPECT_EQ(Int64MsToUQ32x32(kBoundaryMs + 1000), kMaxUQ32x32); // Clamped
+
+ // Max - 1 second
+ EXPECT_LE(Int64MsToUQ32x32(kBoundaryMs - 1000), kMaxUQ32x32 - kOneSecQ32x32);
+}
+
+TEST(NtpTimeTest, VerifyQ32x32ToInt64MsNearMax) {
+ constexpr int64_t kMaxQ32x32 = std::numeric_limits<int64_t>::max();
+ constexpr int64_t kBoundaryMs = (kMaxQ32x32 >> 32) * 1000 + 1000;
+
+ // Max
+ EXPECT_EQ(Q32x32ToInt64Ms(kMaxQ32x32), kBoundaryMs);
+
+ // Max - 1 millisecond
+ EXPECT_EQ(Q32x32ToInt64Ms(kMaxQ32x32 - kOneMsQ32x32), kBoundaryMs - 1);
+
+ // Max - 1 second
+ EXPECT_EQ(Q32x32ToInt64Ms(kMaxQ32x32 - kOneSecQ32x32), kBoundaryMs - 1000);
+}
+
+TEST(NtpTimeTest, VerifyUQ32x32ToInt64MsNearMax) {
+ constexpr uint64_t kMaxUQ32x32 = std::numeric_limits<uint64_t>::max();
+ constexpr int64_t kBoundaryMs = (kMaxUQ32x32 >> 32) * 1000 + 1000;
+
+ // Max
+ EXPECT_EQ(UQ32x32ToInt64Ms(kMaxUQ32x32), kBoundaryMs);
+
+ // Max - 1 millisecond
+ EXPECT_EQ(UQ32x32ToInt64Ms(kMaxUQ32x32 - kOneMsQ32x32), kBoundaryMs - 1);
+
+ // Max - 1 second
+ EXPECT_EQ(UQ32x32ToInt64Ms(kMaxUQ32x32 - kOneSecQ32x32), kBoundaryMs - 1000);
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToQ32x32NearMin) {
+ constexpr int64_t kBoundaryQ32x32 = 0x8000000000000000;
+ constexpr int64_t kBoundaryMs = -int64_t{0x80000000} * 1000;
+
+ // Min
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs), kBoundaryQ32x32);
+
+ // Min + 1 millisecond
+ EXPECT_EQ(Q32x32ToInt64Ms(Int64MsToQ32x32(kBoundaryMs + 1)), kBoundaryMs + 1);
+
+ // Min - 1 millisecond
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs - 1), kBoundaryQ32x32); // Clamped
+
+ // Min + 1 second
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs + 1000),
+ kBoundaryQ32x32 + kOneSecQ32x32);
+
+ // Min - 1 second
+ EXPECT_EQ(Int64MsToQ32x32(kBoundaryMs - 1000), kBoundaryQ32x32); // Clamped
+}
+
+TEST(NtpTimeTest, VerifyQ32x32ToInt64MsNearMin) {
+ constexpr int64_t kBoundaryQ32x32 = 0x8000000000000000;
+ constexpr int64_t kBoundaryMs = -int64_t{0x80000000} * 1000;
+
+ // Min
+ EXPECT_EQ(Q32x32ToInt64Ms(kBoundaryQ32x32), kBoundaryMs);
+
+ // Min + 1 millisecond
+ EXPECT_EQ(Q32x32ToInt64Ms(kBoundaryQ32x32 + kOneMsQ32x32), kBoundaryMs + 1);
+
+ // Min + 1 second
+ EXPECT_EQ(Q32x32ToInt64Ms(kBoundaryQ32x32 + kOneSecQ32x32),
+ kBoundaryMs + 1000);
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToQ32x32RoundTrip) {
+ constexpr int kIterations = 50000;
+
+ std::mt19937 generator(123456789);
+ std::uniform_int_distribution<int64_t> distribution(
+ Q32x32ToInt64Ms(std::numeric_limits<int64_t>::min()),
+ Q32x32ToInt64Ms(std::numeric_limits<int64_t>::max()));
+
+ for (int iteration = 0; iteration < kIterations; ++iteration) {
+ int64_t input_ms = distribution(generator);
+ int64_t transit_q32x32 = Int64MsToQ32x32(input_ms);
+ int64_t output_ms = Q32x32ToInt64Ms(transit_q32x32);
+
+ ASSERT_EQ(input_ms, output_ms)
+ << "iteration = " << iteration << ", input_ms = " << input_ms
+ << ", transit_q32x32 = " << transit_q32x32
+ << ", output_ms = " << output_ms;
+ }
+}
+
+TEST(NtpTimeTest, VerifyInt64MsToUQ32x32RoundTrip) {
+ constexpr int kIterations = 50000;
+
+ std::mt19937 generator(123456789);
+ std::uniform_int_distribution<uint64_t> distribution(
+ UQ32x32ToInt64Ms(std::numeric_limits<uint64_t>::min()),
+ UQ32x32ToInt64Ms(std::numeric_limits<uint64_t>::max()));
+
+ for (int iteration = 0; iteration < kIterations; ++iteration) {
+ uint64_t input_ms = distribution(generator);
+ uint64_t transit_uq32x32 = Int64MsToUQ32x32(input_ms);
+ uint64_t output_ms = UQ32x32ToInt64Ms(transit_uq32x32);
+
+ ASSERT_EQ(input_ms, output_ms)
+ << "iteration = " << iteration << ", input_ms = " << input_ms
+ << ", transit_uq32x32 = " << transit_uq32x32
+ << ", output_ms = " << output_ms;
+ }
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator.cc b/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator.cc
new file mode 100644
index 0000000000..ef5d9a7508
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2012 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 "system_wrappers/include/rtp_to_ntp_estimator.h"
+
+#include <stddef.h>
+
+#include <cmath>
+#include <vector>
+
+#include "api/array_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+
+namespace webrtc {
+namespace {
+// Maximum number of RTCP SR reports to use to map between RTP and NTP.
+constexpr size_t kNumRtcpReportsToUse = 20;
+// Don't allow NTP timestamps to jump more than 1 hour. Chosen arbitrary as big
+// enough to not affect normal use-cases. Yet it is smaller than RTP wrap-around
+// half-period (90khz RTP clock wrap-arounds every 13.25 hours). After half of
+// wrap-around period it is impossible to unwrap RTP timestamps correctly.
+constexpr uint64_t kMaxAllowedRtcpNtpInterval = uint64_t{60 * 60} << 32;
+} // namespace
+
+void RtpToNtpEstimator::UpdateParameters() {
+ size_t n = measurements_.size();
+ if (n < 2)
+ return;
+
+ // Run linear regression:
+ // Given x[] and y[] writes out such k and b that line y=k*x+b approximates
+ // given points in the best way (Least Squares Method).
+ auto x = [](const RtcpMeasurement& m) {
+ return static_cast<double>(m.unwrapped_rtp_timestamp);
+ };
+ auto y = [](const RtcpMeasurement& m) {
+ return static_cast<double>(static_cast<uint64_t>(m.ntp_time));
+ };
+
+ double avg_x = 0;
+ double avg_y = 0;
+ for (const RtcpMeasurement& m : measurements_) {
+ avg_x += x(m);
+ avg_y += y(m);
+ }
+ avg_x /= n;
+ avg_y /= n;
+
+ double variance_x = 0;
+ double covariance_xy = 0;
+ for (const RtcpMeasurement& m : measurements_) {
+ double normalized_x = x(m) - avg_x;
+ double normalized_y = y(m) - avg_y;
+ variance_x += normalized_x * normalized_x;
+ covariance_xy += normalized_x * normalized_y;
+ }
+
+ if (std::fabs(variance_x) < 1e-8)
+ return;
+
+ double k = covariance_xy / variance_x;
+ double b = avg_y - k * avg_x;
+ params_ = {{.slope = k, .offset = b}};
+}
+
+RtpToNtpEstimator::UpdateResult RtpToNtpEstimator::UpdateMeasurements(
+ NtpTime ntp,
+ uint32_t rtp_timestamp) {
+ int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp);
+
+ RtcpMeasurement new_measurement = {
+ .ntp_time = ntp, .unwrapped_rtp_timestamp = unwrapped_rtp_timestamp};
+
+ for (const RtcpMeasurement& measurement : measurements_) {
+ // Use || since two equal timestamps will result in zero frequency.
+ if (measurement.ntp_time == ntp ||
+ measurement.unwrapped_rtp_timestamp == unwrapped_rtp_timestamp) {
+ return kSameMeasurement;
+ }
+ }
+
+ if (!new_measurement.ntp_time.Valid())
+ return kInvalidMeasurement;
+
+ uint64_t ntp_new = static_cast<uint64_t>(new_measurement.ntp_time);
+ bool invalid_sample = false;
+ if (!measurements_.empty()) {
+ int64_t old_rtp_timestamp = measurements_.front().unwrapped_rtp_timestamp;
+ uint64_t old_ntp = static_cast<uint64_t>(measurements_.front().ntp_time);
+ if (ntp_new <= old_ntp || ntp_new > old_ntp + kMaxAllowedRtcpNtpInterval) {
+ invalid_sample = true;
+ } else if (unwrapped_rtp_timestamp <= old_rtp_timestamp) {
+ RTC_LOG(LS_WARNING)
+ << "Newer RTCP SR report with older RTP timestamp, dropping";
+ invalid_sample = true;
+ } else if (unwrapped_rtp_timestamp - old_rtp_timestamp > (1 << 25)) {
+ // Sanity check. No jumps too far into the future in rtp.
+ invalid_sample = true;
+ }
+ }
+
+ if (invalid_sample) {
+ ++consecutive_invalid_samples_;
+ if (consecutive_invalid_samples_ < kMaxInvalidSamples) {
+ return kInvalidMeasurement;
+ }
+ RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, "
+ "clearing measurements.";
+ measurements_.clear();
+ params_ = absl::nullopt;
+ }
+ consecutive_invalid_samples_ = 0;
+
+ // Insert new RTCP SR report.
+ if (measurements_.size() == kNumRtcpReportsToUse)
+ measurements_.pop_back();
+
+ measurements_.push_front(new_measurement);
+
+ // List updated, calculate new parameters.
+ UpdateParameters();
+ return kNewMeasurement;
+}
+
+NtpTime RtpToNtpEstimator::Estimate(uint32_t rtp_timestamp) const {
+ if (!params_)
+ return NtpTime();
+
+ double estimated =
+ static_cast<double>(unwrapper_.Unwrap(rtp_timestamp)) * params_->slope +
+ params_->offset + 0.5f;
+
+ return NtpTime(rtc::saturated_cast<uint64_t>(estimated));
+}
+
+double RtpToNtpEstimator::EstimatedFrequencyKhz() const {
+ if (!params_.has_value()) {
+ return 0.0;
+ }
+ static constexpr double kNtpUnitPerMs = 4.294967296E6; // 2^32 / 1000.
+ return kNtpUnitPerMs / params_->slope;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc b/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc
new file mode 100644
index 0000000000..81effd9452
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2012 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 "system_wrappers/include/rtp_to_ntp_estimator.h"
+
+#include <stddef.h>
+
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+constexpr uint64_t kOneMsInNtp = 4294967;
+constexpr uint64_t kOneHourInNtp = uint64_t{60 * 60} << 32;
+constexpr uint32_t kTimestampTicksPerMs = 90;
+} // namespace
+
+TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), 0),
+ RtpToNtpEstimator::kNewMeasurement);
+ // No wraparound will be detected, since we are not allowed to wrap below 0,
+ // but there will be huge rtp timestamp jump, e.g. old_timestamp = 0,
+ // new_timestamp = 4294967295, which should be detected.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp),
+ -kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp_Wraparound_Detected) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFE),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 2 * kOneMsInNtp),
+ 0xFFFFFFFE + 2 * kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Expected to fail since the older RTCP has a smaller RTP timestamp than the
+ // newer (old:10, new:4294967206).
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 3 * kOneMsInNtp),
+ 0xFFFFFFFE + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp_NegativeWraparound) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Expected to fail since the older RTCP has a smaller RTP timestamp than the
+ // newer (old:0, new:-180).
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + 2 * kOneMsInNtp),
+ 0xFFFFFFFF - 2 * kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(WrapAroundTests, NewRtcpWrapped) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
+ 0xFFFFFFFF + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Since this RTP packet has the same timestamp as the RTCP packet constructed
+ // at time 0 it should be mapped to 0 as well.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));
+}
+
+TEST(WrapAroundTests, RtpWrapped) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1),
+ 0xFFFFFFFF - 2 * kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
+ 0xFFFFFFFF - kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ // Since this RTP packet has the same timestamp as the RTCP packet constructed
+ // at time 0 it should be mapped to 0 as well.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF - 2 * kTimestampTicksPerMs),
+ NtpTime(1));
+ // Two kTimestampTicksPerMs advanced.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1 + 2 * kOneMsInNtp));
+ // Wrapped rtp.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF + kTimestampTicksPerMs),
+ NtpTime(1 + 3 * kOneMsInNtp));
+}
+
+TEST(WrapAroundTests, OldRtp_RtcpsWrapped) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
+ 0xFFFFFFFF + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ EXPECT_FALSE(estimator.Estimate(0xFFFFFFFF - kTimestampTicksPerMs).Valid());
+}
+
+TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
+ 0xFFFFFFFF + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ // Constructed at the same time as the first RTCP and should therefore be
+ // mapped to zero.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));
+}
+
+TEST(WrapAroundTests, GracefullyHandleRtpJump) {
+ RtpToNtpEstimator estimator;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), 0xFFFFFFFF),
+ RtpToNtpEstimator::kNewMeasurement);
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1 + kOneMsInNtp),
+ 0xFFFFFFFF + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ // Constructed at the same time as the first RTCP and should therefore be
+ // mapped to zero.
+ EXPECT_EQ(estimator.Estimate(0xFFFFFFFF), NtpTime(1));
+
+ uint32_t timestamp = 0xFFFFFFFF - 0xFFFFF;
+ uint64_t ntp_raw = 1 + 2 * kOneMsInNtp;
+ for (int i = 0; i < RtpToNtpEstimator::kMaxInvalidSamples - 1; ++i) {
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kInvalidMeasurement);
+ ntp_raw += kOneMsInNtp;
+ timestamp += kTimestampTicksPerMs;
+ }
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ ntp_raw += kOneMsInNtp;
+ timestamp += kTimestampTicksPerMs;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ EXPECT_EQ(estimator.Estimate(timestamp), NtpTime(ntp_raw));
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForZeroNtp) {
+ RtpToNtpEstimator estimator;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(0), 0x12345678),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForEqualNtp) {
+ RtpToNtpEstimator estimator;
+ NtpTime ntp(0, 699925050);
+ uint32_t timestamp = 0x12345678;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Ntp time already added, list not updated.
+ EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp + 1),
+ RtpToNtpEstimator::kSameMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForOldNtp) {
+ RtpToNtpEstimator estimator;
+ uint64_t ntp_raw = 699925050;
+ NtpTime ntp(ntp_raw);
+ uint32_t timestamp = 0x12345678;
+ EXPECT_EQ(estimator.UpdateMeasurements(ntp, timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ // Old ntp time, list not updated.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw - kOneMsInNtp),
+ timestamp + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForTooNewNtp) {
+ RtpToNtpEstimator estimator;
+
+ uint64_t ntp_raw = 699925050;
+ uint32_t timestamp = 0x12345678;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ // Ntp time from far future, list not updated.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw + 2 * kOneHourInNtp),
+ timestamp + 10 * kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForEqualTimestamp) {
+ RtpToNtpEstimator estimator;
+
+ uint32_t timestamp = 0x12345678;
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Timestamp already added, list not updated.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(3), timestamp),
+ RtpToNtpEstimator::kSameMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, FailsForOldRtpTimestamp) {
+ RtpToNtpEstimator estimator;
+ uint32_t timestamp = 0x12345678;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Old timestamp, list not updated.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 + kOneMsInNtp),
+ timestamp - kTimestampTicksPerMs),
+ RtpToNtpEstimator::kInvalidMeasurement);
+}
+
+TEST(UpdateRtcpMeasurementTests, VerifyParameters) {
+ RtpToNtpEstimator estimator;
+ uint32_t timestamp = 0x12345678;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(kOneMsInNtp), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0);
+
+ // Add second report, parameters should be calculated.
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(2 * kOneMsInNtp),
+ timestamp + kTimestampTicksPerMs),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ EXPECT_NEAR(estimator.EstimatedFrequencyKhz(), kTimestampTicksPerMs, 0.01);
+}
+
+TEST(RtpToNtpTests, FailsForNoParameters) {
+ RtpToNtpEstimator estimator;
+ uint32_t timestamp = 0x12345678;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(1), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+ // Parameters are not calculated, conversion of RTP to NTP time should fail.
+ EXPECT_DOUBLE_EQ(estimator.EstimatedFrequencyKhz(), 0.0);
+ EXPECT_FALSE(estimator.Estimate(timestamp).Valid());
+}
+
+TEST(RtpToNtpTests, AveragesErrorOut) {
+ RtpToNtpEstimator estimator;
+ uint64_t ntp_raw = 90000000; // More than 1 ms.
+ ASSERT_GT(ntp_raw, kOneMsInNtp);
+ uint32_t timestamp = 0x12345678;
+ constexpr uint64_t kNtpSecStep = uint64_t{1} << 32; // 1 second.
+ constexpr int kRtpTicksPerMs = 90;
+ constexpr int kRtpStep = kRtpTicksPerMs * 1000;
+
+ EXPECT_EQ(estimator.UpdateMeasurements(NtpTime(ntp_raw), timestamp),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ Random rand(1123536L);
+ for (size_t i = 0; i < 1000; i++) {
+ // Advance both timestamps by exactly 1 second.
+ ntp_raw += kNtpSecStep;
+ timestamp += kRtpStep;
+ // Add upto 1ms of errors to NTP and RTP timestamps passed to estimator.
+ EXPECT_EQ(
+ estimator.UpdateMeasurements(
+ NtpTime(ntp_raw + rand.Rand(-int{kOneMsInNtp}, int{kOneMsInNtp})),
+ timestamp + rand.Rand(-kRtpTicksPerMs, kRtpTicksPerMs)),
+ RtpToNtpEstimator::kNewMeasurement);
+
+ NtpTime estimated_ntp = estimator.Estimate(timestamp);
+ EXPECT_TRUE(estimated_ntp.Valid());
+ // Allow upto 2 ms of error.
+ EXPECT_NEAR(ntp_raw, static_cast<uint64_t>(estimated_ntp), 2 * kOneMsInNtp);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/system_wrappers/source/sleep.cc b/third_party/libwebrtc/system_wrappers/source/sleep.cc
new file mode 100644
index 0000000000..e2fa486118
--- /dev/null
+++ b/third_party/libwebrtc/system_wrappers/source/sleep.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+// An OS-independent sleep function.
+
+#include "system_wrappers/include/sleep.h"
+
+#ifdef _WIN32
+// For Sleep()
+#include <windows.h>
+#else
+// For nanosleep()
+#include <time.h>
+#endif
+
+namespace webrtc {
+
+void SleepMs(int msecs) {
+#ifdef _WIN32
+ Sleep(msecs);
+#else
+ struct timespec short_wait;
+ struct timespec remainder;
+ short_wait.tv_sec = msecs / 1000;
+ short_wait.tv_nsec = (msecs % 1000) * 1000 * 1000;
+ nanosleep(&short_wait, &remainder);
+#endif
+}
+
+} // namespace webrtc