Moves network unit types to API.
This prepares for being able to inject network congestion controllers.
And makes it easier to use the units in other parts of the code.
Bug: webrtc:9155
Change-Id: Ib8f9c1c97b06d791a01c3376046933d576ae46f9
Reviewed-on: https://webrtc-review.googlesource.com/70201
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23168}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index c394584..989858f 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -493,6 +493,7 @@
"../rtc_base:rtc_base_approved",
"../rtc_base:rtc_base_tests_utils",
"../test:test_support",
+ "units:units_unittests",
]
}
}
diff --git a/api/units/BUILD.gn b/api/units/BUILD.gn
new file mode 100644
index 0000000..7dbddf4
--- /dev/null
+++ b/api/units/BUILD.gn
@@ -0,0 +1,78 @@
+# Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../webrtc.gni")
+
+rtc_source_set("data_rate") {
+ sources = [
+ "data_rate.cc",
+ "data_rate.h",
+ ]
+
+ deps = [
+ ":data_size",
+ ":time_delta",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+
+rtc_source_set("data_size") {
+ sources = [
+ "data_size.cc",
+ "data_size.h",
+ ]
+
+ deps = [
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+rtc_source_set("time_delta") {
+ sources = [
+ "time_delta.cc",
+ "time_delta.h",
+ ]
+
+ deps = [
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+
+rtc_source_set("timestamp") {
+ sources = [
+ "timestamp.cc",
+ "timestamp.h",
+ ]
+
+ deps = [
+ ":time_delta",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+
+if (rtc_include_tests) {
+ rtc_source_set("units_unittests") {
+ testonly = true
+ sources = [
+ "data_rate_unittest.cc",
+ "data_size_unittest.cc",
+ "time_delta_unittest.cc",
+ "timestamp_unittest.cc",
+ ]
+ deps = [
+ ":data_rate",
+ ":data_size",
+ ":time_delta",
+ ":timestamp",
+ "../../test:test_support",
+ ]
+ }
+}
diff --git a/api/units/data_rate.cc b/api/units/data_rate.cc
new file mode 100644
index 0000000..4e31d51
--- /dev/null
+++ b/api/units/data_rate.cc
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 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 "api/units/data_rate.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+
+std::string ToString(const DataRate& value) {
+ char buf[64];
+ rtc::SimpleStringBuilder sb(buf);
+ if (value.IsInfinite()) {
+ sb << "inf bps";
+ } else {
+ sb << value.bps() << " bps";
+ }
+ return sb.str();
+}
+} // namespace webrtc
diff --git a/api/units/data_rate.h b/api/units/data_rate.h
new file mode 100644
index 0000000..067b200
--- /dev/null
+++ b/api/units/data_rate.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_UNITS_DATA_RATE_H_
+#define API_UNITS_DATA_RATE_H_
+#include <stdint.h>
+#include <cmath>
+#include <limits>
+#include <string>
+
+#include "rtc_base/checks.h"
+
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+
+namespace webrtc {
+namespace data_rate_impl {
+constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
+
+inline int64_t Microbits(const DataSize& size) {
+ constexpr int64_t kMaxBeforeConversion =
+ std::numeric_limits<int64_t>::max() / 8000000;
+ RTC_DCHECK_LE(size.bytes(), kMaxBeforeConversion)
+ << "size is too large to be expressed in microbytes";
+ return size.bytes() * 8000000;
+}
+} // namespace data_rate_impl
+
+// DataRate is a class that represents a given data rate. This can be used to
+// represent bandwidth, encoding bitrate, etc. The internal storage is bits per
+// second (bps).
+class DataRate {
+ public:
+ DataRate() = delete;
+ static DataRate Zero() { return DataRate(0); }
+ static DataRate Infinity() {
+ return DataRate(data_rate_impl::kPlusInfinityVal);
+ }
+ static DataRate bits_per_second(int64_t bits_per_sec) {
+ RTC_DCHECK_GE(bits_per_sec, 0);
+ return DataRate(bits_per_sec);
+ }
+ static DataRate bps(int64_t bits_per_sec) {
+ return DataRate::bits_per_second(bits_per_sec);
+ }
+ static DataRate kbps(int64_t kilobits_per_sec) {
+ return DataRate::bits_per_second(kilobits_per_sec * 1000);
+ }
+ int64_t bits_per_second() const {
+ RTC_DCHECK(IsFinite());
+ return bits_per_sec_;
+ }
+ int64_t bps() const { return bits_per_second(); }
+ int64_t kbps() const { return (bps() + 500) / 1000; }
+ bool IsZero() const { return bits_per_sec_ == 0; }
+ bool IsInfinite() const {
+ return bits_per_sec_ == data_rate_impl::kPlusInfinityVal;
+ }
+ bool IsFinite() const { return !IsInfinite(); }
+
+ bool operator==(const DataRate& other) const {
+ return bits_per_sec_ == other.bits_per_sec_;
+ }
+ bool operator!=(const DataRate& other) const {
+ return bits_per_sec_ != other.bits_per_sec_;
+ }
+ bool operator<=(const DataRate& other) const {
+ return bits_per_sec_ <= other.bits_per_sec_;
+ }
+ bool operator>=(const DataRate& other) const {
+ return bits_per_sec_ >= other.bits_per_sec_;
+ }
+ bool operator>(const DataRate& other) const {
+ return bits_per_sec_ > other.bits_per_sec_;
+ }
+ bool operator<(const DataRate& other) const {
+ return bits_per_sec_ < other.bits_per_sec_;
+ }
+
+ private:
+ // Bits per second used internally to simplify debugging by making the value
+ // more recognizable.
+ explicit DataRate(int64_t bits_per_second) : bits_per_sec_(bits_per_second) {}
+ int64_t bits_per_sec_;
+};
+
+inline DataRate operator*(const DataRate& rate, const double& scalar) {
+ return DataRate::bits_per_second(std::round(rate.bits_per_second() * scalar));
+}
+inline DataRate operator*(const double& scalar, const DataRate& rate) {
+ return rate * scalar;
+}
+inline DataRate operator*(const DataRate& rate, const int64_t& scalar) {
+ return DataRate::bits_per_second(rate.bits_per_second() * scalar);
+}
+inline DataRate operator*(const int64_t& scalar, const DataRate& rate) {
+ return rate * scalar;
+}
+inline DataRate operator*(const DataRate& rate, const int32_t& scalar) {
+ return DataRate::bits_per_second(rate.bits_per_second() * scalar);
+}
+inline DataRate operator*(const int32_t& scalar, const DataRate& rate) {
+ return rate * scalar;
+}
+
+inline DataRate operator/(const DataSize& size, const TimeDelta& duration) {
+ return DataRate::bits_per_second(data_rate_impl::Microbits(size) /
+ duration.us());
+}
+inline TimeDelta operator/(const DataSize& size, const DataRate& rate) {
+ return TimeDelta::us(data_rate_impl::Microbits(size) /
+ rate.bits_per_second());
+}
+inline DataSize operator*(const DataRate& rate, const TimeDelta& duration) {
+ int64_t microbits = rate.bits_per_second() * duration.us();
+ return DataSize::bytes((microbits + 4000000) / 8000000);
+}
+inline DataSize operator*(const TimeDelta& duration, const DataRate& rate) {
+ return rate * duration;
+}
+
+std::string ToString(const DataRate& value);
+
+} // namespace webrtc
+
+#endif // API_UNITS_DATA_RATE_H_
diff --git a/api/units/data_rate_unittest.cc b/api/units/data_rate_unittest.cc
new file mode 100644
index 0000000..d4dd192
--- /dev/null
+++ b/api/units/data_rate_unittest.cc
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018 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 "api/units/data_rate.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+TEST(DataRateTest, GetBackSameValues) {
+ const int64_t kValue = 123 * 8;
+ EXPECT_EQ(DataRate::bits_per_second(kValue).bits_per_second(), kValue);
+ EXPECT_EQ(DataRate::bps(kValue).bps(), kValue);
+ EXPECT_EQ(DataRate::kbps(kValue).kbps(), kValue);
+}
+
+TEST(DataRateTest, GetDifferentPrefix) {
+ const int64_t kValue = 123 * 8000;
+ EXPECT_EQ(DataRate::bps(kValue).kbps(), kValue / 1000);
+}
+
+TEST(DataRateTest, IdentityChecks) {
+ const int64_t kValue = 3000;
+ EXPECT_TRUE(DataRate::Zero().IsZero());
+ EXPECT_FALSE(DataRate::bits_per_second(kValue).IsZero());
+
+ EXPECT_TRUE(DataRate::Infinity().IsInfinite());
+ EXPECT_FALSE(DataRate::Zero().IsInfinite());
+ EXPECT_FALSE(DataRate::bits_per_second(kValue).IsInfinite());
+
+ EXPECT_FALSE(DataRate::Infinity().IsFinite());
+ EXPECT_TRUE(DataRate::bits_per_second(kValue).IsFinite());
+ EXPECT_TRUE(DataRate::Zero().IsFinite());
+}
+
+TEST(DataRateTest, ComparisonOperators) {
+ const int64_t kSmall = 450;
+ const int64_t kLarge = 451;
+ const DataRate small = DataRate::bits_per_second(kSmall);
+ const DataRate large = DataRate::bits_per_second(kLarge);
+
+ EXPECT_EQ(DataRate::Zero(), DataRate::bps(0));
+ EXPECT_EQ(DataRate::Infinity(), DataRate::Infinity());
+ EXPECT_EQ(small, small);
+ EXPECT_LE(small, small);
+ EXPECT_GE(small, small);
+ EXPECT_NE(small, large);
+ EXPECT_LE(small, large);
+ EXPECT_LT(small, large);
+ EXPECT_GE(large, small);
+ EXPECT_GT(large, small);
+ EXPECT_LT(DataRate::Zero(), small);
+ EXPECT_GT(DataRate::Infinity(), large);
+}
+
+TEST(DataRateTest, MathOperations) {
+ const int64_t kValueA = 450;
+ const int64_t kValueB = 267;
+ const DataRate size_a = DataRate::bits_per_second(kValueA);
+ const int32_t kInt32Value = 123;
+ const double kFloatValue = 123.0;
+ EXPECT_EQ((size_a * kValueB).bits_per_second(), kValueA * kValueB);
+ EXPECT_EQ((size_a * kInt32Value).bits_per_second(), kValueA * kInt32Value);
+ EXPECT_EQ((size_a * kFloatValue).bits_per_second(), kValueA * kFloatValue);
+}
+
+TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) {
+ const int64_t kSeconds = 5;
+ const int64_t kBitsPerSecond = 440;
+ const int64_t kBytes = 44000;
+ const TimeDelta delta_a = TimeDelta::seconds(kSeconds);
+ const DataRate rate_b = DataRate::bits_per_second(kBitsPerSecond);
+ const DataSize size_c = DataSize::bytes(kBytes);
+ EXPECT_EQ((delta_a * rate_b).bytes(), kSeconds * kBitsPerSecond / 8);
+ EXPECT_EQ((rate_b * delta_a).bytes(), kSeconds * kBitsPerSecond / 8);
+ EXPECT_EQ((size_c / delta_a).bits_per_second(), kBytes * 8 / kSeconds);
+ EXPECT_EQ((size_c / rate_b).seconds(), kBytes * 8 / kBitsPerSecond);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST(UnitConversionTest, DivisionByZeroFails) {
+ const DataSize non_zero_size = DataSize::bytes(100);
+ const DataSize zero_size = DataSize::Zero();
+ const DataRate zero_rate = DataRate::Zero();
+ const TimeDelta zero_delta = TimeDelta::Zero();
+
+ EXPECT_DEATH(non_zero_size / zero_rate, "");
+ EXPECT_DEATH(non_zero_size / zero_delta, "");
+ EXPECT_DEATH(zero_size / zero_rate, "");
+ EXPECT_DEATH(zero_size / zero_delta, "");
+}
+
+TEST(UnitConversionTest, DivisionFailsOnLargeSize) {
+ // Note that the failure is expected since the current implementation is
+ // implementated in a way that does not support division of large sizes. If
+ // the implementation is changed, this test can safely be removed.
+ const int64_t kJustSmallEnoughForDivision =
+ std::numeric_limits<int64_t>::max() / 8000000;
+ const int64_t kToolargeForDivision = kJustSmallEnoughForDivision + 1;
+ const DataSize large_size = DataSize::bytes(kJustSmallEnoughForDivision);
+ const DataSize too_large_size = DataSize::bytes(kToolargeForDivision);
+ const DataRate data_rate = DataRate::kbps(100);
+ const TimeDelta time_delta = TimeDelta::ms(100);
+ EXPECT_TRUE((large_size / data_rate).IsFinite());
+ EXPECT_TRUE((large_size / time_delta).IsFinite());
+
+ EXPECT_DEATH(too_large_size / data_rate, "");
+ EXPECT_DEATH(too_large_size / time_delta, "");
+}
+#endif // GTEST_HAS_DEATH_TEST && !!defined(WEBRTC_ANDROID)
+} // namespace test
+} // namespace webrtc
diff --git a/api/units/data_size.cc b/api/units/data_size.cc
new file mode 100644
index 0000000..4440f89
--- /dev/null
+++ b/api/units/data_size.cc
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 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 "api/units/data_size.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+
+std::string ToString(const DataSize& value) {
+ char buf[64];
+ rtc::SimpleStringBuilder sb(buf);
+ if (value.IsInfinite()) {
+ sb << "inf bytes";
+ } else {
+ sb << value.bytes() << " bytes";
+ }
+ return sb.str();
+}
+} // namespace webrtc
diff --git a/api/units/data_size.h b/api/units/data_size.h
new file mode 100644
index 0000000..74ab19e
--- /dev/null
+++ b/api/units/data_size.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_UNITS_DATA_SIZE_H_
+#define API_UNITS_DATA_SIZE_H_
+
+#include <stdint.h>
+#include <cmath>
+#include <limits>
+#include <string>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace data_size_impl {
+constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
+} // namespace data_size_impl
+
+// DataSize is a class represeting a count of bytes.
+class DataSize {
+ public:
+ DataSize() = delete;
+ static DataSize Zero() { return DataSize(0); }
+ static DataSize Infinity() {
+ return DataSize(data_size_impl::kPlusInfinityVal);
+ }
+ static DataSize bytes(int64_t bytes) {
+ RTC_DCHECK_GE(bytes, 0);
+ return DataSize(bytes);
+ }
+ int64_t bytes() const {
+ RTC_DCHECK(IsFinite());
+ return bytes_;
+ }
+ int64_t kilobytes() const { return (bytes() + 500) / 1000; }
+ bool IsZero() const { return bytes_ == 0; }
+ bool IsInfinite() const { return bytes_ == data_size_impl::kPlusInfinityVal; }
+ bool IsFinite() const { return !IsInfinite(); }
+ DataSize operator-(const DataSize& other) const {
+ return DataSize::bytes(bytes() - other.bytes());
+ }
+ DataSize operator+(const DataSize& other) const {
+ return DataSize::bytes(bytes() + other.bytes());
+ }
+ DataSize& operator-=(const DataSize& other) {
+ bytes_ -= other.bytes();
+ return *this;
+ }
+ DataSize& operator+=(const DataSize& other) {
+ bytes_ += other.bytes();
+ return *this;
+ }
+ bool operator==(const DataSize& other) const {
+ return bytes_ == other.bytes_;
+ }
+ bool operator!=(const DataSize& other) const {
+ return bytes_ != other.bytes_;
+ }
+ bool operator<=(const DataSize& other) const {
+ return bytes_ <= other.bytes_;
+ }
+ bool operator>=(const DataSize& other) const {
+ return bytes_ >= other.bytes_;
+ }
+ bool operator>(const DataSize& other) const { return bytes_ > other.bytes_; }
+ bool operator<(const DataSize& other) const { return bytes_ < other.bytes_; }
+
+ private:
+ explicit DataSize(int64_t bytes) : bytes_(bytes) {}
+ int64_t bytes_;
+};
+inline DataSize operator*(const DataSize& size, const double& scalar) {
+ return DataSize::bytes(std::round(size.bytes() * scalar));
+}
+inline DataSize operator*(const double& scalar, const DataSize& size) {
+ return size * scalar;
+}
+inline DataSize operator*(const DataSize& size, const int64_t& scalar) {
+ return DataSize::bytes(size.bytes() * scalar);
+}
+inline DataSize operator*(const int64_t& scalar, const DataSize& size) {
+ return size * scalar;
+}
+inline DataSize operator*(const DataSize& size, const int32_t& scalar) {
+ return DataSize::bytes(size.bytes() * scalar);
+}
+inline DataSize operator*(const int32_t& scalar, const DataSize& size) {
+ return size * scalar;
+}
+inline DataSize operator/(const DataSize& size, const int64_t& scalar) {
+ return DataSize::bytes(size.bytes() / scalar);
+}
+
+std::string ToString(const DataSize& value);
+
+} // namespace webrtc
+
+#endif // API_UNITS_DATA_SIZE_H_
diff --git a/api/units/data_size_unittest.cc b/api/units/data_size_unittest.cc
new file mode 100644
index 0000000..35cff01
--- /dev/null
+++ b/api/units/data_size_unittest.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018 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 "api/units/data_size.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+TEST(DataSizeTest, GetBackSameValues) {
+ const int64_t kValue = 123 * 8;
+ EXPECT_EQ(DataSize::bytes(kValue).bytes(), kValue);
+}
+
+TEST(DataSizeTest, GetDifferentPrefix) {
+ const int64_t kValue = 123 * 8000;
+ EXPECT_EQ(DataSize::bytes(kValue).kilobytes(), kValue / 1000);
+}
+
+TEST(DataSizeTest, IdentityChecks) {
+ const int64_t kValue = 3000;
+ EXPECT_TRUE(DataSize::Zero().IsZero());
+ EXPECT_FALSE(DataSize::bytes(kValue).IsZero());
+
+ EXPECT_TRUE(DataSize::Infinity().IsInfinite());
+ EXPECT_FALSE(DataSize::Zero().IsInfinite());
+ EXPECT_FALSE(DataSize::bytes(kValue).IsInfinite());
+
+ EXPECT_FALSE(DataSize::Infinity().IsFinite());
+ EXPECT_TRUE(DataSize::bytes(kValue).IsFinite());
+ EXPECT_TRUE(DataSize::Zero().IsFinite());
+}
+
+TEST(DataSizeTest, ComparisonOperators) {
+ const int64_t kSmall = 450;
+ const int64_t kLarge = 451;
+ const DataSize small = DataSize::bytes(kSmall);
+ const DataSize large = DataSize::bytes(kLarge);
+
+ EXPECT_EQ(DataSize::Zero(), DataSize::bytes(0));
+ EXPECT_EQ(DataSize::Infinity(), DataSize::Infinity());
+ EXPECT_EQ(small, small);
+ EXPECT_LE(small, small);
+ EXPECT_GE(small, small);
+ EXPECT_NE(small, large);
+ EXPECT_LE(small, large);
+ EXPECT_LT(small, large);
+ EXPECT_GE(large, small);
+ EXPECT_GT(large, small);
+ EXPECT_LT(DataSize::Zero(), small);
+ EXPECT_GT(DataSize::Infinity(), large);
+}
+
+TEST(DataSizeTest, MathOperations) {
+ const int64_t kValueA = 450;
+ const int64_t kValueB = 267;
+ const DataSize size_a = DataSize::bytes(kValueA);
+ const DataSize size_b = DataSize::bytes(kValueB);
+ EXPECT_EQ((size_a + size_b).bytes(), kValueA + kValueB);
+ EXPECT_EQ((size_a - size_b).bytes(), kValueA - kValueB);
+
+ const int32_t kInt32Value = 123;
+ const double kFloatValue = 123.0;
+ EXPECT_EQ((size_a * kValueB).bytes(), kValueA * kValueB);
+ EXPECT_EQ((size_a * kInt32Value).bytes(), kValueA * kInt32Value);
+ EXPECT_EQ((size_a * kFloatValue).bytes(), kValueA * kFloatValue);
+
+ EXPECT_EQ((size_a / 10).bytes(), kValueA / 10);
+
+ DataSize mutable_size = DataSize::bytes(kValueA);
+ mutable_size += size_b;
+ EXPECT_EQ(mutable_size.bytes(), kValueA + kValueB);
+ mutable_size -= size_a;
+ EXPECT_EQ(mutable_size.bytes(), kValueB);
+}
+} // namespace test
+} // namespace webrtc
diff --git a/api/units/time_delta.cc b/api/units/time_delta.cc
new file mode 100644
index 0000000..398df77
--- /dev/null
+++ b/api/units/time_delta.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 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 "api/units/time_delta.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+
+std::string ToString(const TimeDelta& value) {
+ char buf[64];
+ rtc::SimpleStringBuilder sb(buf);
+ if (value.IsPlusInfinity()) {
+ sb << "+inf ms";
+ } else if (value.IsMinusInfinity()) {
+ sb << "-inf ms";
+ } else {
+ sb << value.ms() << " ms";
+ }
+ return sb.str();
+}
+} // namespace webrtc
diff --git a/api/units/time_delta.h b/api/units/time_delta.h
new file mode 100644
index 0000000..826e1a3
--- /dev/null
+++ b/api/units/time_delta.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_UNITS_TIME_DELTA_H_
+#define API_UNITS_TIME_DELTA_H_
+
+#include <stdint.h>
+#include <cmath>
+#include <limits>
+#include <string>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace timedelta_impl {
+constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
+constexpr int64_t kMinusInfinityVal = std::numeric_limits<int64_t>::min();
+} // namespace timedelta_impl
+
+// TimeDelta represents the difference between two timestamps. Commonly this can
+// be a duration. However since two Timestamps are not guaranteed to have the
+// same epoch (they might come from different computers, making exact
+// synchronisation infeasible), the duration covered by a TimeDelta can be
+// undefined. To simplify usage, it can be constructed and converted to
+// different units, specifically seconds (s), milliseconds (ms) and
+// microseconds (us).
+class TimeDelta {
+ public:
+ TimeDelta() = delete;
+ static TimeDelta Zero() { return TimeDelta(0); }
+ static TimeDelta PlusInfinity() {
+ return TimeDelta(timedelta_impl::kPlusInfinityVal);
+ }
+ static TimeDelta MinusInfinity() {
+ return TimeDelta(timedelta_impl::kMinusInfinityVal);
+ }
+ static TimeDelta seconds(int64_t seconds) {
+ return TimeDelta::us(seconds * 1000000);
+ }
+ static TimeDelta ms(int64_t milliseconds) {
+ return TimeDelta::us(milliseconds * 1000);
+ }
+ static TimeDelta us(int64_t microseconds) {
+ // Infinities only allowed via use of explicit constants.
+ RTC_DCHECK(microseconds > std::numeric_limits<int64_t>::min());
+ RTC_DCHECK(microseconds < std::numeric_limits<int64_t>::max());
+ return TimeDelta(microseconds);
+ }
+ int64_t seconds() const {
+ return (us() + (us() >= 0 ? 500000 : -500000)) / 1000000;
+ }
+ int64_t ms() const { return (us() + (us() >= 0 ? 500 : -500)) / 1000; }
+ int64_t us() const {
+ RTC_DCHECK(IsFinite());
+ return microseconds_;
+ }
+
+ double SecondsAsDouble() const;
+
+ TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
+ bool IsZero() const { return microseconds_ == 0; }
+ bool IsFinite() const { return !IsInfinite(); }
+ bool IsInfinite() const {
+ return microseconds_ == timedelta_impl::kPlusInfinityVal ||
+ microseconds_ == timedelta_impl::kMinusInfinityVal;
+ }
+ bool IsPlusInfinity() const {
+ return microseconds_ == timedelta_impl::kPlusInfinityVal;
+ }
+ bool IsMinusInfinity() const {
+ return microseconds_ == timedelta_impl::kMinusInfinityVal;
+ }
+ TimeDelta operator+(const TimeDelta& other) const {
+ return TimeDelta::us(us() + other.us());
+ }
+ TimeDelta operator-(const TimeDelta& other) const {
+ return TimeDelta::us(us() - other.us());
+ }
+ TimeDelta& operator-=(const TimeDelta& other) {
+ microseconds_ -= other.us();
+ return *this;
+ }
+ TimeDelta& operator+=(const TimeDelta& other) {
+ microseconds_ += other.us();
+ return *this;
+ }
+
+ bool operator==(const TimeDelta& other) const {
+ return microseconds_ == other.microseconds_;
+ }
+ bool operator!=(const TimeDelta& other) const {
+ return microseconds_ != other.microseconds_;
+ }
+ bool operator<=(const TimeDelta& other) const {
+ return microseconds_ <= other.microseconds_;
+ }
+ bool operator>=(const TimeDelta& other) const {
+ return microseconds_ >= other.microseconds_;
+ }
+ bool operator>(const TimeDelta& other) const {
+ return microseconds_ > other.microseconds_;
+ }
+ bool operator<(const TimeDelta& other) const {
+ return microseconds_ < other.microseconds_;
+ }
+
+ private:
+ explicit TimeDelta(int64_t us) : microseconds_(us) {}
+ int64_t microseconds_;
+};
+
+inline TimeDelta operator*(const TimeDelta& delta, const double& scalar) {
+ return TimeDelta::us(std::round(delta.us() * scalar));
+}
+inline TimeDelta operator*(const double& scalar, const TimeDelta& delta) {
+ return delta * scalar;
+}
+inline TimeDelta operator*(const TimeDelta& delta, const int64_t& scalar) {
+ return TimeDelta::us(delta.us() * scalar);
+}
+inline TimeDelta operator*(const int64_t& scalar, const TimeDelta& delta) {
+ return delta * scalar;
+}
+inline TimeDelta operator*(const TimeDelta& delta, const int32_t& scalar) {
+ return TimeDelta::us(delta.us() * scalar);
+}
+inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) {
+ return delta * scalar;
+}
+
+inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) {
+ return TimeDelta::us(delta.us() / scalar);
+}
+
+std::string ToString(const TimeDelta& value);
+} // namespace webrtc
+
+#endif // API_UNITS_TIME_DELTA_H_
diff --git a/api/units/time_delta_unittest.cc b/api/units/time_delta_unittest.cc
new file mode 100644
index 0000000..493c6bf
--- /dev/null
+++ b/api/units/time_delta_unittest.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018 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 "api/units/time_delta.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+TEST(TimeDeltaTest, GetBackSameValues) {
+ const int64_t kValue = 499;
+ for (int sign = -1; sign <= 1; ++sign) {
+ int64_t value = kValue * sign;
+ EXPECT_EQ(TimeDelta::ms(value).ms(), value);
+ EXPECT_EQ(TimeDelta::us(value).us(), value);
+ EXPECT_EQ(TimeDelta::seconds(value).seconds(), value);
+ EXPECT_EQ(TimeDelta::seconds(value).seconds(), value);
+ }
+ EXPECT_EQ(TimeDelta::Zero().us(), 0);
+}
+
+TEST(TimeDeltaTest, GetDifferentPrefix) {
+ const int64_t kValue = 3000000;
+ EXPECT_EQ(TimeDelta::us(kValue).seconds(), kValue / 1000000);
+ EXPECT_EQ(TimeDelta::ms(kValue).seconds(), kValue / 1000);
+ EXPECT_EQ(TimeDelta::us(kValue).ms(), kValue / 1000);
+
+ EXPECT_EQ(TimeDelta::ms(kValue).us(), kValue * 1000);
+ EXPECT_EQ(TimeDelta::seconds(kValue).ms(), kValue * 1000);
+ EXPECT_EQ(TimeDelta::seconds(kValue).us(), kValue * 1000000);
+}
+
+TEST(TimeDeltaTest, IdentityChecks) {
+ const int64_t kValue = 3000;
+ EXPECT_TRUE(TimeDelta::Zero().IsZero());
+ EXPECT_FALSE(TimeDelta::ms(kValue).IsZero());
+
+ EXPECT_TRUE(TimeDelta::PlusInfinity().IsInfinite());
+ EXPECT_TRUE(TimeDelta::MinusInfinity().IsInfinite());
+ EXPECT_FALSE(TimeDelta::Zero().IsInfinite());
+ EXPECT_FALSE(TimeDelta::ms(-kValue).IsInfinite());
+ EXPECT_FALSE(TimeDelta::ms(kValue).IsInfinite());
+
+ EXPECT_FALSE(TimeDelta::PlusInfinity().IsFinite());
+ EXPECT_FALSE(TimeDelta::MinusInfinity().IsFinite());
+ EXPECT_TRUE(TimeDelta::ms(-kValue).IsFinite());
+ EXPECT_TRUE(TimeDelta::ms(kValue).IsFinite());
+ EXPECT_TRUE(TimeDelta::Zero().IsFinite());
+}
+
+TEST(TimeDeltaTest, ComparisonOperators) {
+ const int64_t kSmall = 450;
+ const int64_t kLarge = 451;
+ const TimeDelta small = TimeDelta::ms(kSmall);
+ const TimeDelta large = TimeDelta::ms(kLarge);
+
+ EXPECT_EQ(TimeDelta::Zero(), TimeDelta::ms(0));
+ EXPECT_EQ(TimeDelta::PlusInfinity(), TimeDelta::PlusInfinity());
+ EXPECT_EQ(small, TimeDelta::ms(kSmall));
+ EXPECT_LE(small, TimeDelta::ms(kSmall));
+ EXPECT_GE(small, TimeDelta::ms(kSmall));
+ EXPECT_NE(small, TimeDelta::ms(kLarge));
+ EXPECT_LE(small, TimeDelta::ms(kLarge));
+ EXPECT_LT(small, TimeDelta::ms(kLarge));
+ EXPECT_GE(large, TimeDelta::ms(kSmall));
+ EXPECT_GT(large, TimeDelta::ms(kSmall));
+ EXPECT_LT(TimeDelta::Zero(), small);
+ EXPECT_GT(TimeDelta::Zero(), TimeDelta::ms(-kSmall));
+ EXPECT_GT(TimeDelta::Zero(), TimeDelta::ms(-kSmall));
+
+ EXPECT_GT(TimeDelta::PlusInfinity(), large);
+ EXPECT_LT(TimeDelta::MinusInfinity(), TimeDelta::Zero());
+}
+
+TEST(TimeDeltaTest, MathOperations) {
+ const int64_t kValueA = 267;
+ const int64_t kValueB = 450;
+ const TimeDelta delta_a = TimeDelta::ms(kValueA);
+ const TimeDelta delta_b = TimeDelta::ms(kValueB);
+ EXPECT_EQ((delta_a + delta_b).ms(), kValueA + kValueB);
+ EXPECT_EQ((delta_a - delta_b).ms(), kValueA - kValueB);
+
+ const int32_t kInt32Value = 123;
+ const double kFloatValue = 123.0;
+ EXPECT_EQ((TimeDelta::us(kValueA) * kValueB).us(), kValueA * kValueB);
+ EXPECT_EQ((TimeDelta::us(kValueA) * kInt32Value).us(), kValueA * kInt32Value);
+ EXPECT_EQ((TimeDelta::us(kValueA) * kFloatValue).us(), kValueA * kFloatValue);
+
+ EXPECT_EQ(TimeDelta::us(-kValueA).Abs().us(), kValueA);
+ EXPECT_EQ(TimeDelta::us(kValueA).Abs().us(), kValueA);
+}
+} // namespace test
+} // namespace webrtc
diff --git a/api/units/timestamp.cc b/api/units/timestamp.cc
new file mode 100644
index 0000000..7ae084c
--- /dev/null
+++ b/api/units/timestamp.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 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 "api/units/timestamp.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+double Timestamp::SecondsAsDouble() const {
+ if (IsInfinite()) {
+ return std::numeric_limits<double>::infinity();
+ } else {
+ return us() * 1e-6;
+ }
+}
+
+std::string ToString(const Timestamp& value) {
+ char buf[64];
+ rtc::SimpleStringBuilder sb(buf);
+ if (value.IsInfinite()) {
+ sb << "inf ms";
+ } else {
+ sb << value.ms() << " ms";
+ }
+ return sb.str();
+}
+} // namespace webrtc
diff --git a/api/units/timestamp.h b/api/units/timestamp.h
new file mode 100644
index 0000000..af62b3b
--- /dev/null
+++ b/api/units/timestamp.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_UNITS_TIMESTAMP_H_
+#define API_UNITS_TIMESTAMP_H_
+
+#include <stdint.h>
+#include <limits>
+#include <string>
+
+#include "api/units/time_delta.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace timestamp_impl {
+constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
+constexpr int64_t kMinusInfinityVal = std::numeric_limits<int64_t>::min();
+} // namespace timestamp_impl
+
+// Timestamp represents the time that has passed since some unspecified epoch.
+// The epoch is assumed to be before any represented timestamps, this means that
+// negative values are not valid. The most notable feature is that the
+// difference of two Timestamps results in a TimeDelta.
+class Timestamp {
+ public:
+ Timestamp() = delete;
+ static Timestamp Infinity() {
+ return Timestamp(timestamp_impl::kPlusInfinityVal);
+ }
+ static Timestamp seconds(int64_t seconds) {
+ return Timestamp::us(seconds * 1000000);
+ }
+ static Timestamp ms(int64_t millis) { return Timestamp::us(millis * 1000); }
+ static Timestamp us(int64_t micros) {
+ RTC_DCHECK_GE(micros, 0);
+ return Timestamp(micros);
+ }
+ int64_t seconds() const { return (us() + 500000) / 1000000; }
+ int64_t ms() const { return (us() + 500) / 1000; }
+ int64_t us() const {
+ RTC_DCHECK(IsFinite());
+ return microseconds_;
+ }
+
+ double SecondsAsDouble() const;
+
+ bool IsInfinite() const {
+ return microseconds_ == timestamp_impl::kPlusInfinityVal;
+ }
+ bool IsFinite() const { return !IsInfinite(); }
+ TimeDelta operator-(const Timestamp& other) const {
+ return TimeDelta::us(us() - other.us());
+ }
+ Timestamp operator-(const TimeDelta& delta) const {
+ return Timestamp::us(us() - delta.us());
+ }
+ Timestamp operator+(const TimeDelta& delta) const {
+ return Timestamp::us(us() + delta.us());
+ }
+ Timestamp& operator-=(const TimeDelta& other) {
+ microseconds_ -= other.us();
+ return *this;
+ }
+ Timestamp& operator+=(const TimeDelta& other) {
+ microseconds_ += other.us();
+ return *this;
+ }
+ bool operator==(const Timestamp& other) const {
+ return microseconds_ == other.microseconds_;
+ }
+ bool operator!=(const Timestamp& other) const {
+ return microseconds_ != other.microseconds_;
+ }
+ bool operator<=(const Timestamp& other) const { return us() <= other.us(); }
+ bool operator>=(const Timestamp& other) const { return us() >= other.us(); }
+ bool operator>(const Timestamp& other) const { return us() > other.us(); }
+ bool operator<(const Timestamp& other) const { return us() < other.us(); }
+
+ private:
+ explicit Timestamp(int64_t us) : microseconds_(us) {}
+ int64_t microseconds_;
+};
+
+std::string ToString(const Timestamp& value);
+
+} // namespace webrtc
+
+#endif // API_UNITS_TIMESTAMP_H_
diff --git a/api/units/timestamp_unittest.cc b/api/units/timestamp_unittest.cc
new file mode 100644
index 0000000..549a9c8
--- /dev/null
+++ b/api/units/timestamp_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018 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 "api/units/timestamp.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+TEST(TimestampTest, GetBackSameValues) {
+ const int64_t kValue = 499;
+ EXPECT_EQ(Timestamp::ms(kValue).ms(), kValue);
+ EXPECT_EQ(Timestamp::us(kValue).us(), kValue);
+ EXPECT_EQ(Timestamp::seconds(kValue).seconds(), kValue);
+}
+
+TEST(TimestampTest, GetDifferentPrefix) {
+ const int64_t kValue = 3000000;
+ EXPECT_EQ(Timestamp::us(kValue).seconds(), kValue / 1000000);
+ EXPECT_EQ(Timestamp::ms(kValue).seconds(), kValue / 1000);
+ EXPECT_EQ(Timestamp::us(kValue).ms(), kValue / 1000);
+
+ EXPECT_EQ(Timestamp::ms(kValue).us(), kValue * 1000);
+ EXPECT_EQ(Timestamp::seconds(kValue).ms(), kValue * 1000);
+ EXPECT_EQ(Timestamp::seconds(kValue).us(), kValue * 1000000);
+}
+
+TEST(TimestampTest, IdentityChecks) {
+ const int64_t kValue = 3000;
+
+ EXPECT_TRUE(Timestamp::Infinity().IsInfinite());
+ EXPECT_FALSE(Timestamp::ms(kValue).IsInfinite());
+
+ EXPECT_FALSE(Timestamp::Infinity().IsFinite());
+ EXPECT_TRUE(Timestamp::ms(kValue).IsFinite());
+}
+
+TEST(TimestampTest, ComparisonOperators) {
+ const int64_t kSmall = 450;
+ const int64_t kLarge = 451;
+
+ EXPECT_EQ(Timestamp::Infinity(), Timestamp::Infinity());
+ EXPECT_EQ(Timestamp::ms(kSmall), Timestamp::ms(kSmall));
+ EXPECT_LE(Timestamp::ms(kSmall), Timestamp::ms(kSmall));
+ EXPECT_GE(Timestamp::ms(kSmall), Timestamp::ms(kSmall));
+ EXPECT_NE(Timestamp::ms(kSmall), Timestamp::ms(kLarge));
+ EXPECT_LE(Timestamp::ms(kSmall), Timestamp::ms(kLarge));
+ EXPECT_LT(Timestamp::ms(kSmall), Timestamp::ms(kLarge));
+ EXPECT_GE(Timestamp::ms(kLarge), Timestamp::ms(kSmall));
+ EXPECT_GT(Timestamp::ms(kLarge), Timestamp::ms(kSmall));
+}
+
+TEST(UnitConversionTest, TimestampAndTimeDeltaMath) {
+ const int64_t kValueA = 267;
+ const int64_t kValueB = 450;
+ const Timestamp time_a = Timestamp::ms(kValueA);
+ const Timestamp time_b = Timestamp::ms(kValueB);
+ const TimeDelta delta_a = TimeDelta::ms(kValueA);
+
+ EXPECT_EQ((time_a - time_b), TimeDelta::ms(kValueA - kValueB));
+ EXPECT_EQ((time_b - delta_a), Timestamp::ms(kValueB - kValueA));
+ EXPECT_EQ((time_b + delta_a), Timestamp::ms(kValueB + kValueA));
+}
+} // namespace test
+} // namespace webrtc