Add helper functions to convert between integer milliseconds and fixed-point seconds.

This change adds the following helper functions to convert between "integer milliseconds"-style timestamps and durations, and "UQ32.32 and Q32.32"-style NTP timestamps and durations:
- Int64MsToQ32x32
- UInt64MsToUQ32x32
- Q32x32ToInt64Ms
- UQ32x32ToUInt64Ms

The Q-format NTP timestamps and durations are used by some RTP/RTCP packets.

Bug: webrtc:10739
Change-Id: I89123d2dba7370f26e239d722a4975bf5ac6e668
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/148444
Commit-Queue: Chen Xing <chxg@google.com>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28878}
diff --git a/system_wrappers/include/ntp_time.h b/system_wrappers/include/ntp_time.h
index 1c32184..1f57558 100644
--- a/system_wrappers/include/ntp_time.h
+++ b/system_wrappers/include/ntp_time.h
@@ -10,7 +10,9 @@
 #ifndef SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
 #define SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
 
-#include <stdint.h>
+#include <cmath>
+#include <cstdint>
+#include <limits>
 
 #include "rtc_base/numerics/safe_conversions.h"
 
@@ -60,5 +62,55 @@
   return !(n1 == n2);
 }
 
+// Converts |int64_t| milliseconds to Q32.32-formatted fixed-point seconds.
+// Performs clamping if the result overflows or underflows.
+inline int64_t Int64MsToQ32x32(int64_t milliseconds) {
+  // TODO(bugs.webrtc.org/10893): Change to use |rtc::saturated_cast| once the
+  // bug has been fixed.
+  double result =
+      std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
+
+  if (result <= std::numeric_limits<int64_t>::min()) {
+    return std::numeric_limits<int64_t>::min();
+  }
+
+  if (result >= std::numeric_limits<int64_t>::max()) {
+    return std::numeric_limits<int64_t>::max();
+  }
+
+  return rtc::dchecked_cast<int64_t>(result);
+}
+
+// Converts |int64_t| milliseconds to UQ32.32-formatted fixed-point seconds.
+// Performs clamping if the result overflows or underflows.
+inline uint64_t Int64MsToUQ32x32(int64_t milliseconds) {
+  // TODO(bugs.webrtc.org/10893): Change to use |rtc::saturated_cast| once the
+  // bug has been fixed.
+  double result =
+      std::round(milliseconds * (NtpTime::kFractionsPerSecond / 1000.0));
+
+  if (result <= std::numeric_limits<uint64_t>::min()) {
+    return std::numeric_limits<uint64_t>::min();
+  }
+
+  if (result >= std::numeric_limits<uint64_t>::max()) {
+    return std::numeric_limits<uint64_t>::max();
+  }
+
+  return rtc::dchecked_cast<uint64_t>(result);
+}
+
+// Converts Q32.32-formatted fixed-point seconds to |int64_t| milliseconds.
+inline int64_t Q32x32ToInt64Ms(int64_t q32x32) {
+  return rtc::dchecked_cast<int64_t>(
+      std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
+}
+
+// Converts UQ32.32-formatted fixed-point seconds to |int64_t| milliseconds.
+inline int64_t UQ32x32ToInt64Ms(uint64_t q32x32) {
+  return rtc::dchecked_cast<int64_t>(
+      std::round(q32x32 * (1000.0 / NtpTime::kFractionsPerSecond)));
+}
+
 }  // namespace webrtc
 #endif  // SYSTEM_WRAPPERS_INCLUDE_NTP_TIME_H_
diff --git a/system_wrappers/source/ntp_time_unittest.cc b/system_wrappers/source/ntp_time_unittest.cc
index f45273f..cdaca67 100644
--- a/system_wrappers/source/ntp_time_unittest.cc
+++ b/system_wrappers/source/ntp_time_unittest.cc
@@ -10,14 +10,19 @@
 
 #include "system_wrappers/include/ntp_time.h"
 
+#include <random>
+
 #include "system_wrappers/include/clock.h"
 #include "test/gtest.h"
 
 namespace webrtc {
 namespace {
 
-const uint32_t kNtpSec = 0x12345678;
-const uint32_t kNtpFrac = 0x23456789;
+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;
@@ -62,5 +67,215 @@
   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