diff --git a/src/channel/rtp_channel/rtp_video_receiver.cpp b/src/channel/rtp_channel/rtp_video_receiver.cpp index 0da9ec4..559f2f0 100644 --- a/src/channel/rtp_channel/rtp_video_receiver.cpp +++ b/src/channel/rtp_channel/rtp_video_receiver.cpp @@ -50,7 +50,7 @@ void RtpVideoReceiver::InsertRtpPacket(RtpPacket& rtp_packet) { rtp_packet_received.set_arrival_time( std::chrono::system_clock::now().time_since_epoch().count()); - rtp_packet_received.set_ecn(EcnMarking::kEct0); + rtp_packet_received.set_ecn(rtc::EcnMarking::kEct0); rtp_packet_received.set_recovered(false); rtp_packet_received.set_payload_type_frequency(0); receive_side_congestion_controller_.OnReceivedPacket( diff --git a/src/common/api/array_view.h b/src/common/api/array_view.h new file mode 100644 index 0000000..c2a7994 --- /dev/null +++ b/src/common/api/array_view.h @@ -0,0 +1,319 @@ +/* + * Copyright 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. + */ + +#ifndef API_ARRAY_VIEW_H_ +#define API_ARRAY_VIEW_H_ + +#include +#include +#include +#include +#include + +#include "rtc_base/type_traits.h" + +namespace rtc { + +// tl;dr: rtc::ArrayView is the same thing as gsl::span from the Guideline +// Support Library. +// +// Many functions read from or write to arrays. The obvious way to do this is +// to use two arguments, a pointer to the first element and an element count: +// +// bool Contains17(const int* arr, size_t size) { +// for (size_t i = 0; i < size; ++i) { +// if (arr[i] == 17) +// return true; +// } +// return false; +// } +// +// This is flexible, since it doesn't matter how the array is stored (C array, +// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has +// to correctly specify the array length: +// +// Contains17(arr, arraysize(arr)); // C array +// Contains17(arr.data(), arr.size()); // std::vector +// Contains17(arr, size); // pointer + size +// ... +// +// It's also kind of messy to have two separate arguments for what is +// conceptually a single thing. +// +// Enter rtc::ArrayView. It contains a T pointer (to an array it doesn't +// own) and a count, and supports the basic things you'd expect, such as +// indexing and iteration. It allows us to write our function like this: +// +// bool Contains17(rtc::ArrayView arr) { +// for (auto e : arr) { +// if (e == 17) +// return true; +// } +// return false; +// } +// +// And even better, because a bunch of things will implicitly convert to +// ArrayView, we can call it like this: +// +// Contains17(arr); // C array +// Contains17(arr); // std::vector +// Contains17(rtc::ArrayView(arr, size)); // pointer + size +// Contains17(nullptr); // nullptr -> empty ArrayView +// ... +// +// ArrayView stores both a pointer and a size, but you may also use +// ArrayView, which has a size that's fixed at compile time (which means +// it only has to store the pointer). +// +// One important point is that ArrayView and ArrayView are +// different types, which allow and don't allow mutation of the array elements, +// respectively. The implicit conversions work just like you'd hope, so that +// e.g. vector will convert to either ArrayView or ArrayView, but const vector will convert only to ArrayView. +// (ArrayView itself can be the source type in such conversions, so +// ArrayView will convert to ArrayView.) +// +// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just +// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to +// pass it by value than by const reference. + +namespace array_view_internal { + +// Magic constant for indicating that the size of an ArrayView is variable +// instead of fixed. +enum : std::ptrdiff_t { kArrayViewVarSize = -4711 }; + +// Base class for ArrayViews of fixed nonzero size. +template +class ArrayViewBase { + static_assert(Size > 0, "ArrayView size must be variable or non-negative"); + + public: + ArrayViewBase(T* data, size_t /* size */) : data_(data) {} + + static constexpr size_t size() { return Size; } + static constexpr bool empty() { return false; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return true; } + + private: + T* data_; +}; + +// Specialized base class for ArrayViews of fixed zero size. +template +class ArrayViewBase { + public: + explicit ArrayViewBase(T* /* data */, size_t /* size */) {} + + static constexpr size_t size() { return 0; } + static constexpr bool empty() { return true; } + T* data() const { return nullptr; } + + protected: + static constexpr bool fixed_size() { return true; } +}; + +// Specialized base class for ArrayViews of variable size. +template +class ArrayViewBase { + public: + ArrayViewBase(T* data, size_t size) + : data_(size == 0 ? nullptr : data), size_(size) {} + + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return false; } + + private: + T* data_; + size_t size_; +}; + +} // namespace array_view_internal + +template +class ArrayView final : public array_view_internal::ArrayViewBase { + public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using const_iterator = const T*; + + // Construct an ArrayView from a pointer and a length. + template + ArrayView(U* data, size_t size) + : array_view_internal::ArrayViewBase::ArrayViewBase(data, size) { + } + + // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0 + // cannot be empty. + ArrayView() : ArrayView(nullptr, 0) {} + ArrayView(std::nullptr_t) // NOLINT + : ArrayView() {} + ArrayView(std::nullptr_t, size_t size) + : ArrayView(static_cast(nullptr), size) { + static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize, + ""); + } + + // Construct an ArrayView from a C-style array. + template + ArrayView(U (&array)[N]) // NOLINT + : ArrayView(array, N) { + static_assert(Size == N || Size == array_view_internal::kArrayViewVarSize, + "Array size must match ArrayView size"); + } + + // (Only if size is fixed.) Construct a fixed size ArrayView from a + // non-const std::array instance. For an ArrayView with variable size, the + // used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct a fixed size ArrayView where T is + // const from a const(expr) std::array instance. For an ArrayView with + // variable size, the used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(const std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct an ArrayView from any type U that has a + // static constexpr size() method whose return value is equal to Size, and a + // data() method whose return value converts implicitly to T*. In particular, + // this means we allow conversion from ArrayView to ArrayView, but not the other way around. We also don't allow conversion from + // ArrayView to ArrayView, or from ArrayView to ArrayView when M != N. + template ::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + template ::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + + // (Only if size is variable.) Construct an ArrayView from any type U that + // has a size() method whose return value converts implicitly to size_t, and + // a data() method whose return value converts implicitly to T*. In + // particular, this means we allow conversion from ArrayView to + // ArrayView, but not the other way around. Other allowed + // conversions include + // ArrayView to ArrayView or ArrayView, + // std::vector to ArrayView or ArrayView, + // const std::vector to ArrayView, + // rtc::Buffer to ArrayView or ArrayView, and + // const rtc::Buffer to ArrayView. + template ::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + template ::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) {} + + // Indexing and iteration. These allow mutation even if the ArrayView is + // const, because the ArrayView doesn't own the array. (To prevent mutation, + // use a const element type.) + T& operator[](size_t idx) const { return this->data()[idx]; } + T* begin() const { return this->data(); } + T* end() const { return this->data() + this->size(); } + const T* cbegin() const { return this->data(); } + const T* cend() const { return this->data() + this->size(); } + std::reverse_iterator rbegin() const { + return std::make_reverse_iterator(end()); + } + std::reverse_iterator rend() const { + return std::make_reverse_iterator(begin()); + } + std::reverse_iterator crbegin() const { + return std::make_reverse_iterator(cend()); + } + std::reverse_iterator crend() const { + return std::make_reverse_iterator(cbegin()); + } + + ArrayView subview(size_t offset, size_t size) const { + return offset < this->size() + ? ArrayView(this->data() + offset, + std::min(size, this->size() - offset)) + : ArrayView(); + } + ArrayView subview(size_t offset) const { + return subview(offset, this->size()); + } +}; + +// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not* +// dereference the pointers. +template +bool operator==(const ArrayView& a, const ArrayView& b) { + return a.data() == b.data() && a.size() == b.size(); +} +template +bool operator!=(const ArrayView& a, const ArrayView& b) { + return !(a == b); +} + +// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews +// are the size of one pointer. (And as a special case, fixed-size ArrayViews +// of size 0 require no storage.) +static_assert(sizeof(ArrayView) == 2 * sizeof(int*), ""); +static_assert(sizeof(ArrayView) == sizeof(int*), ""); +static_assert(std::is_empty>::value, ""); + +template +inline ArrayView MakeArrayView(T* data, size_t size) { + return ArrayView(data, size); +} + +// Only for primitive types that have the same size and aligment. +// Allow reinterpret cast of the array view to another primitive type of the +// same size. +// Template arguments order is (U, T, Size) to allow deduction of the template +// arguments in client calls: reinterpret_array_view(array_view). +template +inline ArrayView reinterpret_array_view(ArrayView view) { + static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T), + "ArrayView reinterpret_cast is only supported for casting " + "between views that represent the same chunk of memory."); + static_assert( + std::is_fundamental::value && std::is_fundamental::value, + "ArrayView reinterpret_cast is only supported for casting between " + "fundamental types."); + return ArrayView(reinterpret_cast(view.data()), view.size()); +} + +} // namespace rtc + +#endif // API_ARRAY_VIEW_H_ diff --git a/src/common/api/ref_count.h b/src/common/api/ref_count.h new file mode 100644 index 0000000..5209277 --- /dev/null +++ b/src/common/api/ref_count.h @@ -0,0 +1,67 @@ +/* + * Copyright 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. + */ +#ifndef API_REF_COUNT_H_ +#define API_REF_COUNT_H_ + +namespace webrtc { + +// Refcounted objects should implement the following informal interface: +// +// void AddRef() const ; +// RefCountReleaseStatus Release() const; +// +// You may access members of a reference-counted object, including the AddRef() +// and Release() methods, only if you already own a reference to it, or if +// you're borrowing someone else's reference. (A newly created object is a +// special case: the reference count is zero on construction, and the code that +// creates the object should immediately call AddRef(), bringing the reference +// count from zero to one, e.g., by constructing an rtc::scoped_refptr). +// +// AddRef() creates a new reference to the object. +// +// Release() releases a reference to the object; the caller now has one less +// reference than before the call. Returns kDroppedLastRef if the number of +// references dropped to zero because of this (in which case the object destroys +// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise +// time the caller's reference was dropped, other references still remained (but +// if other threads own references, this may of course have changed by the time +// Release() returns). +// +// The caller of Release() must treat it in the same way as a delete operation: +// Regardless of the return value from Release(), the caller mustn't access the +// object. The object might still be alive, due to references held by other +// users of the object, but the object can go away at any time, e.g., as the +// result of another thread calling Release(). +// +// Calling AddRef() and Release() manually is discouraged. It's recommended to +// use rtc::scoped_refptr to manage all pointers to reference counted objects. +// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally +// implementing the below RefCountInterface is not required. + +enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained }; + +// Interfaces where refcounting is part of the public api should +// inherit this abstract interface. The implementation of these +// methods is usually provided by the RefCountedObject template class, +// applied as a leaf in the inheritance tree. +class RefCountInterface { + public: + virtual void AddRef() const = 0; + virtual RefCountReleaseStatus Release() const = 0; + + // Non-public destructor, because Release() has exclusive responsibility for + // destroying the object. + protected: + virtual ~RefCountInterface() {} +}; + +} // namespace webrtc + +#endif // API_REF_COUNT_H_ diff --git a/src/common/api/ref_counted_base.h b/src/common/api/ref_counted_base.h new file mode 100644 index 0000000..a47a8bb --- /dev/null +++ b/src/common/api/ref_counted_base.h @@ -0,0 +1,107 @@ +/* + * Copyright 2017 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_REF_COUNTED_BASE_H_ +#define API_REF_COUNTED_BASE_H_ + +#include + +#include "api/ref_count.h" +#include "rtc_base/ref_counter.h" + +namespace webrtc { + +class RefCountedBase { + public: + RefCountedBase() = default; + + RefCountedBase(const RefCountedBase&) = delete; + RefCountedBase& operator=(const RefCountedBase&) = delete; + + void AddRef() const { ref_count_.IncRef(); } + RefCountReleaseStatus Release() const { + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + return status; + } + + protected: + // Provided for internal webrtc subclasses for corner cases where it's + // necessary to know whether or not a reference is exclusively held. + bool HasOneRef() const { return ref_count_.HasOneRef(); } + + virtual ~RefCountedBase() = default; + + private: + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +// Template based version of `RefCountedBase` for simple implementations that do +// not need (or want) destruction via virtual destructor or the overhead of a +// vtable. +// +// To use: +// struct MyInt : public rtc::RefCountedNonVirtual { +// int foo_ = 0; +// }; +// +// rtc::scoped_refptr my_int(new MyInt()); +// +// sizeof(MyInt) on a 32 bit system would then be 8, int + refcount and no +// vtable generated. +template +class RefCountedNonVirtual { + public: + RefCountedNonVirtual() = default; + + RefCountedNonVirtual(const RefCountedNonVirtual&) = delete; + RefCountedNonVirtual& operator=(const RefCountedNonVirtual&) = delete; + + void AddRef() const { ref_count_.IncRef(); } + RefCountReleaseStatus Release() const { + // If you run into this assert, T has virtual methods. There are two + // options: + // 1) The class doesn't actually need virtual methods, the type is complete + // so the virtual attribute(s) can be removed. + // 2) The virtual methods are a part of the design of the class. In this + // case you can consider using `RefCountedBase` instead or alternatively + // use `rtc::RefCountedObject`. + static_assert(!std::is_polymorphic::value, + "T has virtual methods. RefCountedBase is a better fit."); + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete static_cast(this); + } + return status; + } + + protected: + // Provided for internal webrtc subclasses for corner cases where it's + // necessary to know whether or not a reference is exclusively held. + bool HasOneRef() const { return ref_count_.HasOneRef(); } + + ~RefCountedNonVirtual() = default; + + private: + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +} // namespace webrtc + +// Backwards compatibe aliases. +// TODO: https://issues.webrtc.org/42225969 - deprecate and remove. +namespace rtc { +using RefCountedBase = webrtc::RefCountedBase; +template +using RefCountedNonVirtual = webrtc::RefCountedNonVirtual; +} // namespace rtc + +#endif // API_REF_COUNTED_BASE_H_ diff --git a/src/common/api/scoped_refptr.h b/src/common/api/scoped_refptr.h new file mode 100644 index 0000000..d4a086b --- /dev/null +++ b/src/common/api/scoped_refptr.h @@ -0,0 +1,203 @@ +/* + * Copyright 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. + */ + +// Originally these classes are from Chromium. +// http://src.chromium.org/viewvc/chrome/trunk/src/base/memory/ref_counted.h?view=markup + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = make_ref_counted(); +// foo->Method(param); +// // `foo` is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = make_ref_counted(); +// ... +// foo = nullptr; // explicitly releases `foo` +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = make_ref_counted(); +// scoped_refptr b; +// +// b.swap(a); +// // now, `b` references the MyFoo object, and `a` references null. +// } +// +// To make both `a` and `b` in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = make_ref_counted(); +// scoped_refptr b; +// +// b = a; +// // now, `a` and `b` each own a reference to the same MyFoo object. +// } +// + +#ifndef API_SCOPED_REFPTR_H_ +#define API_SCOPED_REFPTR_H_ + +#include +#include +#include + +namespace webrtc { + +template +class scoped_refptr { + public: + using element_type = T; + + scoped_refptr() : ptr_(nullptr) {} + scoped_refptr(std::nullptr_t) : ptr_(nullptr) {} // NOLINT(runtime/explicit) + + explicit scoped_refptr(T* p) : ptr_(p) {} + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) {} + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) {} + + // Move constructors. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(std::move(r.ptr_)) {} + + template + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(std::move(r.ptr_)) {} + + ~scoped_refptr() = default; + + T* get() const { return ptr_.get(); } + explicit operator bool() const { return ptr_ != nullptr; } + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_.get(); } + + T* release() { + T* retVal = ptr_.get(); + ptr_.reset(); + return retVal; + } + + scoped_refptr& operator=(T* p) { + ptr_.reset(p); + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + ptr_ = r.ptr_; + return *this; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + ptr_ = r.ptr_; + return *this; + } + + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + ptr_ = std::move(r.ptr_); + return *this; + } + + template + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + ptr_ = std::move(r.ptr_); + return *this; + } + + void swap(T** pp) noexcept { std::swap(ptr_, *pp); } + + void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } + + protected: + std::shared_ptr ptr_; +}; + +template +bool operator==(const scoped_refptr& a, const scoped_refptr& b) { + return a.get() == b.get(); +} +template +bool operator!=(const scoped_refptr& a, const scoped_refptr& b) { + return !(a == b); +} + +template +bool operator==(const scoped_refptr& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template +bool operator!=(const scoped_refptr& a, std::nullptr_t) { + return !(a == nullptr); +} + +template +bool operator==(std::nullptr_t, const scoped_refptr& a) { + return a.get() == nullptr; +} + +template +bool operator!=(std::nullptr_t, const scoped_refptr& a) { + return !(a == nullptr); +} + +// Comparison with raw pointer. +template +bool operator==(const scoped_refptr& a, const U* b) { + return a.get() == b; +} +template +bool operator!=(const scoped_refptr& a, const U* b) { + return !(a == b); +} + +template +bool operator==(const T* a, const scoped_refptr& b) { + return a == b.get(); +} +template +bool operator!=(const T* a, const scoped_refptr& b) { + return !(a == b); +} + +// Ordered comparison, needed for use as a std::map key. +template +bool operator<(const scoped_refptr& a, const scoped_refptr& b) { + return a.get() < b.get(); +} + +} // namespace webrtc + +namespace rtc { +// Backwards compatible alias. +// TODO: bugs.webrtc.org/42225969 - Deprecate and remove. +using ::webrtc::scoped_refptr; +} // namespace rtc + +#endif // API_SCOPED_REFPTR_H_ \ No newline at end of file diff --git a/src/common/api/units/data_rate.cc b/src/common/api/units/data_rate.cc new file mode 100644 index 0000000..baa0eb2 --- /dev/null +++ b/src/common/api/units/data_rate.cc @@ -0,0 +1,30 @@ +/* + * 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 + +namespace webrtc { + +std::string ToString(DataRate value) { + if (value.IsPlusInfinity()) { + return "+inf bps"; + } else if (value.IsMinusInfinity()) { + return "-inf bps"; + } else { + if (value.bps() == 0 || value.bps() % 1000 != 0) { + return std::to_string(value.bps()) + " bps"; + } else { + return std::to_string(value.kbps()) + " kbps"; + } + } +} +} // namespace webrtc diff --git a/src/common/api/units/data_rate.h b/src/common/api/units/data_rate.h new file mode 100644 index 0000000..6fdc307 --- /dev/null +++ b/src/common/api/units/data_rate.h @@ -0,0 +1,141 @@ +/* + * 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 +#include +#include +#include + +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "unit_base.h" + +namespace webrtc { +// 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 final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataRate BitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr DataRate BytesPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(8, value); + } + template + static constexpr DataRate KilobitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1000, value); + } + static constexpr DataRate Infinity() { return PlusInfinity(); } + + constexpr DataRate() = default; + + template + friend void AbslStringify(Sink& sink, DataRate value); + + template + constexpr T bps() const { + return ToValue(); + } + template + constexpr T bytes_per_sec() const { + return ToFraction<8, T>(); + } + template + constexpr T kbps() const { + return ToFraction<1000, T>(); + } + constexpr int64_t bps_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + constexpr int64_t kbps_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + + private: + // Bits per second used internally to simplify debugging by making the value + // more recognizable. + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +namespace data_rate_impl { +inline constexpr int64_t Microbits(const DataSize& size) { + constexpr int64_t kMaxBeforeConversion = + std::numeric_limits::max() / 8000000; + return size.bytes() * 8000000; +} + +inline constexpr int64_t MillibytePerSec(const DataRate& size) { + constexpr int64_t kMaxBeforeConversion = + std::numeric_limits::max() / (1000 / 8); + return size.bps() * (1000 / 8); +} +} // namespace data_rate_impl + +inline constexpr DataRate operator/(const DataSize size, + const TimeDelta duration) { + return DataRate::BitsPerSec(data_rate_impl::Microbits(size) / duration.us()); +} +inline constexpr TimeDelta operator/(const DataSize size, const DataRate rate) { + return TimeDelta::Micros(data_rate_impl::Microbits(size) / rate.bps()); +} +inline constexpr DataSize operator*(const DataRate rate, + const TimeDelta duration) { + int64_t microbits = rate.bps() * duration.us(); + return DataSize::Bytes((microbits + 4000000) / 8000000); +} +inline constexpr DataSize operator*(const TimeDelta duration, + const DataRate rate) { + return rate * duration; +} + +inline constexpr DataSize operator/(const DataRate rate, + const Frequency frequency) { + int64_t millihertz = frequency.millihertz(); + // Note that the value is truncated here reather than rounded, potentially + // introducing an error of .5 bytes if rounding were expected. + return DataSize::Bytes(data_rate_impl::MillibytePerSec(rate) / millihertz); +} +inline constexpr Frequency operator/(const DataRate rate, const DataSize size) { + return Frequency::MilliHertz(data_rate_impl::MillibytePerSec(rate) / + size.bytes()); +} +inline constexpr DataRate operator*(const DataSize size, + const Frequency frequency) { + int64_t millibits_per_second = + size.bytes() * 8 * frequency.millihertz(); + return DataRate::BitsPerSec((millibits_per_second + 500) / 1000); +} +inline constexpr DataRate operator*(const Frequency frequency, + const DataSize size) { + return size * frequency; +} + +std::string ToString(DataRate value); + +template +void AbslStringify(Sink& sink, DataRate value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_DATA_RATE_H_ diff --git a/src/common/api/units/data_size.cc b/src/common/api/units/data_size.cc new file mode 100644 index 0000000..29a9238 --- /dev/null +++ b/src/common/api/units/data_size.cc @@ -0,0 +1,26 @@ +/* + * 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 + +namespace webrtc { + +std::string ToString(DataSize value) { + if (value.IsPlusInfinity()) { + return "+inf bytes"; + } else if (value.IsMinusInfinity()) { + return "-inf bytes"; + } else { + return std::to_string(value.bytes()) + " bytes"; + } +} +} // namespace webrtc diff --git a/src/common/api/units/data_size.h b/src/common/api/units/data_size.h new file mode 100644 index 0000000..5516804 --- /dev/null +++ b/src/common/api/units/data_size.h @@ -0,0 +1,60 @@ +/* + * 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 +#include +#include + +#include "unit_base.h" // IWYU pragma: export + +namespace webrtc { +// DataSize is a class represeting a count of bytes. +class DataSize final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataSize Bytes(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + static constexpr DataSize Infinity() { return PlusInfinity(); } + + constexpr DataSize() = default; + + template + friend void AbslStringify(Sink& sink, DataSize value); + + template + constexpr T bytes() const { + return ToValue(); + } + + constexpr int64_t bytes_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +std::string ToString(DataSize value); + +template +void AbslStringify(Sink& sink, DataSize value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_DATA_SIZE_H_ diff --git a/src/common/api/units/frequency.cc b/src/common/api/units/frequency.cc new file mode 100644 index 0000000..2c22cb4 --- /dev/null +++ b/src/common/api/units/frequency.cc @@ -0,0 +1,27 @@ +/* + * Copyright (c) 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 "frequency.h" + +#include +#include + +namespace webrtc { +std::string ToString(Frequency value) { + if (value.IsPlusInfinity()) { + return "+inf Hz"; + } else if (value.IsMinusInfinity()) { + return "-inf Hz"; + } else if (value.millihertz() % 1000 != 0) { + return std::to_string(value.hertz()) + " Hz"; + } else { + return std::to_string(value.hertz()) + " Hz"; + } +} +} // namespace webrtc diff --git a/src/common/api/units/frequency.h b/src/common/api/units/frequency.h new file mode 100644 index 0000000..a491bcb --- /dev/null +++ b/src/common/api/units/frequency.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 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. + */ +#ifndef API_UNITS_FREQUENCY_H_ +#define API_UNITS_FREQUENCY_H_ + +#include +#include +#include +#include +#include + +#include "api/units/time_delta.h" +#include "unit_base.h" // IWYU pragma: export + +namespace webrtc { + +class Frequency final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr Frequency MilliHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr Frequency Hertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Frequency KiloHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + + constexpr Frequency() = default; + + template + friend void AbslStringify(Sink& sink, Frequency value); + + template + constexpr T hertz() const { + return ToFraction<1000, T>(); + } + template + constexpr T millihertz() const { + return ToValue(); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +inline constexpr Frequency operator/(int64_t nominator, + const TimeDelta& interval) { + constexpr int64_t kKiloPerMicro = 1000 * 1000000; + return Frequency::MilliHertz(nominator * kKiloPerMicro / interval.us()); +} + +inline constexpr TimeDelta operator/(int64_t nominator, + const Frequency& frequency) { + constexpr int64_t kMegaPerMilli = 1000000 * 1000; + return TimeDelta::Micros(nominator * kMegaPerMilli / frequency.millihertz()); +} + +inline constexpr double operator*(Frequency frequency, TimeDelta time_delta) { + return frequency.hertz() * time_delta.seconds(); +} +inline constexpr double operator*(TimeDelta time_delta, Frequency frequency) { + return frequency * time_delta; +} + +std::string ToString(Frequency value); + +template +void AbslStringify(Sink& sink, Frequency value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc +#endif // API_UNITS_FREQUENCY_H_ diff --git a/src/common/api/units/time_delta.cc b/src/common/api/units/time_delta.cc new file mode 100644 index 0000000..4ead80b --- /dev/null +++ b/src/common/api/units/time_delta.cc @@ -0,0 +1,32 @@ +/* + * 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 + +namespace webrtc { + +std::string ToString(TimeDelta value) { + if (value.IsPlusInfinity()) { + return "+inf ms"; + } else if (value.IsMinusInfinity()) { + return "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + return std::to_string(value.us()) + " us"; + else if (value.ms() % 1000 != 0) + return std::to_string(value.ms()) + " ms"; + else + return std::to_string(value.seconds()) + " s"; + } +} + +} // namespace webrtc diff --git a/src/common/api/units/time_delta.h b/src/common/api/units/time_delta.h new file mode 100644 index 0000000..f5f36fd --- /dev/null +++ b/src/common/api/units/time_delta.h @@ -0,0 +1,104 @@ +/* + * 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 +#include +#include +#include + +#include "unit_base.h" // IWYU pragma: export + +namespace webrtc { + +// 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 final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr TimeDelta Minutes(T value) { + static_assert(std::is_arithmetic::value, ""); + return Seconds(value * 60); + } + template + static constexpr TimeDelta Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr TimeDelta Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr TimeDelta Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + constexpr TimeDelta() = default; + + template + friend void AbslStringify(Sink& sink, TimeDelta value); + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + template + constexpr T ns() const { + return ToMultiple<1000, T>(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr TimeDelta Abs() const { + return us() < 0 ? TimeDelta::Micros(-us()) : *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = false; +}; + +std::string ToString(TimeDelta value); + +template +void AbslStringify(Sink& sink, TimeDelta value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_TIME_DELTA_H_ diff --git a/src/common/api/units/timestamp.cc b/src/common/api/units/timestamp.cc new file mode 100644 index 0000000..99133b3 --- /dev/null +++ b/src/common/api/units/timestamp.cc @@ -0,0 +1,30 @@ +/* + * 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 + +namespace webrtc { +std::string ToString(Timestamp value) { + if (value.IsPlusInfinity()) { + return "+inf ms"; + } else if (value.IsMinusInfinity()) { + return "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + return std::to_string(value.us()) + " us"; + else if (value.ms() % 1000 != 0) + return std::to_string(value.ms()) + " ms"; + else + return std::to_string(value.seconds()) + " s"; + } +} +} // namespace webrtc diff --git a/src/common/api/units/timestamp.h b/src/common/api/units/timestamp.h new file mode 100644 index 0000000..05d41f2 --- /dev/null +++ b/src/common/api/units/timestamp.h @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include + +#include "time_delta.h" +#include "unit_base.h" // IWYU pragma: export + +namespace webrtc { +// 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 final : public rtc_units_impl::UnitBase { + public: + template + static constexpr Timestamp Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr Timestamp Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Timestamp Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + Timestamp() = delete; + + template + friend void AbslStringify(Sink& sink, Timestamp value); + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr Timestamp operator+(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsPlusInfinity()) { + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsMinusInfinity()) { + return MinusInfinity(); + } + return Timestamp::Micros(us() + delta.us()); + } + constexpr Timestamp operator-(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsMinusInfinity()) { + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsPlusInfinity()) { + return MinusInfinity(); + } + return Timestamp::Micros(us() - delta.us()); + } + constexpr TimeDelta operator-(const Timestamp other) const { + if (IsPlusInfinity() || other.IsMinusInfinity()) { + return TimeDelta::PlusInfinity(); + } else if (IsMinusInfinity() || other.IsPlusInfinity()) { + return TimeDelta::MinusInfinity(); + } + return TimeDelta::Micros(us() - other.us()); + } + constexpr Timestamp& operator-=(const TimeDelta delta) { + *this = *this - delta; + return *this; + } + constexpr Timestamp& operator+=(const TimeDelta delta) { + *this = *this + delta; + return *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using UnitBase::UnitBase; + static constexpr bool one_sided = true; +}; + +std::string ToString(Timestamp value); + +template +void AbslStringify(Sink& sink, Timestamp value) { + sink.Append(ToString(value)); +} + +} // namespace webrtc + +#endif // API_UNITS_TIMESTAMP_H_ diff --git a/src/common/api/units/unit_base.h b/src/common/api/units/unit_base.h new file mode 100644 index 0000000..8f9fe22 --- /dev/null +++ b/src/common/api/units/unit_base.h @@ -0,0 +1,275 @@ +/* + * Copyright 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 RTC_BASE_UNITS_UNIT_BASE_H_ +#define RTC_BASE_UNITS_UNIT_BASE_H_ + +#include + +#include +#include +#include +#include + +#include "rtc_base/numerics/divide_round.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace rtc_units_impl { + +// UnitBase is a base class for implementing custom value types with a specific +// unit. It provides type safety and commonly useful operations. The underlying +// storage is always an int64_t, it's up to the unit implementation to choose +// what scale it represents. +// +// It's used like: +// class MyUnit: public UnitBase {...}; +// +// Unit_T is the subclass representing the specific unit. +template +class UnitBase { + public: + UnitBase() = delete; + static constexpr Unit_T Zero() { return Unit_T(0); } + static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); } + static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); } + + constexpr bool IsZero() const { return value_ == 0; } + constexpr bool IsFinite() const { return !IsInfinite(); } + constexpr bool IsInfinite() const { + return value_ == PlusInfinityVal() || value_ == MinusInfinityVal(); + } + constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); } + constexpr bool IsMinusInfinity() const { + return value_ == MinusInfinityVal(); + } + + constexpr bool operator==(const UnitBase& other) const { + return value_ == other.value_; + } + constexpr bool operator!=(const UnitBase& other) const { + return value_ != other.value_; + } + constexpr bool operator<=(const UnitBase& other) const { + return value_ <= other.value_; + } + constexpr bool operator>=(const UnitBase& other) const { + return value_ >= other.value_; + } + constexpr bool operator>(const UnitBase& other) const { + return value_ > other.value_; + } + constexpr bool operator<(const UnitBase& other) const { + return value_ < other.value_; + } + constexpr Unit_T RoundTo(const Unit_T& resolution) const { + return Unit_T((value_ + resolution.value_ / 2) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundUpTo(const Unit_T& resolution) const { + return Unit_T((value_ + resolution.value_ - 1) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundDownTo(const Unit_T& resolution) const { + return Unit_T(value_ / resolution.value_) * resolution.value_; + } + + protected: + template ::value>::type* = nullptr> + static constexpr Unit_T FromValue(T value) { + return Unit_T(rtc::dchecked_cast(value)); + } + template ::value>::type* = nullptr> + static constexpr Unit_T FromValue(T value) { + if (value == std::numeric_limits::infinity()) { + return PlusInfinity(); + } else if (value == -std::numeric_limits::infinity()) { + return MinusInfinity(); + } else { + return FromValue(rtc::dchecked_cast(value)); + } + } + + template ::value>::type* = nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + return Unit_T(rtc::dchecked_cast(value * denominator)); + } + template ::value>::type* = nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + return FromValue(value * denominator); + } + + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + return rtc::dchecked_cast(value_); + } + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + return IsPlusInfinity() ? std::numeric_limits::infinity() + : IsMinusInfinity() ? -std::numeric_limits::infinity() + : value_; + } + template + constexpr T ToValueOr(T fallback_value) const { + return IsFinite() ? value_ : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + return rtc::dchecked_cast(DivideRoundToNearest(value_, Denominator)); + } + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + return ToValue() * (1 / static_cast(Denominator)); + } + + template + constexpr int64_t ToFractionOr(int64_t fallback_value) const { + return IsFinite() ? DivideRoundToNearest(value_, Denominator) + : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + return rtc::dchecked_cast(ToValue() * Factor); + } + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + return ToValue() * Factor; + } + + explicit constexpr UnitBase(int64_t value) : value_(value) {} + + private: + template + friend class RelativeUnit; + + static inline constexpr int64_t PlusInfinityVal() { + return std::numeric_limits::max(); + } + static inline constexpr int64_t MinusInfinityVal() { + return std::numeric_limits::min(); + } + + constexpr Unit_T& AsSubClassRef() { return static_cast(*this); } + constexpr const Unit_T& AsSubClassRef() const { + return static_cast(*this); + } + + int64_t value_; +}; + +// Extends UnitBase to provide operations for relative units, that is, units +// that have a meaningful relation between values such that a += b is a +// sensible thing to do. For a,b <- same unit. +template +class RelativeUnit : public UnitBase { + public: + constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const { + return std::max(min_value, + std::min(UnitBase::AsSubClassRef(), max_value)); + } + constexpr void Clamp(Unit_T min_value, Unit_T max_value) { + *this = Clamped(min_value, max_value); + } + constexpr Unit_T operator+(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsPlusInfinity()) { + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) { + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() + other.ToValue()); + } + constexpr Unit_T operator-(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsMinusInfinity()) { + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) { + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() - other.ToValue()); + } + constexpr Unit_T& operator+=(const Unit_T other) { + *this = *this + other; + return this->AsSubClassRef(); + } + constexpr Unit_T& operator-=(const Unit_T other) { + *this = *this - other; + return this->AsSubClassRef(); + } + constexpr double operator/(const Unit_T other) const { + return UnitBase::template ToValue() / + other.template ToValue(); + } + template >* = nullptr> + constexpr Unit_T operator/(T scalar) const { + return UnitBase::FromValue(std::llround(this->ToValue() / scalar)); + } + template >* = nullptr> + constexpr Unit_T operator/(T scalar) const { + return UnitBase::FromValue(this->ToValue() / scalar); + } + constexpr Unit_T operator*(double scalar) const { + return UnitBase::FromValue(std::llround(this->ToValue() * scalar)); + } + constexpr Unit_T operator*(int64_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + constexpr Unit_T operator*(int32_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + constexpr Unit_T operator*(size_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + + protected: + using UnitBase::UnitBase; + constexpr RelativeUnit() : UnitBase(0) {} +}; + +template +inline constexpr Unit_T operator*(double scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(size_t scalar, RelativeUnit other) { + return other * scalar; +} + +template +inline constexpr Unit_T operator-(RelativeUnit other) { + if (other.IsPlusInfinity()) return UnitBase::MinusInfinity(); + if (other.IsMinusInfinity()) return UnitBase::PlusInfinity(); + return -1 * other; +} + +} // namespace rtc_units_impl + +} // namespace webrtc + +#endif // RTC_BASE_UNITS_UNIT_BASE_H_ diff --git a/src/common/enc_mark.h b/src/common/enc_mark.h deleted file mode 100644 index ffcd995..0000000 --- a/src/common/enc_mark.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * @Author: DI JUNKUN - * @Date: 2025-01-08 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. - */ - -#ifndef _ENC_MARK_H_ -#define _ENC_MARK_H_ - -// L4S Explicit Congestion Notification (ECN) . -// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable -// Transport and CE stands for Congestion Experienced. - -// RFC-3168, Section 5 -// +-----+-----+ -// | ECN FIELD | -// +-----+-----+ -// ECT CE [Obsolete] RFC 2481 names for the ECN bits. -// 0 0 Not-ECT -// 0 1 ECT(1) -// 1 0 ECT(0) -// 1 1 CE - -enum class EcnMarking { - kNotEct = 0, // Not ECN-Capable Transport - kEct1 = 1, // ECN-Capable Transport - kEct0 = 2, // Not used by L4s (or webrtc.) - kCe = 3, // Congestion experienced -}; - -#endif \ No newline at end of file diff --git a/src/common/module_common_types_public.h b/src/common/module_common_types_public.h new file mode 100644 index 0000000..289b5d0 --- /dev/null +++ b/src/common/module_common_types_public.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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 MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ +#define MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ + +#include +#include + +namespace webrtc { + +template +inline bool IsNewer(U value, U prev_value) { + static_assert(!std::numeric_limits::is_signed, "U must be unsigned"); + // kBreakpoint is the half-way mark for the type U. For instance, for a + // uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000. + constexpr U kBreakpoint = (std::numeric_limits::max() >> 1) + 1; + // Distinguish between elements that are exactly kBreakpoint apart. + // If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true, + // IsNewer(t2,t1)=false + // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. + if (value - prev_value == kBreakpoint) { + return value > prev_value; + } + return value != prev_value && + static_cast(value - prev_value) < kBreakpoint; +} + +// NB: Doesn't fulfill strict weak ordering requirements. +// Mustn't be used as std::map Compare function. +inline bool IsNewerSequenceNumber(uint16_t sequence_number, + uint16_t prev_sequence_number) { + return IsNewer(sequence_number, prev_sequence_number); +} + +// NB: Doesn't fulfill strict weak ordering requirements. +// Mustn't be used as std::map Compare function. +inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) { + return IsNewer(timestamp, prev_timestamp); +} + +inline uint16_t LatestSequenceNumber(uint16_t sequence_number1, + uint16_t sequence_number2) { + return IsNewerSequenceNumber(sequence_number1, sequence_number2) + ? sequence_number1 + : sequence_number2; +} + +inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) { + return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2; +} + +} // namespace webrtc +#endif // MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ diff --git a/src/common/network_types.h b/src/common/network_types.h index d8e3ea4..ba4c082 100644 --- a/src/common/network_types.h +++ b/src/common/network_types.h @@ -1,72 +1,104 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-13 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * 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 _NETWORK_TYPES_H_ -#define _NETWORK_TYPES_H_ +#ifndef API_TRANSPORT_NETWORK_TYPES_H_ +#define API_TRANSPORT_NETWORK_TYPES_H_ +#include -#include -#include +#include +#include #include -#include "enc_mark.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/network/ecn_marking.h" -struct NetworkEstimate { - int64_t at_time = std::numeric_limits::max(); - // Deprecated, use TargetTransferRate::target_rate instead. - int64_t bandwidth = std::numeric_limits::max(); - int64_t round_trip_time = std::numeric_limits::max(); - int64_t bwe_period = std::numeric_limits::max(); +namespace webrtc { - float loss_rate_ratio = 0; +// Configuration + +// Represents constraints and rates related to the currently enabled streams. +// This is used as input to the congestion controller via the StreamsConfig +// struct. +struct BitrateAllocationLimits { + // The total minimum send bitrate required by all sending streams. + DataRate min_allocatable_rate = DataRate::Zero(); + // The total maximum allocatable bitrate for all currently available streams. + DataRate max_allocatable_rate = DataRate::Zero(); + // The max bitrate to use for padding. The sum of the per-stream max padding + // rate. + DataRate max_padding_rate = DataRate::Zero(); }; -struct TargetTransferRate { - int64_t at_time = std::numeric_limits::max(); - // The estimate on which the target rate is based on. - NetworkEstimate network_estimate; - int64_t target_rate = 0; - int64_t stable_target_rate = 0; - double cwnd_reduce_ratio = 0; +// Use StreamsConfig for information about streams that is required for specific +// adjustments to the algorithms in network controllers. Especially useful +// for experiments. +struct StreamsConfig { + StreamsConfig(); + StreamsConfig(const StreamsConfig&); + ~StreamsConfig(); + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional requests_alr_probing; + // If `enable_repeated_initial_probing` is set to true, Probes are sent + // periodically every 1s during the first 5s after the network becomes + // available. The probes ignores max_total_allocated_bitrate. + std::optional enable_repeated_initial_probing; + std::optional pacing_factor; + + // TODO(srte): Use BitrateAllocationLimits here. + std::optional min_total_allocated_bitrate; + std::optional max_padding_rate; + std::optional max_total_allocated_bitrate; }; -struct NetworkControlUpdate { - NetworkControlUpdate() = default; - NetworkControlUpdate(const NetworkControlUpdate&) = default; - ~NetworkControlUpdate() = default; +struct TargetRateConstraints { + TargetRateConstraints(); + TargetRateConstraints(const TargetRateConstraints&); + ~TargetRateConstraints(); + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional min_data_rate; + std::optional max_data_rate; + // The initial bandwidth estimate to base target rate on. This should be used + // as the basis for initial OnTargetTransferRate and OnPacerConfig callbacks. + std::optional starting_rate; +}; - bool has_updates() const { - // return congestion_window.has_value() || pacer_config.has_value() || - // !probe_cluster_configs.empty() || - return target_rate.has_value(); - } +// Send side information - std::optional congestion_window; - // std::optional pacer_config; - // std::vector probe_cluster_configs; - std::optional target_rate; +struct NetworkAvailability { + Timestamp at_time = Timestamp::PlusInfinity(); + bool network_available = false; +}; + +struct NetworkRouteChange { + NetworkRouteChange(); + NetworkRouteChange(const NetworkRouteChange&); + ~NetworkRouteChange(); + Timestamp at_time = Timestamp::PlusInfinity(); + // The TargetRateConstraints are set here so they can be changed synchronously + // when network route changes. + TargetRateConstraints constraints; }; struct PacedPacketInfo { - PacedPacketInfo() = default; + PacedPacketInfo(); PacedPacketInfo(int probe_cluster_id, int probe_cluster_min_probes, - int probe_cluster_min_bytes) - : probe_cluster_id(probe_cluster_id), - probe_cluster_min_probes(probe_cluster_min_probes), - probe_cluster_min_bytes(probe_cluster_min_bytes) {} + int probe_cluster_min_bytes); - bool operator==(const PacedPacketInfo& rhs) const { - return send_bitrate == rhs.send_bitrate && - probe_cluster_id == rhs.probe_cluster_id && - probe_cluster_min_probes == rhs.probe_cluster_min_probes && - probe_cluster_min_bytes == rhs.probe_cluster_min_bytes; - } + bool operator==(const PacedPacketInfo& rhs) const; // TODO(srte): Move probing info to a separate, optional struct. static constexpr int kNotAProbe = -1; - int64_t send_bitrate = 0; + DataRate send_bitrate = DataRate::BitsPerSec(0); int probe_cluster_id = kNotAProbe; int probe_cluster_min_probes = -1; int probe_cluster_min_bytes = -1; @@ -74,11 +106,11 @@ struct PacedPacketInfo { }; struct SentPacket { - int64_t send_time = std::numeric_limits::max(); + Timestamp send_time = Timestamp::PlusInfinity(); // Size of packet with overhead up to IP layer. - int64_t size = 0; + DataSize size = DataSize::Zero(); // Size of preceeding packets that are not part of feedback. - int64_t prior_unacked_data = 0; + DataSize prior_unacked_data = DataSize::Zero(); // Probe cluster id and parameters including bitrate, number of packets and // number of bytes. PacedPacketInfo pacing_info; @@ -89,73 +121,172 @@ struct SentPacket { // each packet. int64_t sequence_number; // Tracked data in flight when the packet was sent, excluding unacked data. - int64_t data_in_flight = 0; + DataSize data_in_flight = DataSize::Zero(); }; +struct ReceivedPacket { + Timestamp send_time = Timestamp::MinusInfinity(); + Timestamp receive_time = Timestamp::PlusInfinity(); + DataSize size = DataSize::Zero(); +}; + +// Transport level feedback + +struct RemoteBitrateReport { + Timestamp receive_time = Timestamp::PlusInfinity(); + DataRate bandwidth = DataRate::Infinity(); +}; + +struct RoundTripTimeUpdate { + Timestamp receive_time = Timestamp::PlusInfinity(); + TimeDelta round_trip_time = TimeDelta::PlusInfinity(); + bool smoothed = false; +}; + +struct TransportLossReport { + Timestamp receive_time = Timestamp::PlusInfinity(); + Timestamp start_time = Timestamp::PlusInfinity(); + Timestamp end_time = Timestamp::PlusInfinity(); + uint64_t packets_lost_delta = 0; + uint64_t packets_received_delta = 0; +}; + +// Packet level feedback + struct PacketResult { class ReceiveTimeOrder { public: bool operator()(const PacketResult& lhs, const PacketResult& rhs); }; - PacketResult() = default; - PacketResult(const PacketResult&) = default; - ~PacketResult() = default; + PacketResult(); + PacketResult(const PacketResult&); + ~PacketResult(); - inline bool IsReceived() const { - return receive_time != std::numeric_limits::max(); - } + inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); } SentPacket sent_packet; - int64_t receive_time = std::numeric_limits::max(); - EcnMarking ecn = EcnMarking::kNotEct; + Timestamp receive_time = Timestamp::PlusInfinity(); + rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct; }; struct TransportPacketsFeedback { - TransportPacketsFeedback() = default; - TransportPacketsFeedback(const TransportPacketsFeedback& other) = default; - ~TransportPacketsFeedback() = default; + TransportPacketsFeedback(); + TransportPacketsFeedback(const TransportPacketsFeedback& other); + ~TransportPacketsFeedback(); - int64_t feedback_time = std::numeric_limits::max(); - int64_t data_in_flight = 0; + Timestamp feedback_time = Timestamp::PlusInfinity(); + DataSize data_in_flight = DataSize::Zero(); bool transport_supports_ecn = false; std::vector packet_feedbacks; // Arrival times for messages without send time information. - std::vector sendless_arrival_times; + std::vector sendless_arrival_times; - std::vector ReceivedWithSendInfo() const { - std::vector res; - for (const PacketResult& fb : packet_feedbacks) { - if (fb.IsReceived()) { - res.push_back(fb); - } - } - return res; - } - std::vector LostWithSendInfo() const { - std::vector res; - for (const PacketResult& fb : packet_feedbacks) { - if (!fb.IsReceived()) { - res.push_back(fb); - } - } - return res; - } - - std::vector PacketsWithFeedback() const { - return packet_feedbacks; - } - std::vector SortedByReceiveTime() const { - std::vector res; - for (const PacketResult& fb : packet_feedbacks) { - if (fb.IsReceived()) { - res.push_back(fb); - } - } - std::sort(res.begin(), res.end(), PacketResult::ReceiveTimeOrder()); - return res; - } + std::vector ReceivedWithSendInfo() const; + std::vector LostWithSendInfo() const; + std::vector PacketsWithFeedback() const; + std::vector SortedByReceiveTime() const; }; -#endif \ No newline at end of file +// Network estimation + +struct NetworkEstimate { + Timestamp at_time = Timestamp::PlusInfinity(); + // Deprecated, use TargetTransferRate::target_rate instead. + DataRate bandwidth = DataRate::Infinity(); + TimeDelta round_trip_time = TimeDelta::PlusInfinity(); + TimeDelta bwe_period = TimeDelta::PlusInfinity(); + + float loss_rate_ratio = 0; +}; + +// Network control + +struct PacerConfig { + Timestamp at_time = Timestamp::PlusInfinity(); + // Pacer should send at most data_window data over time_window duration. + DataSize data_window = DataSize::Infinity(); + TimeDelta time_window = TimeDelta::PlusInfinity(); + // Pacer should send at least pad_window data over time_window duration. + DataSize pad_window = DataSize::Zero(); + DataRate data_rate() const { return data_window / time_window; } + DataRate pad_rate() const { return pad_window / time_window; } +}; + +struct ProbeClusterConfig { + Timestamp at_time = Timestamp::PlusInfinity(); + DataRate target_data_rate = DataRate::Zero(); + // Duration of a probe. + TimeDelta target_duration = TimeDelta::Zero(); + // Delta time between sent bursts of packets during probe. + TimeDelta min_probe_delta = TimeDelta::Millis(2); + int32_t target_probe_count = 0; + int32_t id = 0; +}; + +struct TargetTransferRate { + Timestamp at_time = Timestamp::PlusInfinity(); + // The estimate on which the target rate is based on. + NetworkEstimate network_estimate; + DataRate target_rate = DataRate::Zero(); + DataRate stable_target_rate = DataRate::Zero(); + double cwnd_reduce_ratio = 0; +}; + +// Contains updates of network controller comand state. Using optionals to +// indicate whether a member has been updated. The array of probe clusters +// should be used to send out probes if not empty. +struct NetworkControlUpdate { + NetworkControlUpdate(); + NetworkControlUpdate(const NetworkControlUpdate&); + ~NetworkControlUpdate(); + + bool has_updates() const { + return congestion_window.has_value() || pacer_config.has_value() || + !probe_cluster_configs.empty() || target_rate.has_value(); + } + + std::optional congestion_window; + std::optional pacer_config; + std::vector probe_cluster_configs; + std::optional target_rate; +}; + +// Process control +struct ProcessInterval { + Timestamp at_time = Timestamp::PlusInfinity(); + std::optional pacer_queue; +}; + +// Under development, subject to change without notice. +struct NetworkStateEstimate { + double confidence = NAN; + // The time the estimate was received/calculated. + Timestamp update_time = Timestamp::MinusInfinity(); + Timestamp last_receive_time = Timestamp::MinusInfinity(); + Timestamp last_send_time = Timestamp::MinusInfinity(); + + // Total estimated link capacity. + DataRate link_capacity = DataRate::MinusInfinity(); + // Used as a safe measure of available capacity. + DataRate link_capacity_lower = DataRate::MinusInfinity(); + // Used as limit for increasing bitrate. + DataRate link_capacity_upper = DataRate::MinusInfinity(); + + TimeDelta pre_link_buffer_delay = TimeDelta::MinusInfinity(); + TimeDelta post_link_buffer_delay = TimeDelta::MinusInfinity(); + TimeDelta propagation_delay = TimeDelta::MinusInfinity(); + + // Only for debugging + TimeDelta time_delta = TimeDelta::MinusInfinity(); + Timestamp last_feed_time = Timestamp::MinusInfinity(); + double cross_delay_rate = NAN; + double spike_delay_rate = NAN; + DataRate link_capacity_std_dev = DataRate::MinusInfinity(); + DataRate link_capacity_min = DataRate::MinusInfinity(); + double cross_traffic_ratio = NAN; +}; +} // namespace webrtc + +#endif // API_TRANSPORT_NETWORK_TYPES_H_ diff --git a/src/common/rtc_base/module_common_types_public.h b/src/common/rtc_base/module_common_types_public.h new file mode 100644 index 0000000..289b5d0 --- /dev/null +++ b/src/common/rtc_base/module_common_types_public.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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 MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ +#define MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ + +#include +#include + +namespace webrtc { + +template +inline bool IsNewer(U value, U prev_value) { + static_assert(!std::numeric_limits::is_signed, "U must be unsigned"); + // kBreakpoint is the half-way mark for the type U. For instance, for a + // uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000. + constexpr U kBreakpoint = (std::numeric_limits::max() >> 1) + 1; + // Distinguish between elements that are exactly kBreakpoint apart. + // If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true, + // IsNewer(t2,t1)=false + // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. + if (value - prev_value == kBreakpoint) { + return value > prev_value; + } + return value != prev_value && + static_cast(value - prev_value) < kBreakpoint; +} + +// NB: Doesn't fulfill strict weak ordering requirements. +// Mustn't be used as std::map Compare function. +inline bool IsNewerSequenceNumber(uint16_t sequence_number, + uint16_t prev_sequence_number) { + return IsNewer(sequence_number, prev_sequence_number); +} + +// NB: Doesn't fulfill strict weak ordering requirements. +// Mustn't be used as std::map Compare function. +inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) { + return IsNewer(timestamp, prev_timestamp); +} + +inline uint16_t LatestSequenceNumber(uint16_t sequence_number1, + uint16_t sequence_number2) { + return IsNewerSequenceNumber(sequence_number1, sequence_number2) + ? sequence_number1 + : sequence_number2; +} + +inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) { + return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2; +} + +} // namespace webrtc +#endif // MODULES_INCLUDE_MODULE_COMMON_TYPES_PUBLIC_H_ diff --git a/src/common/rtc_base/network/ecn_marking.h b/src/common/rtc_base/network/ecn_marking.h new file mode 100644 index 0000000..43a4f20 --- /dev/null +++ b/src/common/rtc_base/network/ecn_marking.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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 RTC_BASE_NETWORK_ECN_MARKING_H_ +#define RTC_BASE_NETWORK_ECN_MARKING_H_ + +// // TODO: bugs.webrtc.org/42225697 - delete this file. +#include "ecn_marking.h" + +namespace rtc { + +// TODO: bugs.webrtc.org/42225697 - L4S support is slowly being developed. +// Help is appreciated. + +// L4S Explicit Congestion Notification (ECN) . +// https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable +// Transport and CE stands for Congestion Experienced. + +// RFC-3168, Section 5 +// +-----+-----+ +// | ECN FIELD | +// +-----+-----+ +// ECT CE [Obsolete] RFC 2481 names for the ECN bits. +// 0 0 Not-ECT +// 0 1 ECT(1) +// 1 0 ECT(0) +// 1 1 CE + +enum class EcnMarking { + kNotEct = 0, // Not ECN-Capable Transport + kEct1 = 1, // ECN-Capable Transport + kEct0 = 2, // Not used by L4s (or webrtc.) + kCe = 3, // Congestion experienced +}; + +} // namespace rtc + +#endif // RTC_BASE_NETWORK_ECN_MARKING_H_ diff --git a/src/common/rtc_base/numerics/divide_round.h b/src/common/rtc_base/numerics/divide_round.h new file mode 100644 index 0000000..47c0284 --- /dev/null +++ b/src/common/rtc_base/numerics/divide_round.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ +#define RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ + +#include + +#include "safe_compare.h" + +namespace webrtc { + +template +inline auto constexpr DivideRoundUp(Dividend dividend, Divisor divisor) { + static_assert(std::is_integral(), ""); + static_assert(std::is_integral(), ""); + + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + return quotient + (remainder > 0 ? 1 : 0); +} + +template +inline auto constexpr DivideRoundToNearest(Dividend dividend, Divisor divisor) { + static_assert(std::is_integral(), ""); + static_assert(std::is_integral(), ""); + + if (dividend < Dividend{0}) { + auto half_of_divisor = divisor / 2; + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + if (rtc::SafeGt(-remainder, half_of_divisor)) { + --quotient; + } + return quotient; + } + + auto half_of_divisor = (divisor - 1) / 2; + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + if (rtc::SafeGt(remainder, half_of_divisor)) { + ++quotient; + } + return quotient; +} + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_DIVIDE_ROUND_H_ diff --git a/src/common/rtc_base/numerics/event_based_exponential_moving_average.cc b/src/common/rtc_base/numerics/event_based_exponential_moving_average.cc new file mode 100644 index 0000000..a13c478 --- /dev/null +++ b/src/common/rtc_base/numerics/event_based_exponential_moving_average.cc @@ -0,0 +1,81 @@ +/* + * 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 "event_based_exponential_moving_average.h" + +#include +#include +#include + +namespace { + +// For a normal distributed value, the 95% double sided confidence interval is +// is 1.96 * stddev. +constexpr double ninetyfive_percent_confidence = 1.96; + +} // namespace + +namespace rtc { + +// `half_time` specifies how much weight will be given to old samples, +// a sample gets exponentially less weight so that it's 50% +// after `half_time` time units has passed. +EventBasedExponentialMovingAverage::EventBasedExponentialMovingAverage( + int half_time) { + SetHalfTime(half_time); +} + +void EventBasedExponentialMovingAverage::SetHalfTime(int half_time) { + tau_ = static_cast(half_time) / log(2); + Reset(); +} + +void EventBasedExponentialMovingAverage::Reset() { + value_ = std::nan("uninit"); + sample_variance_ = std::numeric_limits::infinity(); + estimator_variance_ = 1; + last_observation_timestamp_.reset(); +} + +void EventBasedExponentialMovingAverage::AddSample(int64_t now, int sample) { + if (!last_observation_timestamp_.has_value()) { + value_ = sample; + } else { + // TODO(webrtc:11140): This should really be > (e.g not >=) + // but some pesky tests run with simulated clock and let + // samples arrive simultaneously! + // Variance gets computed after second sample. + int64_t age = now - *last_observation_timestamp_; + double e = exp(-age / tau_); + double alpha = e / (1 + e); + double one_minus_alpha = 1 - alpha; + double sample_diff = sample - value_; + value_ = one_minus_alpha * value_ + alpha * sample; + estimator_variance_ = + (one_minus_alpha * one_minus_alpha) * estimator_variance_ + + (alpha * alpha); + if (sample_variance_ == std::numeric_limits::infinity()) { + // First variance. + sample_variance_ = sample_diff * sample_diff; + } else { + double new_variance = one_minus_alpha * sample_variance_ + + alpha * sample_diff * sample_diff; + sample_variance_ = new_variance; + } + } + last_observation_timestamp_ = now; +} + +double EventBasedExponentialMovingAverage::GetConfidenceInterval() const { + return ninetyfive_percent_confidence * + sqrt(sample_variance_ * estimator_variance_); +} + +} // namespace rtc diff --git a/src/common/rtc_base/numerics/event_based_exponential_moving_average.h b/src/common/rtc_base/numerics/event_based_exponential_moving_average.h new file mode 100644 index 0000000..7055a4d --- /dev/null +++ b/src/common/rtc_base/numerics/event_based_exponential_moving_average.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ +#define RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ + +#include +#include +#include +#include + +namespace rtc { + +/** + * This class implements exponential moving average for time series + * estimating both value, variance and variance of estimator based on + * https://en.wikipedia.org/w/index.php?title=Moving_average§ion=9#Application_to_measuring_computer_performance + * with the additions from nisse@ added to + * https://en.wikipedia.org/wiki/Talk:Moving_average. + * + * A sample gets exponentially less weight so that it's 50% + * after `half_time` time units. + */ +class EventBasedExponentialMovingAverage { + public: + // `half_time` specifies how much weight will be given to old samples, + // see example above. + explicit EventBasedExponentialMovingAverage(int half_time); + + void AddSample(int64_t now, int value); + + double GetAverage() const { return value_; } + double GetVariance() const { return sample_variance_; } + + // Compute 95% confidence interval assuming that + // - variance of samples are normal distributed. + // - variance of estimator is normal distributed. + // + // The returned values specifies the distance from the average, + // i.e if X = GetAverage(), m = GetConfidenceInterval() + // then a there is 95% likelihood that the observed variables is inside + // [ X +/- m ]. + double GetConfidenceInterval() const; + + // Reset + void Reset(); + + // Update the half_time. + // NOTE: resets estimate too. + void SetHalfTime(int half_time); + + private: + double tau_; + double value_ = std::nan("uninit"); + double sample_variance_ = std::numeric_limits::infinity(); + // This is the ratio between variance of the estimate and variance of samples. + double estimator_variance_ = 1; + std::optional last_observation_timestamp_; +}; + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_ diff --git a/src/common/rtc_base/numerics/exp_filter.cc b/src/common/rtc_base/numerics/exp_filter.cc new file mode 100644 index 0000000..89cfdbb --- /dev/null +++ b/src/common/rtc_base/numerics/exp_filter.cc @@ -0,0 +1,41 @@ +/* + * 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 "exp_filter.h" + +#include + +namespace rtc { + +const float ExpFilter::kValueUndefined = -1.0f; + +void ExpFilter::Reset(float alpha) { + alpha_ = alpha; + filtered_ = kValueUndefined; +} + +float ExpFilter::Apply(float exp, float sample) { + if (filtered_ == kValueUndefined) { + // Initialize filtered value. + filtered_ = sample; + } else if (exp == 1.0) { + filtered_ = alpha_ * filtered_ + (1 - alpha_) * sample; + } else { + float alpha = std::pow(alpha_, exp); + filtered_ = alpha * filtered_ + (1 - alpha) * sample; + } + if (max_ != kValueUndefined && filtered_ > max_) { + filtered_ = max_; + } + return filtered_; +} + +void ExpFilter::UpdateBase(float alpha) { alpha_ = alpha; } +} // namespace rtc diff --git a/src/common/rtc_base/numerics/exp_filter.h b/src/common/rtc_base/numerics/exp_filter.h new file mode 100644 index 0000000..6bded80 --- /dev/null +++ b/src/common/rtc_base/numerics/exp_filter.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_EXP_FILTER_H_ +#define RTC_BASE_NUMERICS_EXP_FILTER_H_ + +namespace rtc { + +// This class can be used, for example, for smoothing the result of bandwidth +// estimation and packet loss estimation. + +class ExpFilter { + public: + static const float kValueUndefined; + + explicit ExpFilter(float alpha, float max = kValueUndefined) : max_(max) { + Reset(alpha); + } + + // Resets the filter to its initial state, and resets filter factor base to + // the given value `alpha`. + void Reset(float alpha); + + // Applies the filter with a given exponent on the provided sample: + // y(k) = min(alpha_^ exp * y(k-1) + (1 - alpha_^ exp) * sample, max_). + float Apply(float exp, float sample); + + // Returns current filtered value. + float filtered() const { return filtered_; } + + // Changes the filter factor base to the given value `alpha`. + void UpdateBase(float alpha); + + private: + float alpha_; // Filter factor base. + float filtered_; // Current filter output. + const float max_; +}; +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_EXP_FILTER_H_ diff --git a/src/common/rtc_base/numerics/histogram_percentile_counter.cc b/src/common/rtc_base/numerics/histogram_percentile_counter.cc new file mode 100644 index 0000000..03e1902 --- /dev/null +++ b/src/common/rtc_base/numerics/histogram_percentile_counter.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 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 "rtc_base/numerics/histogram_percentile_counter.h" + +#include +#include +#include +#include +#include + +namespace rtc { +HistogramPercentileCounter::HistogramPercentileCounter( + uint32_t long_tail_boundary) + : histogram_low_(size_t{long_tail_boundary}), + long_tail_boundary_(long_tail_boundary), + total_elements_(0), + total_elements_low_(0) {} + +HistogramPercentileCounter::~HistogramPercentileCounter() = default; + +void HistogramPercentileCounter::Add(const HistogramPercentileCounter& other) { + for (uint32_t value = 0; value < other.long_tail_boundary_; ++value) { + Add(value, other.histogram_low_[value]); + } + for (const auto& it : histogram_high_) { + Add(it.first, it.second); + } +} + +void HistogramPercentileCounter::Add(uint32_t value, size_t count) { + if (value < long_tail_boundary_) { + histogram_low_[value] += count; + total_elements_low_ += count; + } else { + histogram_high_[value] += count; + } + total_elements_ += count; +} + +void HistogramPercentileCounter::Add(uint32_t value) { Add(value, 1); } + +std::optional HistogramPercentileCounter::GetPercentile( + float fraction) { + if (total_elements_ == 0) return std::nullopt; + size_t elements_to_skip = static_cast( + std::max(0.0f, std::ceil(total_elements_ * fraction) - 1)); + if (elements_to_skip >= total_elements_) + elements_to_skip = total_elements_ - 1; + if (elements_to_skip < total_elements_low_) { + for (uint32_t value = 0; value < long_tail_boundary_; ++value) { + if (elements_to_skip < histogram_low_[value]) return value; + elements_to_skip -= histogram_low_[value]; + } + } else { + elements_to_skip -= total_elements_low_; + for (const auto& it : histogram_high_) { + if (elements_to_skip < it.second) return it.first; + elements_to_skip -= it.second; + } + } + return std::nullopt; +} + +} // namespace rtc diff --git a/src/common/rtc_base/numerics/histogram_percentile_counter.h b/src/common/rtc_base/numerics/histogram_percentile_counter.h new file mode 100644 index 0000000..327ce0d --- /dev/null +++ b/src/common/rtc_base/numerics/histogram_percentile_counter.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 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 RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ +#define RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ + +#include +#include + +#include +#include +#include + +namespace rtc { +// Calculates percentiles on the stream of data. Use `Add` methods to add new +// values. Use `GetPercentile` to get percentile of the currently added values. +class HistogramPercentileCounter { + public: + // Values below `long_tail_boundary` are stored as the histogram in an array. + // Values above - in a map. + explicit HistogramPercentileCounter(uint32_t long_tail_boundary); + ~HistogramPercentileCounter(); + void Add(uint32_t value); + void Add(uint32_t value, size_t count); + void Add(const HistogramPercentileCounter& other); + // Argument should be from 0 to 1. + std::optional GetPercentile(float fraction); + + private: + std::vector histogram_low_; + std::map histogram_high_; + const uint32_t long_tail_boundary_; + size_t total_elements_; + size_t total_elements_low_; +}; +} // namespace rtc +#endif // RTC_BASE_NUMERICS_HISTOGRAM_PERCENTILE_COUNTER_H_ diff --git a/src/common/rtc_base/numerics/math_utils.h b/src/common/rtc_base/numerics/math_utils.h new file mode 100644 index 0000000..38b1fab --- /dev/null +++ b/src/common/rtc_base/numerics/math_utils.h @@ -0,0 +1,70 @@ +/* + * Copyright 2005 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_NUMERICS_MATH_UTILS_H_ +#define API_NUMERICS_MATH_UTILS_H_ + +#include +#include + +namespace webrtc { +namespace webrtc_impl { +// Given two numbers `x` and `y` such that x >= y, computes the difference +// x - y without causing undefined behavior due to signed overflow. +template +typename std::make_unsigned::type unsigned_difference(T x, T y) { + static_assert( + std::is_signed::value, + "Function unsigned_difference is only meaningful for signed types."); + typedef typename std::make_unsigned::type unsigned_type; + // int -> unsigned conversion repeatedly adds UINT_MAX + 1 until the number + // can be represented as an unsigned. Since we know that the actual + // difference x - y can be represented as an unsigned, it is sufficient to + // compute the difference modulo UINT_MAX + 1, i.e using unsigned arithmetic. + return static_cast(x) - static_cast(y); +} + +// Provide neutral element with respect to min(). +// Typically used as an initial value for running minimum. +template ::has_infinity>::type* = + nullptr> +constexpr T infinity_or_max() { + return std::numeric_limits::infinity(); +} + +template ::has_infinity>::type* = nullptr> +constexpr T infinity_or_max() { + // Fallback to max(). + return std::numeric_limits::max(); +} + +// Provide neutral element with respect to max(). +// Typically used as an initial value for running maximum. +template ::has_infinity>::type* = + nullptr> +constexpr T minus_infinity_or_min() { + static_assert(std::is_signed::value, "Unsupported. Please open a bug."); + return -std::numeric_limits::infinity(); +} + +template ::has_infinity>::type* = nullptr> +constexpr T minus_infinity_or_min() { + // Fallback to min(). + return std::numeric_limits::min(); +} + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // API_NUMERICS_MATH_UTILS_H_ diff --git a/src/common/rtc_base/numerics/mod_ops.h b/src/common/rtc_base/numerics/mod_ops.h new file mode 100644 index 0000000..c572134 --- /dev/null +++ b/src/common/rtc_base/numerics/mod_ops.h @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_MOD_OPS_H_ +#define RTC_BASE_NUMERICS_MOD_OPS_H_ + +#include +#include + +namespace webrtc { + +template // NOLINT +inline unsigned long Add(unsigned long a, unsigned long b) { // NOLINT + unsigned long t = M - b % M; // NOLINT + unsigned long res = a - t; // NOLINT + if (t > a) return res + M; + return res; +} + +template // NOLINT +inline unsigned long Subtract(unsigned long a, unsigned long b) { // NOLINT + unsigned long sub = b % M; // NOLINT + if (a < sub) return M - (sub - a); + return a - sub; +} + +// Calculates the forward difference between two wrapping numbers. +// +// Example: +// uint8_t x = 253; +// uint8_t y = 2; +// +// ForwardDiff(x, y) == 5 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// |----->----->----->----->-----> +// +// ForwardDiff(y, x) == 251 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// -->-----> |----->--- +// +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. +template +inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a <= b ? b - a : M - (a - b); +} + +template +inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return b - a; +} + +template +inline T ForwardDiff(T a, T b) { + return ForwardDiff(a, b); +} + +// Calculates the reverse difference between two wrapping numbers. +// +// Example: +// uint8_t x = 253; +// uint8_t y = 2; +// +// ReverseDiff(y, x) == 5 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// <-----<-----<-----<-----<-----| +// +// ReverseDiff(x, y) == 251 +// +// 252 253 254 255 0 1 2 3 +// ################################################# +// | | x | | | | | y | | +// ################################################# +// ---<-----| |<-----<-- +// +// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the +// largest value representable by T. +template +inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return b <= a ? a - b : M - (b - a); +} + +template +inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return a - b; +} + +template +inline T ReverseDiff(T a, T b) { + return ReverseDiff(a, b); +} + +// Calculates the minimum distance between to wrapping numbers. +// +// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b)) +template +inline T MinDiff(T a, T b) { + static_assert(std::is_unsigned::value, + "Type must be an unsigned integer."); + return std::min(ForwardDiff(a, b), ReverseDiff(a, b)); +} + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_MOD_OPS_H_ diff --git a/src/common/rtc_base/numerics/moving_max_counter.h b/src/common/rtc_base/numerics/moving_max_counter.h new file mode 100644 index 0000000..c1a350e --- /dev/null +++ b/src/common/rtc_base/numerics/moving_max_counter.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 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 RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ +#define RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ + +#include + +#include +#include +#include +#include + +namespace rtc { + +// Implements moving max: can add samples to it and calculate maximum over some +// fixed moving window. +// +// Window size is configured at constructor. +// Samples can be added with `Add()` and max over current window is returned by +// `MovingMax`. `current_time_ms` in successive calls to Add and MovingMax +// should never decrease as if it's a wallclock time. +template +class MovingMaxCounter { + public: + explicit MovingMaxCounter(int64_t window_length_ms); + + MovingMaxCounter(const MovingMaxCounter&) = delete; + MovingMaxCounter& operator=(const MovingMaxCounter&) = delete; + + // Advances the current time, and adds a new sample. The new current time must + // be at least as large as the old current time. + void Add(const T& sample, int64_t current_time_ms); + // Advances the current time, and returns the maximum sample in the time + // window ending at the current time. The new current time must be at least as + // large as the old current time. + std::optional Max(int64_t current_time_ms); + void Reset(); + + private: + // Throws out obsolete samples. + void RollWindow(int64_t new_time_ms); + const int64_t window_length_ms_; + // This deque stores (timestamp, sample) pairs in chronological order; new + // pairs are only ever added at the end. However, because they can't affect + // the Max() calculation, pairs older than window_length_ms_ are discarded, + // and if an older pair has a sample that's smaller than that of a younger + // pair, the older pair is discarded. As a result, the sequence of timestamps + // is strictly increasing, and the sequence of samples is strictly decreasing. + std::deque> samples_; +#if RTC_DCHECK_IS_ON + int64_t last_call_time_ms_ = std::numeric_limits::min(); +#endif +}; + +template +MovingMaxCounter::MovingMaxCounter(int64_t window_length_ms) + : window_length_ms_(window_length_ms) {} + +template +void MovingMaxCounter::Add(const T& sample, int64_t current_time_ms) { + RollWindow(current_time_ms); + // Remove samples that will never be maximum in any window: newly added sample + // will always be in all windows the previous samples are. Thus, smaller or + // equal samples could be removed. This will maintain the invariant - deque + // contains strictly decreasing sequence of values. + while (!samples_.empty() && samples_.back().second <= sample) { + samples_.pop_back(); + } + // Add the new sample but only if there's no existing sample at the same time. + // Due to checks above, the already existing element will be larger, so the + // new sample will never be the maximum in any window. + if (samples_.empty() || samples_.back().first < current_time_ms) { + samples_.emplace_back(std::make_pair(current_time_ms, sample)); + } +} + +template +std::optional MovingMaxCounter::Max(int64_t current_time_ms) { + RollWindow(current_time_ms); + std::optional res; + if (!samples_.empty()) { + res.emplace(samples_.front().second); + } + return res; +} + +template +void MovingMaxCounter::Reset() { + samples_.clear(); +} + +template +void MovingMaxCounter::RollWindow(int64_t new_time_ms) { +#if RTC_DCHECK_IS_ON + last_call_time_ms_ = new_time_ms; +#endif + const int64_t window_begin_ms = new_time_ms - window_length_ms_; + auto it = samples_.begin(); + while (it != samples_.end() && it->first < window_begin_ms) { + ++it; + } + samples_.erase(samples_.begin(), it); +} + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_MOVING_MAX_COUNTER_H_ diff --git a/src/common/rtc_base/numerics/moving_percentile_filter.h b/src/common/rtc_base/numerics/moving_percentile_filter.h new file mode 100644 index 0000000..d54e2dd --- /dev/null +++ b/src/common/rtc_base/numerics/moving_percentile_filter.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 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 RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ +#define RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ + +#include + +#include +#include + +#include "percentile_filter.h" + +namespace webrtc { + +// Class to efficiently get moving percentile filter from a stream of samples. +template +class MovingPercentileFilter { + public: + // Construct filter. `percentile` defines what percentile to track and + // `window_size` is how many latest samples are stored for finding the + // percentile. `percentile` must be between 0.0 and 1.0 (inclusive) and + // `window_size` must be greater than 0. + MovingPercentileFilter(float percentile, size_t window_size); + + MovingPercentileFilter(const MovingPercentileFilter&) = delete; + MovingPercentileFilter& operator=(const MovingPercentileFilter&) = delete; + + // Insert a new sample. + void Insert(const T& value); + + // Removes all samples; + void Reset(); + + // Get percentile over the latest window. + T GetFilteredValue() const; + + // The number of samples that are currently stored. + size_t GetNumberOfSamplesStored() const; + + private: + PercentileFilter percentile_filter_; + std::list samples_; + size_t samples_stored_; + const size_t window_size_; +}; + +// Convenience type for the common median case. +template +class MovingMedianFilter : public MovingPercentileFilter { + public: + explicit MovingMedianFilter(size_t window_size) + : MovingPercentileFilter(0.5f, window_size) {} +}; + +template +MovingPercentileFilter::MovingPercentileFilter(float percentile, + size_t window_size) + : percentile_filter_(percentile), + samples_stored_(0), + window_size_(window_size) {} + +template +void MovingPercentileFilter::Insert(const T& value) { + percentile_filter_.Insert(value); + samples_.emplace_back(value); + ++samples_stored_; + if (samples_stored_ > window_size_) { + percentile_filter_.Erase(samples_.front()); + samples_.pop_front(); + --samples_stored_; + } +} + +template +T MovingPercentileFilter::GetFilteredValue() const { + return percentile_filter_.GetPercentileValue(); +} + +template +void MovingPercentileFilter::Reset() { + percentile_filter_.Reset(); + samples_.clear(); + samples_stored_ = 0; +} + +template +size_t MovingPercentileFilter::GetNumberOfSamplesStored() const { + return samples_stored_; +} + +} // namespace webrtc +#endif // RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_ diff --git a/src/common/rtc_base/numerics/percentile_filter.h b/src/common/rtc_base/numerics/percentile_filter.h new file mode 100644 index 0000000..0859b3d --- /dev/null +++ b/src/common/rtc_base/numerics/percentile_filter.h @@ -0,0 +1,116 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ +#define RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ + +#include + +#include +#include + +namespace webrtc { + +// Class to efficiently get the percentile value from a group of observations. +// The percentile is the value below which a given percentage of the +// observations fall. +template +class PercentileFilter { + public: + // Construct filter. `percentile` should be between 0 and 1. + explicit PercentileFilter(float percentile); + + // Insert one observation. The complexity of this operation is logarithmic in + // the size of the container. + void Insert(const T& value); + + // Remove one observation or return false if `value` doesn't exist in the + // container. The complexity of this operation is logarithmic in the size of + // the container. + bool Erase(const T& value); + + // Get the percentile value. The complexity of this operation is constant. + T GetPercentileValue() const; + + // Removes all the stored observations. + void Reset(); + + private: + // Update iterator and index to point at target percentile value. + void UpdatePercentileIterator(); + + const float percentile_; + std::multiset set_; + // Maintain iterator and index of current target percentile value. + typename std::multiset::iterator percentile_it_; + int64_t percentile_index_; +}; + +template +PercentileFilter::PercentileFilter(float percentile) + : percentile_(percentile), + percentile_it_(set_.begin()), + percentile_index_(0) {} + +template +void PercentileFilter::Insert(const T& value) { + // Insert element at the upper bound. + set_.insert(value); + if (set_.size() == 1u) { + // First element inserted - initialize percentile iterator and index. + percentile_it_ = set_.begin(); + percentile_index_ = 0; + } else if (value < *percentile_it_) { + // If new element is before us, increment `percentile_index_`. + ++percentile_index_; + } + UpdatePercentileIterator(); +} + +template +bool PercentileFilter::Erase(const T& value) { + typename std::multiset::const_iterator it = set_.lower_bound(value); + // Ignore erase operation if the element is not present in the current set. + if (it == set_.end() || *it != value) return false; + if (it == percentile_it_) { + // If same iterator, update to the following element. Index is not + // affected. + percentile_it_ = set_.erase(it); + } else { + set_.erase(it); + // If erased element was before us, decrement `percentile_index_`. + if (value <= *percentile_it_) --percentile_index_; + } + UpdatePercentileIterator(); + return true; +} + +template +void PercentileFilter::UpdatePercentileIterator() { + if (set_.empty()) return; + const int64_t index = static_cast(percentile_ * (set_.size() - 1)); + std::advance(percentile_it_, index - percentile_index_); + percentile_index_ = index; +} + +template +T PercentileFilter::GetPercentileValue() const { + return set_.empty() ? T() : *percentile_it_; +} + +template +void PercentileFilter::Reset() { + set_.clear(); + percentile_it_ = set_.begin(); + percentile_index_ = 0; +} +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_ diff --git a/src/common/rtc_base/numerics/rational.h b/src/common/rtc_base/numerics/rational.h new file mode 100644 index 0000000..32f0cb1 --- /dev/null +++ b/src/common/rtc_base/numerics/rational.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 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 RTC_BASE_NUMERICS_RATIONAL_H_ +#define RTC_BASE_NUMERICS_RATIONAL_H_ + +namespace webrtc { + +// This is the worst implementation of a rational... +struct Rational { + int numerator; + int denominator; + + bool operator==(const Rational& other) const { + return numerator == other.numerator && denominator == other.denominator; + } +}; + +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_RATIONAL_H_ diff --git a/src/common/rtc_base/numerics/running_statistics.h b/src/common/rtc_base/numerics/running_statistics.h new file mode 100644 index 0000000..4099b47 --- /dev/null +++ b/src/common/rtc_base/numerics/running_statistics.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 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. + */ + +#ifndef API_NUMERICS_RUNNING_STATISTICS_H_ +#define API_NUMERICS_RUNNING_STATISTICS_H_ + +#include +#include +#include +#include + +#include "rtc_base/numerics/math_utils.h" + +namespace webrtc { +namespace webrtc_impl { + +// tl;dr: Robust and efficient online computation of statistics, +// using Welford's method for variance. [1] +// +// This should be your go-to class if you ever need to compute +// min, max, mean, variance and standard deviation. +// If you need to get percentiles, please use webrtc::SamplesStatsCounter. +// +// Please note RemoveSample() won't affect min and max. +// If you want a full-fledged moving window over N last samples, +// please use webrtc::RollingAccumulator. +// +// The measures return std::nullopt if no samples were fed (Size() == 0), +// otherwise the returned optional is guaranteed to contain a value. +// +// [1] +// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm + +// The type T is a scalar which must be convertible to double. +// Rationale: we often need greater precision for measures +// than for the samples themselves. +template +class RunningStatistics { + public: + // Update stats //////////////////////////////////////////// + + // Add a value participating in the statistics in O(1) time. + void AddSample(T sample) { + max_ = std::max(max_, sample); + min_ = std::min(min_, sample); + sum_ += sample; + ++size_; + // Welford's incremental update. + const double delta = sample - mean_; + mean_ += delta / size_; + const double delta2 = sample - mean_; + cumul_ += delta * delta2; + } + + // Remove a previously added value in O(1) time. + // Nb: This doesn't affect min or max. + // Calling RemoveSample when Size()==0 is incorrect. + void RemoveSample(T sample) { + // In production, just saturate at 0. + if (Size() == 0) { + return; + } + // Since samples order doesn't matter, this is the + // exact reciprocal of Welford's incremental update. + --size_; + const double delta = sample - mean_; + mean_ -= delta / size_; + const double delta2 = sample - mean_; + cumul_ -= delta * delta2; + } + + // Merge other stats, as if samples were added one by one, but in O(1). + void MergeStatistics(const RunningStatistics& other) { + if (other.size_ == 0) { + return; + } + max_ = std::max(max_, other.max_); + min_ = std::min(min_, other.min_); + const int64_t new_size = size_ + other.size_; + const double new_mean = + (mean_ * size_ + other.mean_ * other.size_) / new_size; + // Each cumulant must be corrected. + // * from: sum((x_i - mean_)²) + // * to: sum((x_i - new_mean)²) + auto delta = [new_mean](const RunningStatistics& stats) { + return stats.size_ * (new_mean * (new_mean - 2 * stats.mean_) + + stats.mean_ * stats.mean_); + }; + cumul_ = cumul_ + delta(*this) + other.cumul_ + delta(other); + mean_ = new_mean; + size_ = new_size; + } + + // Get Measures //////////////////////////////////////////// + + // Returns number of samples involved via AddSample() or MergeStatistics(), + // minus number of times RemoveSample() was called. + int64_t Size() const { return size_; } + + // Returns minimum among all seen samples, in O(1) time. + // This isn't affected by RemoveSample(). + std::optional GetMin() const { + if (size_ == 0) { + return std::nullopt; + } + return min_; + } + + // Returns maximum among all seen samples, in O(1) time. + // This isn't affected by RemoveSample(). + std::optional GetMax() const { + if (size_ == 0) { + return std::nullopt; + } + return max_; + } + + // Returns sum in O(1) time. + std::optional GetSum() const { + if (size_ == 0) { + return std::nullopt; + } + return sum_; + } + + // Returns mean in O(1) time. + std::optional GetMean() const { + if (size_ == 0) { + return std::nullopt; + } + return mean_; + } + + // Returns unbiased sample variance in O(1) time. + std::optional GetVariance() const { + if (size_ == 0) { + return std::nullopt; + } + return cumul_ / size_; + } + + // Returns unbiased standard deviation in O(1) time. + std::optional GetStandardDeviation() const { + if (size_ == 0) { + return std::nullopt; + } + return std::sqrt(*GetVariance()); + } + + private: + int64_t size_ = 0; // Samples seen. + T min_ = infinity_or_max(); + T max_ = minus_infinity_or_min(); + double mean_ = 0; + double cumul_ = 0; // Variance * size_, sometimes noted m2. + double sum_ = 0; +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // API_NUMERICS_RUNNING_STATISTICS_H_ diff --git a/src/common/rtc_base/numerics/safe_compare.h b/src/common/rtc_base/numerics/safe_compare.h new file mode 100644 index 0000000..192f7b7 --- /dev/null +++ b/src/common/rtc_base/numerics/safe_compare.h @@ -0,0 +1,165 @@ +/* + * Copyright 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. + */ + +// This file defines six constexpr functions: +// +// rtc::SafeEq // == +// rtc::SafeNe // != +// rtc::SafeLt // < +// rtc::SafeLe // <= +// rtc::SafeGt // > +// rtc::SafeGe // >= +// +// They each accept two arguments of arbitrary types, and in almost all cases, +// they simply call the appropriate comparison operator. However, if both +// arguments are integers, they don't compare them using C++'s quirky rules, +// but instead adhere to the true mathematical definitions. It is as if the +// arguments were first converted to infinite-range signed integers, and then +// compared, although of course nothing expensive like that actually takes +// place. In practice, for signed/signed and unsigned/unsigned comparisons and +// some mixed-signed comparisons with a compile-time constant, the overhead is +// zero; in the remaining cases, it is just a few machine instructions (no +// branches). + +#ifndef RTC_BASE_NUMERICS_SAFE_COMPARE_H_ +#define RTC_BASE_NUMERICS_SAFE_COMPARE_H_ + +#include +#include + +#include + +#include "rtc_base/type_traits.h" + +namespace rtc { + +namespace safe_cmp_impl { + +template +struct LargerIntImpl : std::false_type {}; +template <> +struct LargerIntImpl : std::true_type { + using type = int16_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int32_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int64_t; +}; + +// LargerInt::value is true iff there's a signed type that's larger +// than T1 (and no larger than the larger of T2 and int*, for performance +// reasons); and if there is such a type, LargerInt::type is an alias +// for it. +template +struct LargerInt + : LargerIntImpl {}; + +template +constexpr typename std::make_unsigned::type MakeUnsigned(T a) { + return static_cast::type>(a); +} + +// Overload for when both T1 and T2 have the same signedness. +template ::value == + std::is_signed::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, b); +} + +// Overload for signed - unsigned comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, static_cast::type>(b)); +} + +// Overload for unsigned - signed comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(static_cast::type>(a), b); +} + +// Overload for signed - unsigned comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); +} + +// Overload for unsigned - signed comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); +} + +#define RTC_SAFECMP_MAKE_OP(name, op) \ + struct name { \ + template \ + static constexpr bool Op(T1 a, T2 b) { \ + return a op b; \ + } \ + }; +RTC_SAFECMP_MAKE_OP(EqOp, ==) +RTC_SAFECMP_MAKE_OP(NeOp, !=) +RTC_SAFECMP_MAKE_OP(LtOp, <) +RTC_SAFECMP_MAKE_OP(LeOp, <=) +RTC_SAFECMP_MAKE_OP(GtOp, >) +RTC_SAFECMP_MAKE_OP(GeOp, >=) +#undef RTC_SAFECMP_MAKE_OP + +} // namespace safe_cmp_impl + +#define RTC_SAFECMP_MAKE_FUN(name) \ + template \ + constexpr \ + typename std::enable_if::value && IsIntlike::value, \ + bool>::type Safe##name(T1 a, T2 b) { \ + /* Unary plus here turns enums into real integral types. */ \ + return safe_cmp_impl::Cmp(+a, +b); \ + } \ + template \ + constexpr \ + typename std::enable_if::value || !IsIntlike::value, \ + bool>::type Safe##name(const T1& a, \ + const T2& b) { \ + return safe_cmp_impl::name##Op::Op(a, b); \ + } +RTC_SAFECMP_MAKE_FUN(Eq) +RTC_SAFECMP_MAKE_FUN(Ne) +RTC_SAFECMP_MAKE_FUN(Lt) +RTC_SAFECMP_MAKE_FUN(Le) +RTC_SAFECMP_MAKE_FUN(Gt) +RTC_SAFECMP_MAKE_FUN(Ge) +#undef RTC_SAFECMP_MAKE_FUN + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_COMPARE_H_ diff --git a/src/common/rtc_base/numerics/safe_conversions.h b/src/common/rtc_base/numerics/safe_conversions.h new file mode 100644 index 0000000..0326612 --- /dev/null +++ b/src/common/rtc_base/numerics/safe_conversions.h @@ -0,0 +1,70 @@ +/* + * Copyright 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. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions.h. + +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ + +#include + +#include "rtc_base/numerics/safe_conversions_impl.h" + +namespace rtc { + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template +inline constexpr bool IsValueInRangeForNumericType(Src value) { + return internal::RangeCheck(value) == internal::TYPE_VALID; +} + +// checked_cast<> and dchecked_cast<> are analogous to static_cast<> for +// numeric types, except that they [D]CHECK that the specified numeric +// conversion will not overflow or underflow. NaN source will always trigger +// the [D]CHECK. +template +inline constexpr Dst checked_cast(Src value) { + return static_cast(value); +} +template +inline constexpr Dst dchecked_cast(Src value) { + return static_cast(value); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will trigger a RTC_CHECK condition. +template +inline constexpr Dst saturated_cast(Src value) { + // Optimization for floating point values, which already saturate. + if (std::numeric_limits::is_iec559) return static_cast(value); + + switch (internal::RangeCheck(value)) { + case internal::TYPE_VALID: + return static_cast(value); + + case internal::TYPE_UNDERFLOW: + return std::numeric_limits::min(); + + case internal::TYPE_OVERFLOW: + return std::numeric_limits::max(); + + // Should fail only on attempting to assign NaN to a saturated integer. + case internal::TYPE_INVALID: + return static_cast(value); + } + + return static_cast(value); +} + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/src/common/rtc_base/numerics/safe_conversions_impl.h b/src/common/rtc_base/numerics/safe_conversions_impl.h new file mode 100644 index 0000000..fe0f0d7 --- /dev/null +++ b/src/common/rtc_base/numerics/safe_conversions_impl.h @@ -0,0 +1,179 @@ +/* + * Copyright 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. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. + +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ + +#include + +namespace rtc { +namespace internal { + +enum DstSign { DST_UNSIGNED, DST_SIGNED }; + +enum SrcSign { SRC_UNSIGNED, SRC_SIGNED }; + +enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE }; + +// Helper templates to statically determine if our destination type can contain +// all values represented by the source type. + +template ::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED> +struct StaticRangeCheck {}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = + SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1); + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = + sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = sizeof(Src) * 8; + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template +struct StaticRangeCheck { + static const DstRange value = OVERLAPS_RANGE; +}; + +enum RangeCheckResult { + TYPE_VALID = 0, // Value can be represented by the destination type. + TYPE_UNDERFLOW = 1, // Value would overflow. + TYPE_OVERFLOW = 2, // Value would underflow. + TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). +}; + +// This macro creates a RangeCheckResult from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +#define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ + RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ + ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) + +template ::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED, + DstRange IsSrcRangeContained = StaticRangeCheck::value> +struct RangeCheckImpl {}; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range always contains the result: nothing to check. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src /* value */) { + return TYPE_VALID; + } +}; + +// Signed to signed narrowing. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return DstLimits::is_iec559 + ? BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::max() * -1)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::min())); + } +}; + +// Unsigned to unsigned narrowing. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Unsigned to signed. +template +struct RangeCheckImpl { + static constexpr RangeCheckResult Check(Src value) { + typedef std::numeric_limits DstLimits; + return sizeof(Dst) > sizeof(Src) + ? TYPE_VALID + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); + } +}; + +// Signed to unsigned. +template +struct RangeCheckImpl { + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static constexpr size_t DstMaxExponent() { return sizeof(Dst) * 8; } + static constexpr size_t SrcMaxExponent() { + return SrcLimits::is_iec559 ? SrcLimits::max_exponent + : (sizeof(Src) * 8 - 1); + } + static constexpr RangeCheckResult Check(Src value) { + return (DstMaxExponent() >= SrcMaxExponent()) + ? BASE_NUMERIC_RANGE_CHECK_RESULT(true, + value >= static_cast(0)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(0)); + } +}; + +template +inline constexpr RangeCheckResult RangeCheck(Src value) { + static_assert(std::numeric_limits::is_specialized, + "argument must be numeric"); + static_assert(std::numeric_limits::is_specialized, + "result must be numeric"); + return RangeCheckImpl::Check(value); +} + +} // namespace internal +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/src/common/rtc_base/numerics/safe_minmax.h b/src/common/rtc_base/numerics/safe_minmax.h new file mode 100644 index 0000000..99697e0 --- /dev/null +++ b/src/common/rtc_base/numerics/safe_minmax.h @@ -0,0 +1,318 @@ +/* + * Copyright 2017 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. + */ + +// Minimum and maximum +// =================== +// +// rtc::SafeMin(x, y) +// rtc::SafeMax(x, y) +// +// (These are both constexpr.) +// +// Accept two arguments of either any two integral or any two floating-point +// types, and return the smaller and larger value, respectively, with no +// truncation or wrap-around. If only one of the input types is statically +// guaranteed to be able to represent the result, the return type is that type; +// if either one would do, the result type is the smaller type. (One of these +// two cases always applies.) +// +// * The case with one floating-point and one integral type is not allowed, +// because the floating-point type will have greater range, but may not +// have sufficient precision to represent the integer value exactly.) +// +// Clamp (a.k.a. constrain to a given interval) +// ============================================ +// +// rtc::SafeClamp(x, a, b) +// +// Accepts three arguments of any mix of integral types or any mix of +// floating-point types, and returns the value in the closed interval [a, b] +// that is closest to x (that is, if x < a it returns a; if x > b it returns b; +// and if a <= x <= b it returns x). As for SafeMin() and SafeMax(), there is +// no truncation or wrap-around. The result type +// +// 1. is statically guaranteed to be able to represent the result; +// +// 2. is no larger than the largest of the three argument types; and +// +// 3. has the same signedness as the type of the first argument, if this is +// possible without violating the First or Second Law. +// +// There is always at least one type that meets criteria 1 and 2. If more than +// one type meets these criteria equally well, the result type is one of the +// types that is smallest. Note that unlike SafeMin() and SafeMax(), +// SafeClamp() will sometimes pick a return type that isn't the type of any of +// its arguments. +// +// * In this context, a type A is smaller than a type B if it has a smaller +// range; that is, if A::max() - A::min() < B::max() - B::min(). For +// example, int8_t < int16_t == uint16_t < int32_t, and all integral types +// are smaller than all floating-point types.) +// +// * As for SafeMin and SafeMax, mixing integer and floating-point arguments +// is not allowed, because floating-point types have greater range than +// integer types, but do not have sufficient precision to represent the +// values of most integer types exactly. +// +// Requesting a specific return type +// ================================= +// +// All three functions allow callers to explicitly specify the return type as a +// template parameter, overriding the default return type. E.g. +// +// rtc::SafeMin(x, y) // returns an int +// +// If the requested type is statically guaranteed to be able to represent the +// result, then everything's fine, and the return type is as requested. But if +// the requested type is too small, a static_assert is triggered. + +#ifndef RTC_BASE_NUMERICS_SAFE_MINMAX_H_ +#define RTC_BASE_NUMERICS_SAFE_MINMAX_H_ + +#include +#include +#include + +#include "rtc_base/type_traits.h" +#include "safe_compare.h" + +namespace rtc { + +namespace safe_minmax_impl { + +// Make the range of a type available via something other than a constexpr +// function, to work around MSVC limitations. See +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +template +struct Limits { + static constexpr T lowest = std::numeric_limits::lowest(); + static constexpr T max = std::numeric_limits::max(); +}; + +template ::value> +struct UnderlyingType; + +template +struct UnderlyingType { + using type = T; +}; + +template +struct UnderlyingType { + using type = typename std::underlying_type::type; +}; + +// Given two types T1 and T2, find types that can hold the smallest (in +// ::min_t) and the largest (in ::max_t) of the two values. +template ::value, + bool int2 = IsIntlike::value> +struct MType { + static_assert(int1 == int2, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when neither type is integral (and therefore presumably +// floating-point). +template +struct MType { + using min_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + using max_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// Specialization for when both types are integral. +template +struct MType { + // The type with the lowest minimum value. In case of a tie, the type with + // the lowest maximum value. In case that too is a tie, the types have the + // same range, and we arbitrarily pick T1. + using min_t = typename std::conditional< + SafeLt(Limits::lowest, Limits::lowest), T1, + typename std::conditional< + SafeGt(Limits::lowest, Limits::lowest), T2, + typename std::conditional::max, Limits::max), + T1, T2>::type>::type>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + // The type with the highest maximum value. In case of a tie, the types have + // the same range (because in C++, integer types with the same maximum also + // have the same minimum). + static_assert(SafeNe(Limits::max, Limits::max) || + SafeEq(Limits::lowest, Limits::lowest), + "integer types with the same max should have the same min"); + using max_t = + typename std::conditional::max, Limits::max), T1, + T2>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// A dummy type that we pass around at compile time but never actually use. +// Declared but not defined. +struct DefaultType; + +// ::type is A, except we fall back to B if A is DefaultType. We static_assert +// that the chosen type can hold all values that B can hold. +template +struct TypeOr { + using type = typename std::conditional::value, B, + A>::type; + static_assert(SafeLe(Limits::lowest, Limits::lowest) && + SafeGe(Limits::max, Limits::max), + "The specified type isn't large enough"); + static_assert(IsIntlike::value == IsIntlike::value && + std::is_floating_point::value == + std::is_floating_point::value, + "float<->int conversions not allowed"); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::min_t>::type> +constexpr R2 SafeMin(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeLt(a, b) ? static_cast(a) : static_cast(b); +} + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::max_t>::type> +constexpr R2 SafeMax(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeGt(a, b) ? static_cast(a) : static_cast(b); +} + +namespace safe_minmax_impl { + +// Given three types T, L, and H, let ::type be a suitable return value for +// SafeClamp(T, L, H). See the docs at the top of this file for details. +template ::value, + bool int2 = IsIntlike::value, bool int3 = IsIntlike::value> +struct ClampType { + static_assert(int1 == int2 && int1 == int3, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when all three types are floating-point. +template +struct ClampType { + using type = typename std::common_type::type; +}; + +// Specialization for when all three types are integral. +template +struct ClampType { + private: + // Range of the return value. The return type must be able to represent this + // full range. + static constexpr auto r_min = + SafeMax(Limits::lowest, SafeMin(Limits::lowest, Limits::lowest)); + static constexpr auto r_max = + SafeMin(Limits::max, SafeMax(Limits::max, Limits::max)); + + // Is the given type an acceptable return type? (That is, can it represent + // all possible return values, and is it no larger than the largest of the + // input types?) + template + struct AcceptableType { + private: + static constexpr bool not_too_large = sizeof(A) <= sizeof(L) || + sizeof(A) <= sizeof(H) || + sizeof(A) <= sizeof(T); + static constexpr bool range_contained = + SafeLe(Limits::lowest, r_min) && SafeLe(r_max, Limits::max); + + public: + static constexpr bool value = not_too_large && range_contained; + }; + + using best_signed_type = typename std::conditional< + AcceptableType::value, int8_t, + typename std::conditional< + AcceptableType::value, int16_t, + typename std::conditional::value, int32_t, + int64_t>::type>::type>::type; + + using best_unsigned_type = typename std::conditional< + AcceptableType::value, uint8_t, + typename std::conditional< + AcceptableType::value, uint16_t, + typename std::conditional::value, uint32_t, + uint64_t>::type>::type>::type; + + public: + // Pick the best type, preferring the same signedness as T but falling back + // to the other one if necessary. + using type = typename std::conditional< + std::is_signed::value, + typename std::conditional::value, + best_signed_type, best_unsigned_type>::type, + typename std::conditional::value, + best_unsigned_type, + best_signed_type>::type>::type; + static_assert(AcceptableType::value, ""); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T = safe_minmax_impl::DefaultType, + typename L = safe_minmax_impl::DefaultType, + typename H = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, typename safe_minmax_impl::ClampType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::type>::type> +R2 SafeClamp(T x, L min, H max) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The third argument must be integral or floating-point"); + return SafeLe(x, min) ? static_cast(min) + : SafeGe(x, max) ? static_cast(max) + : static_cast(x); +} + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_MINMAX_H_ diff --git a/src/common/sequence_number_unwrapper.h b/src/common/rtc_base/numerics/sequence_number_unwrapper.h similarity index 73% rename from src/common/sequence_number_unwrapper.h rename to src/common/rtc_base/numerics/sequence_number_unwrapper.h index 043a160..843423e 100644 --- a/src/common/sequence_number_unwrapper.h +++ b/src/common/rtc_base/numerics/sequence_number_unwrapper.h @@ -1,11 +1,15 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-08 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * Copyright (c) 2022 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 _SEQUENCE_NUMBER_UNWRAPPER_H_ -#define _SEQUENCE_NUMBER_UNWRAPPER_H_ +#ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ +#define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ #include @@ -15,6 +19,8 @@ #include "sequence_number_util.h" +namespace webrtc { + // A sequence number unwrapper where the first unwrapped value equals the // first value being unwrapped. template @@ -70,4 +76,6 @@ class SeqNumUnwrapper { using RtpTimestampUnwrapper = SeqNumUnwrapper; using RtpSequenceNumberUnwrapper = SeqNumUnwrapper; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_ diff --git a/src/common/sequence_number_util.h b/src/common/rtc_base/numerics/sequence_number_util.h similarity index 78% rename from src/common/sequence_number_util.h rename to src/common/rtc_base/numerics/sequence_number_util.h index 607938b..4ab0719 100644 --- a/src/common/sequence_number_util.h +++ b/src/common/rtc_base/numerics/sequence_number_util.h @@ -1,17 +1,23 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-08 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * 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. */ -#ifndef _SEQUENCE_NUMBER_UTIL_H_ -#define _SEQUENCE_NUMBER_UTIL_H_ +#ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ +#define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ #include #include #include "mod_ops.h" +namespace webrtc { + // Test if the sequence number `a` is ahead or at sequence number `b`. // // If `M` is an even number and the two sequence numbers are at max distance @@ -70,4 +76,6 @@ struct DescendingSeqNumComp { bool operator()(T a, T b) const { return AheadOf(b, a); } }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_ diff --git a/src/common/rtc_base/ref_counter.h b/src/common/rtc_base/ref_counter.h new file mode 100644 index 0000000..7983c89 --- /dev/null +++ b/src/common/rtc_base/ref_counter.h @@ -0,0 +1,75 @@ +/* + * Copyright 2017 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 RTC_BASE_REF_COUNTER_H_ +#define RTC_BASE_REF_COUNTER_H_ + +#include + +#include "ref_count.h" + +namespace webrtc { +namespace webrtc_impl { + +class RefCounter { + public: + explicit RefCounter(int ref_count) : ref_count_(ref_count) {} + RefCounter() = delete; + + void IncRef() { + // Relaxed memory order: The current thread is allowed to act on the + // resource protected by the reference counter both before and after the + // atomic op, so this function doesn't prevent memory access reordering. + ref_count_.fetch_add(1, std::memory_order_relaxed); + } + + // Returns kDroppedLastRef if this call dropped the last reference; the caller + // should therefore free the resource protected by the reference counter. + // Otherwise, returns kOtherRefsRemained (note that in case of multithreading, + // some other caller may have dropped the last reference by the time this call + // returns; all we know is that we didn't do it). + RefCountReleaseStatus DecRef() { + // Use release-acquire barrier to ensure all actions on the protected + // resource are finished before the resource can be freed. + // When ref_count_after_subtract > 0, this function require + // std::memory_order_release part of the barrier. + // When ref_count_after_subtract == 0, this function require + // std::memory_order_acquire part of the barrier. + // In addition std::memory_order_release is used for synchronization with + // the HasOneRef function to make sure all actions on the protected resource + // are finished before the resource is assumed to have exclusive access. + int ref_count_after_subtract = + ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1; + return ref_count_after_subtract == 0 + ? RefCountReleaseStatus::kDroppedLastRef + : RefCountReleaseStatus::kOtherRefsRemained; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a reference count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call performs + // the test for a reference count of one, and performs the memory barrier + // needed for the owning thread to act on the resource protected by the + // reference counter, knowing that it has exclusive access. + bool HasOneRef() const { + // To ensure resource protected by the reference counter has exclusive + // access, all changes to the resource before it was released by other + // threads must be visible by current thread. That is provided by release + // (in DecRef) and acquire (in this function) ordering. + return ref_count_.load(std::memory_order_acquire) == 1; + } + + private: + std::atomic ref_count_; +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // RTC_BASE_REF_COUNTER_H_ diff --git a/src/common/rtc_base/system_time.cc b/src/common/rtc_base/system_time.cc new file mode 100644 index 0000000..65ddca4 --- /dev/null +++ b/src/common/rtc_base/system_time.cc @@ -0,0 +1,97 @@ +/* + * Copyright 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. + */ + +// If WEBRTC_EXCLUDE_SYSTEM_TIME is set, an implementation of +// rtc::SystemTimeNanos() must be provided externally. +#ifndef WEBRTC_EXCLUDE_SYSTEM_TIME + +#include + +#include + +#if defined(WEBRTC_POSIX) +#include +#if defined(WEBRTC_MAC) +#include +#endif +#endif + +#if defined(WEBRTC_WIN) +// clang-format off +// clang formatting would put last, +// which leads to compilation failure. +#include +#include +#include +// clang-format on +#endif + +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/time_utils.h" +#include "system_time.h" + +namespace rtc { + +int64_t SystemTimeNanos() { + int64_t ticks; +#if defined(WEBRTC_MAC) + static mach_timebase_info_data_t timebase; + if (timebase.denom == 0) { + // Get the timebase if this is the first time we run. + // Recommended by Apple's QA1398. + if (mach_timebase_info(&timebase) != KERN_SUCCESS) { + } + } + // Use timebase to convert absolute time tick units into nanoseconds. + const auto mul = [](uint64_t a, uint32_t b) -> int64_t { + return rtc::dchecked_cast(a * b); + }; + ticks = mul(mach_absolute_time(), timebase.numer) / timebase.denom; +#elif defined(WEBRTC_POSIX) + struct timespec ts; + // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not + // supported? + clock_gettime(CLOCK_MONOTONIC, &ts); + ticks = kNumNanosecsPerSec * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec); +#elif defined(WINUWP) + ticks = WinUwpSystemTimeNanos(); +#elif defined(WEBRTC_WIN) + // TODO(webrtc:14601): Fix the volatile increment instead of suppressing the + // warning. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-volatile" + static volatile LONG last_timegettime = 0; + static volatile int64_t num_wrap_timegettime = 0; + volatile LONG* last_timegettime_ptr = &last_timegettime; + DWORD now = timeGetTime(); + // Atomically update the last gotten time + DWORD old = InterlockedExchange(last_timegettime_ptr, now); + if (now < old) { + // If now is earlier than old, there may have been a race between threads. + // 0x0fffffff ~3.1 days, the code will not take that long to execute + // so it must have been a wrap around. + if (old > 0xf0000000 && now < 0x0fffffff) { + num_wrap_timegettime++; + } + } + ticks = now + (num_wrap_timegettime << 32); + // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're + // just wasting a multiply and divide when doing Time() on Windows. + ticks = ticks * kNumNanosecsPerMillisec; +#pragma clang diagnostic pop +#else +#error Unsupported platform. +#endif + return ticks; +} + +} // namespace rtc +#endif // WEBRTC_EXCLUDE_SYSTEM_TIME diff --git a/src/common/rtc_base/system_time.h b/src/common/rtc_base/system_time.h new file mode 100644 index 0000000..c0ebc2a --- /dev/null +++ b/src/common/rtc_base/system_time.h @@ -0,0 +1,24 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_SYSTEM_TIME_H_ +#define RTC_BASE_SYSTEM_TIME_H_ + +#include + +namespace rtc { + +// Returns the actual system time, even if a clock is set for testing. +// Useful for timeouts while using a test clock, or for logging. +int64_t SystemTimeNanos(); + +} // namespace rtc + +#endif // RTC_BASE_SYSTEM_TIME_H_ diff --git a/src/common/rtc_base/time_utils.cc b/src/common/rtc_base/time_utils.cc new file mode 100644 index 0000000..2018e51 --- /dev/null +++ b/src/common/rtc_base/time_utils.cc @@ -0,0 +1,238 @@ +/* + * Copyright 2004 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 + +#if defined(WEBRTC_POSIX) +#include +#endif + +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/time_utils.h" +#include "system_time.h" +#if defined(WEBRTC_WIN) +#include "rtc_base/win32.h" +#endif +#if defined(WEBRTC_WIN) +#include +#endif + +namespace rtc { + +#if defined(WEBRTC_WIN) || defined(WINUWP) +// FileTime (January 1st 1601) to Unix time (January 1st 1970) +// offset in units of 100ns. +static constexpr uint64_t kFileTimeToUnixTimeEpochOffset = + 116444736000000000ULL; +static constexpr uint64_t kFileTimeToMicroSeconds = 10LL; +#endif + +ClockInterface* g_clock = nullptr; + +ClockInterface* SetClockForTesting(ClockInterface* clock) { + ClockInterface* prev = g_clock; + g_clock = clock; + return prev; +} + +ClockInterface* GetClockForTesting() { return g_clock; } + +#if defined(WINUWP) + +namespace { + +class TimeHelper final { + public: + TimeHelper(const TimeHelper&) = delete; + + // Resets the clock based upon an NTP server. This routine must be called + // prior to the main system start-up to ensure all clocks are based upon + // an NTP server time if NTP synchronization is required. No critical + // section is used thus this method must be called prior to any clock + // routines being used. + static void SyncWithNtp(int64_t ntp_server_time_ms) { + auto& singleton = Singleton(); + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + rtc::dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + singleton.app_start_time_ns_ = + (ntp_server_time_ms - kNTPTimeToUnixTimeEpochOffset) * 1000000 - + time_zone_bias_ns; + singleton.UpdateReferenceTime(); + } + + // Returns the number of nanoseconds that have passed since unix epoch. + static int64_t TicksNs() { + auto& singleton = Singleton(); + int64_t result = 0; + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + result = rtc::dchecked_cast( + (rtc::dchecked_cast(qpcnt.QuadPart) * 100000 / + rtc::dchecked_cast(singleton.os_ticks_per_second_)) * + 10000); + result = singleton.app_start_time_ns_ + result - + singleton.time_since_os_start_ns_; + return result; + } + + private: + TimeHelper() { + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + rtc::dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + FILETIME ft; + // This will give us system file in UTC format. + GetSystemTimeAsFileTime(&ft); + LARGE_INTEGER li; + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + + app_start_time_ns_ = (li.QuadPart - kFileTimeToUnixTimeEpochOffset) * 100 - + time_zone_bias_ns; + + UpdateReferenceTime(); + } + + static TimeHelper& Singleton() { + static TimeHelper singleton; + return singleton; + } + + void UpdateReferenceTime() { + LARGE_INTEGER qpfreq; + QueryPerformanceFrequency(&qpfreq); + os_ticks_per_second_ = rtc::dchecked_cast(qpfreq.QuadPart); + + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + time_since_os_start_ns_ = rtc::dchecked_cast( + (rtc::dchecked_cast(qpcnt.QuadPart) * 100000 / + rtc::dchecked_cast(os_ticks_per_second_)) * + 10000); + } + + private: + static constexpr uint64_t kNTPTimeToUnixTimeEpochOffset = 2208988800000L; + + // The number of nanoseconds since unix system epoch + int64_t app_start_time_ns_; + // The number of nanoseconds since the OS started + int64_t time_since_os_start_ns_; + // The OS calculated ticks per second + int64_t os_ticks_per_second_; +}; + +} // namespace + +void SyncWithNtp(int64_t time_from_ntp_server_ms) { + TimeHelper::SyncWithNtp(time_from_ntp_server_ms); +} + +int64_t WinUwpSystemTimeNanos() { return TimeHelper::TicksNs(); } + +#endif // defined(WINUWP) + +int64_t SystemTimeMillis() { + return static_cast(SystemTimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeNanos() { + if (g_clock) { + return g_clock->TimeNanos(); + } + return SystemTimeNanos(); +} + +uint32_t Time32() { + return static_cast(TimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeMillis() { return TimeNanos() / kNumNanosecsPerMillisec; } + +int64_t TimeMicros() { return TimeNanos() / kNumNanosecsPerMicrosec; } + +int64_t TimeAfter(int64_t elapsed) { + RTC_DCHECK_GE(elapsed, 0); + return TimeMillis() + elapsed; +} + +int32_t TimeDiff32(uint32_t later, uint32_t earlier) { return later - earlier; } + +int64_t TimeDiff(int64_t later, int64_t earlier) { return later - earlier; } + +int64_t TmToSeconds(const tm& tm) { + static short int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + static short int cumul_mdays[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + int year = tm.tm_year + 1900; + int month = tm.tm_mon; + int day = tm.tm_mday - 1; // Make 0-based like the rest. + int hour = tm.tm_hour; + int min = tm.tm_min; + int sec = tm.tm_sec; + + bool expiry_in_leap_year = + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + + if (year < 1970) return -1; + if (month < 0 || month > 11) return -1; + if (day < 0 || day >= mdays[month] + (expiry_in_leap_year && month == 2 - 1)) + return -1; + if (hour < 0 || hour > 23) return -1; + if (min < 0 || min > 59) return -1; + if (sec < 0 || sec > 59) return -1; + + day += cumul_mdays[month]; + + // Add number of leap days between 1970 and the expiration year, inclusive. + day += ((year / 4 - 1970 / 4) - (year / 100 - 1970 / 100) + + (year / 400 - 1970 / 400)); + + // We will have added one day too much above if expiration is during a leap + // year, and expiration is in January or February. + if (expiry_in_leap_year && month <= 2 - 1) // `month` is zero based. + day -= 1; + + // Combine all variables into seconds from 1970-01-01 00:00 (except `month` + // which was accumulated into `day` above). + return (((static_cast(year - 1970) * 365 + day) * 24 + hour) * 60 + + min) * + 60 + + sec; +} + +int64_t TimeUTCMicros() { + if (g_clock) { + return g_clock->TimeNanos() / kNumNanosecsPerMicrosec; + } +#if defined(WEBRTC_POSIX) + struct timeval time; + gettimeofday(&time, nullptr); + // Convert from second (1.0) and microsecond (1e-6). + return (static_cast(time.tv_sec) * rtc::kNumMicrosecsPerSec + + time.tv_usec); +#elif defined(WEBRTC_WIN) + FILETIME ft; + // This will give us system file in UTC format in multiples of 100ns. + GetSystemTimeAsFileTime(&ft); + LARGE_INTEGER li; + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + return (li.QuadPart - kFileTimeToUnixTimeEpochOffset) / + kFileTimeToMicroSeconds; +#endif +} + +int64_t TimeUTCMillis() { return TimeUTCMicros() / kNumMicrosecsPerMillisec; } + +} // namespace rtc diff --git a/src/common/rtc_base/time_utils.h b/src/common/rtc_base/time_utils.h new file mode 100644 index 0000000..7c80cb2 --- /dev/null +++ b/src/common/rtc_base/time_utils.h @@ -0,0 +1,132 @@ +/* + * Copyright 2005 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 RTC_BASE_TIME_UTILS_H_ +#define RTC_BASE_TIME_UTILS_H_ + +#include +#include + +#include "system_time.h" + +namespace rtc { + +static const int64_t kNumMillisecsPerSec = INT64_C(1000); +static const int64_t kNumMicrosecsPerSec = INT64_C(1000000); +static const int64_t kNumNanosecsPerSec = INT64_C(1000000000); + +static const int64_t kNumMicrosecsPerMillisec = + kNumMicrosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMillisec = + kNumNanosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMicrosec = + kNumNanosecsPerSec / kNumMicrosecsPerSec; + +// Elapsed milliseconds between NTP base, 1900 January 1 00:00 GMT +// (see https://tools.ietf.org/html/rfc868), and January 1 00:00 GMT 1970 +// epoch. This is useful when converting between the NTP time base and the +// time base used in RTCP reports. +constexpr int64_t kNtpJan1970Millisecs = 2'208'988'800 * kNumMillisecsPerSec; + +// TODO(honghaiz): Define a type for the time value specifically. + +class ClockInterface { + public: + virtual ~ClockInterface() {} + virtual int64_t TimeNanos() const = 0; +}; + +// Sets the global source of time. This is useful mainly for unit tests. +// +// Returns the previously set ClockInterface, or nullptr if none is set. +// +// Does not transfer ownership of the clock. SetClockForTesting(nullptr) +// should be called before the ClockInterface is deleted. +// +// This method is not thread-safe; it should only be used when no other thread +// is running (for example, at the start/end of a unit test, or start/end of +// main()). +// +// TODO(deadbeef): Instead of having functions that access this global +// ClockInterface, we may want to pass the ClockInterface into everything +// that uses it, eliminating the need for a global variable and this function. +ClockInterface* SetClockForTesting(ClockInterface* clock); + +// Returns previously set clock, or nullptr if no custom clock is being used. +ClockInterface* GetClockForTesting(); + +#if defined(WINUWP) +// Synchronizes the current clock based upon an NTP server's epoch in +// milliseconds. +void SyncWithNtp(int64_t time_from_ntp_server_ms); + +// Returns the current time in nanoseconds. The clock is synchonized with the +// system wall clock time upon instatiation. It may also be synchronized using +// the SyncWithNtp() function above. Please note that the clock will most likely +// drift away from the system wall clock time as time goes by. +int64_t WinUwpSystemTimeNanos(); +#endif // defined(WINUWP) + +// Returns the actual system time, even if a clock is set for testing. +// Useful for timeouts while using a test clock, or for logging. +int64_t SystemTimeMillis(); + +// Returns the current time in milliseconds in 32 bits. +uint32_t Time32(); + +// Returns the current time in milliseconds in 64 bits. +int64_t TimeMillis(); +// Deprecated. Do not use this in any new code. +inline int64_t Time() { return TimeMillis(); } + +// Returns the current time in microseconds. +int64_t TimeMicros(); + +// Returns the current time in nanoseconds. +int64_t TimeNanos(); + +// Returns a future timestamp, 'elapsed' milliseconds from now. +int64_t TimeAfter(int64_t elapsed); + +// Number of milliseconds that would elapse between 'earlier' and 'later' +// timestamps. The value is negative if 'later' occurs before 'earlier'. +int64_t TimeDiff(int64_t later, int64_t earlier); +int32_t TimeDiff32(uint32_t later, uint32_t earlier); + +// The number of milliseconds that have elapsed since 'earlier'. +inline int64_t TimeSince(int64_t earlier) { return TimeMillis() - earlier; } + +// The number of milliseconds that will elapse between now and 'later'. +inline int64_t TimeUntil(int64_t later) { return later - TimeMillis(); } + +// Convert from tm, which is relative to 1900-01-01 00:00 to number of +// seconds from 1970-01-01 00:00 ("epoch"). Don't return time_t since that +// is still 32 bits on many systems. +int64_t TmToSeconds(const tm& tm); + +// Return the number of microseconds since January 1, 1970, UTC. +// Useful mainly when producing logs to be correlated with other +// devices, and when the devices in question all have properly +// synchronized clocks. +// +// Note that this function obeys the system's idea about what the time +// is. It is not guaranteed to be monotonic; it will jump in case the +// system time is changed, e.g., by some other process calling +// settimeofday. Always use rtc::TimeMicros(), not this function, for +// measuring time intervals and timeouts. +int64_t TimeUTCMicros(); + +// Return the number of milliseconds since January 1, 1970, UTC. +// See above. +int64_t TimeUTCMillis(); + +} // namespace rtc + +#endif // RTC_BASE_TIME_UTILS_H_ diff --git a/src/common/type_traits.h b/src/common/rtc_base/type_traits.h similarity index 87% rename from src/common/type_traits.h rename to src/common/rtc_base/type_traits.h index 16e15c6..8f90a88 100644 --- a/src/common/type_traits.h +++ b/src/common/rtc_base/type_traits.h @@ -1,16 +1,22 @@ /* - * @Author: DI JUNKUN - * @Date: 2024-12-18 - * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * Copyright 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. */ -#ifndef _TYPE_TRAITS_H_ -#define _TYPE_TRAITS_H_ +#ifndef RTC_BASE_TYPE_TRAITS_H_ +#define RTC_BASE_TYPE_TRAITS_H_ #include #include #include +namespace rtc { + // Determines if the given class has zero-argument .data() and .size() methods // whose return values are convertible to T* and size_t, respectively. template @@ -130,4 +136,6 @@ static_assert(!IsIntlike::value, ""); } // namespace test_enum_intlike -#endif \ No newline at end of file +} // namespace rtc + +#endif // RTC_BASE_TYPE_TRAITS_H_ diff --git a/src/qos/acknowledged_bitrate_estimator.cpp b/src/qos/acknowledged_bitrate_estimator.cc similarity index 73% rename from src/qos/acknowledged_bitrate_estimator.cpp rename to src/qos/acknowledged_bitrate_estimator.cc index b344f3d..8287131 100644 --- a/src/qos/acknowledged_bitrate_estimator.cpp +++ b/src/qos/acknowledged_bitrate_estimator.cc @@ -16,48 +16,46 @@ #include #include -#include "bitrate_estimator.h" -#include "log.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/timestamp.h" #include "network_types.h" +namespace webrtc { + AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator() : AcknowledgedBitrateEstimator(std::make_unique()) {} -AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {} - AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator( std::unique_ptr bitrate_estimator) : in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {} void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector( const std::vector& packet_feedback_vector) { - if (!std::is_sorted(packet_feedback_vector.begin(), - packet_feedback_vector.end(), - PacketResult::ReceiveTimeOrder())) { - LOG_FATAL("packet_feedback_vector is not sorted"); - } for (const auto& packet : packet_feedback_vector) { if (alr_ended_time_ && packet.sent_packet.send_time > *alr_ended_time_) { bitrate_estimator_->ExpectFastRateChange(); alr_ended_time_.reset(); } - int64_t acknowledged_estimate = packet.sent_packet.size; + DataSize acknowledged_estimate = packet.sent_packet.size; acknowledged_estimate += packet.sent_packet.prior_unacked_data; bitrate_estimator_->Update(packet.receive_time, acknowledged_estimate, in_alr_); } } -std::optional AcknowledgedBitrateEstimator::bitrate() const { +std::optional AcknowledgedBitrateEstimator::bitrate() const { return bitrate_estimator_->bitrate(); } -std::optional AcknowledgedBitrateEstimator::PeekRate() const { +std::optional AcknowledgedBitrateEstimator::PeekRate() const { return bitrate_estimator_->PeekRate(); } -void AcknowledgedBitrateEstimator::SetAlrEndedTime(int64_t alr_ended_time) { +void AcknowledgedBitrateEstimator::SetAlrEndedTime(Timestamp alr_ended_time) { alr_ended_time_.emplace(alr_ended_time); } void AcknowledgedBitrateEstimator::SetAlr(bool in_alr) { in_alr_ = in_alr; } + +} // namespace webrtc diff --git a/src/qos/acknowledged_bitrate_estimator.h b/src/qos/acknowledged_bitrate_estimator.h index da97106..b349534 100644 --- a/src/qos/acknowledged_bitrate_estimator.h +++ b/src/qos/acknowledged_bitrate_estimator.h @@ -1,19 +1,27 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-14 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * Copyright (c) 2017 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 _ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ -#define _ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ #include #include #include +#include "api/units/data_rate.h" +#include "api/units/timestamp.h" #include "bitrate_estimator.h" #include "network_types.h" +namespace webrtc { + class AcknowledgedBitrateEstimator { public: AcknowledgedBitrateEstimator( @@ -24,15 +32,17 @@ class AcknowledgedBitrateEstimator { void IncomingPacketFeedbackVector( const std::vector& packet_feedback_vector); - std::optional bitrate() const; - std::optional PeekRate() const; + std::optional bitrate() const; + std::optional PeekRate() const; void SetAlr(bool in_alr); - void SetAlrEndedTime(int64_t alr_ended_time); + void SetAlrEndedTime(Timestamp alr_ended_time); private: - std::optional alr_ended_time_; + std::optional alr_ended_time_; bool in_alr_; std::unique_ptr bitrate_estimator_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ diff --git a/src/qos/aimd_rate_control.cc b/src/qos/aimd_rate_control.cc new file mode 100644 index 0000000..af8bca3 --- /dev/null +++ b/src/qos/aimd_rate_control.cc @@ -0,0 +1,330 @@ +/* + * 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 "aimd_rate_control.h" + +#include + +#include +#include +#include +#include + +#include "api/units/data_rate.h" +#include "log.h" +#include "network_types.h" +#include "overuse_detector.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200); +constexpr double kDefaultBackoffFactor = 0.85; + +constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor"; + +} // namespace + +AimdRateControl::AimdRateControl() : AimdRateControl(/* send_side =*/false) {} + +AimdRateControl::AimdRateControl(bool send_side) + : min_configured_bitrate_(kCongestionControllerMinBitrate), + max_configured_bitrate_(DataRate::KilobitsPerSec(30000)), + current_bitrate_(max_configured_bitrate_), + latest_estimated_throughput_(current_bitrate_), + link_capacity_(), + rate_control_state_(RateControlState::kRcHold), + time_last_bitrate_change_(Timestamp::MinusInfinity()), + time_last_bitrate_decrease_(Timestamp::MinusInfinity()), + time_first_throughput_estimate_(Timestamp::MinusInfinity()), + bitrate_is_initialized_(false), + in_alr_(false), + rtt_(kDefaultRtt), + send_side_(send_side) {} + +AimdRateControl::~AimdRateControl() {} + +void AimdRateControl::SetStartBitrate(DataRate start_bitrate) { + current_bitrate_ = start_bitrate; + latest_estimated_throughput_ = current_bitrate_; + bitrate_is_initialized_ = true; +} + +void AimdRateControl::SetMinBitrate(DataRate min_bitrate) { + min_configured_bitrate_ = min_bitrate; + current_bitrate_ = std::max(min_bitrate, current_bitrate_); +} + +bool AimdRateControl::ValidEstimate() const { return bitrate_is_initialized_; } + +TimeDelta AimdRateControl::GetFeedbackInterval() const { + // Estimate how often we can send RTCP if we allocate up to 5% of bandwidth + // to feedback. + const DataSize kRtcpSize = DataSize::Bytes(80); + const DataRate rtcp_bitrate = current_bitrate_ * 0.05; + const TimeDelta interval = kRtcpSize / rtcp_bitrate; + const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200); + const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000); + return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval); +} + +bool AimdRateControl::TimeToReduceFurther(Timestamp at_time, + DataRate estimated_throughput) const { + const TimeDelta bitrate_reduction_interval = + rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200)); + if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) { + return true; + } + if (ValidEstimate()) { + // TODO(terelius/holmer): Investigate consequences of increasing + // the threshold to 0.95 * LatestEstimate(). + const DataRate threshold = 0.5 * LatestEstimate(); + return estimated_throughput < threshold; + } + return false; +} + +bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const { + return ValidEstimate() && + TimeToReduceFurther(at_time, + LatestEstimate() / 2 - DataRate::BitsPerSec(1)); +} + +DataRate AimdRateControl::LatestEstimate() const { return current_bitrate_; } + +void AimdRateControl::SetRtt(TimeDelta rtt) { rtt_ = rtt; } + +DataRate AimdRateControl::Update(const RateControlInput& input, + Timestamp at_time) { + // Set the initial bit rate value to what we're receiving the first half + // second. + // TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code. + if (!bitrate_is_initialized_) { + const TimeDelta kInitializationTime = TimeDelta::Seconds(5); + if (time_first_throughput_estimate_.IsInfinite()) { + if (input.estimated_throughput) time_first_throughput_estimate_ = at_time; + } else if (at_time - time_first_throughput_estimate_ > + kInitializationTime && + input.estimated_throughput) { + current_bitrate_ = *input.estimated_throughput; + bitrate_is_initialized_ = true; + } + } + + ChangeBitrate(input, at_time); + return current_bitrate_; +} + +void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) { + in_alr_ = in_alr; +} + +void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) { + bitrate_is_initialized_ = true; + DataRate prev_bitrate = current_bitrate_; + current_bitrate_ = ClampBitrate(bitrate); + time_last_bitrate_change_ = at_time; + if (current_bitrate_ < prev_bitrate) { + time_last_bitrate_decrease_ = at_time; + } +} + +double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const { + const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30; + DataSize frame_size = current_bitrate_ * kFrameInterval; + const DataSize kPacketSize = DataSize::Bytes(1200); + double packets_per_frame = std::ceil(frame_size / kPacketSize); + DataSize avg_packet_size = frame_size / packets_per_frame; + + // Approximate the over-use estimator delay to 100 ms. + TimeDelta response_time = rtt_ + TimeDelta::Millis(100); + + response_time = response_time * 2; + double increase_rate_bps_per_second = + (avg_packet_size / response_time).bps(); + double kMinIncreaseRateBpsPerSecond = 4000; + return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second); +} + +TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const { + const TimeDelta kMinPeriod = TimeDelta::Seconds(2); + const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3); + const TimeDelta kMaxPeriod = TimeDelta::Seconds(50); + + double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond(); + if (!last_decrease_) return kDefaultPeriod; + double time_to_recover_decrease_seconds = + last_decrease_->bps() / increase_rate_bps_per_second; + TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds); + return period.Clamped(kMinPeriod, kMaxPeriod); +} + +void AimdRateControl::ChangeBitrate(const RateControlInput& input, + Timestamp at_time) { + std::optional new_bitrate; + DataRate estimated_throughput = + input.estimated_throughput.value_or(latest_estimated_throughput_); + if (input.estimated_throughput) + latest_estimated_throughput_ = *input.estimated_throughput; + + // An over-use should always trigger us to reduce the bitrate, even though + // we have not yet established our first estimate. By acting on the over-use, + // we will end up with a valid estimate. + if (!bitrate_is_initialized_ && + input.bw_state != BandwidthUsage::kBwOverusing) + return; + + ChangeState(input, at_time); + + switch (rate_control_state_) { + case RateControlState::kRcHold: + break; + + case RateControlState::kRcIncrease: { + if (estimated_throughput > link_capacity_.UpperBound()) + link_capacity_.Reset(); + + // We limit the new bitrate based on the troughput to avoid unlimited + // bitrate increases. We allow a bit more lag at very low rates to not too + // easily get stuck if the encoder produces uneven outputs. + DataRate increase_limit = + 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); + if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) { + // Do not increase the delay based estimate in alr since the estimator + // will not be able to get transport feedback necessary to detect if + // the new estimate is correct. + // If we have previously increased above the limit (for instance due to + // probing), we don't allow further changes. + increase_limit = current_bitrate_; + } + + if (current_bitrate_ < increase_limit) { + DataRate increased_bitrate = DataRate::MinusInfinity(); + if (link_capacity_.has_estimate()) { + // The link_capacity estimate is reset if the measured throughput + // is too far from the estimate. We can therefore assume that our + // target rate is reasonably close to link capacity and use additive + // increase. + DataRate additive_increase = + AdditiveRateIncrease(at_time, time_last_bitrate_change_); + increased_bitrate = current_bitrate_ + additive_increase; + } else { + // If we don't have an estimate of the link capacity, use faster ramp + // up to discover the capacity. + DataRate multiplicative_increase = MultiplicativeRateIncrease( + at_time, time_last_bitrate_change_, current_bitrate_); + increased_bitrate = current_bitrate_ + multiplicative_increase; + } + new_bitrate = std::min(increased_bitrate, increase_limit); + } + time_last_bitrate_change_ = at_time; + break; + } + + case RateControlState::kRcDecrease: { + DataRate decreased_bitrate = DataRate::PlusInfinity(); + + // Set bit rate to something slightly lower than the measured throughput + // to get rid of any self-induced delay. + decreased_bitrate = estimated_throughput * beta_; + if (decreased_bitrate > DataRate::KilobitsPerSec(5)) { + decreased_bitrate -= DataRate::KilobitsPerSec(5); + } + + if (decreased_bitrate > current_bitrate_) { + // TODO(terelius): The link_capacity estimate may be based on old + // throughput measurements. Relying on them may lead to unnecessary + // BWE drops. + if (link_capacity_.has_estimate()) { + decreased_bitrate = beta_ * link_capacity_.estimate(); + } + } + // Avoid increasing the rate when over-using. + if (decreased_bitrate < current_bitrate_) { + new_bitrate = decreased_bitrate; + } + + if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) { + if (!new_bitrate.has_value()) { + last_decrease_ = DataRate::Zero(); + } else { + last_decrease_ = current_bitrate_ - *new_bitrate; + } + } + if (estimated_throughput < link_capacity_.LowerBound()) { + // The current throughput is far from the estimated link capacity. Clear + // the estimate to allow an immediate update in OnOveruseDetected. + link_capacity_.Reset(); + } + + bitrate_is_initialized_ = true; + link_capacity_.OnOveruseDetected(estimated_throughput); + // Stay on hold until the pipes are cleared. + rate_control_state_ = RateControlState::kRcHold; + time_last_bitrate_change_ = at_time; + time_last_bitrate_decrease_ = at_time; + break; + } + default: + break; + } + + current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_)); +} + +DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const { + new_bitrate = std::max(new_bitrate, min_configured_bitrate_); + return new_bitrate; +} + +DataRate AimdRateControl::MultiplicativeRateIncrease( + Timestamp at_time, Timestamp last_time, DataRate current_bitrate) const { + double alpha = 1.08; + if (last_time.IsFinite()) { + auto time_since_last_update = at_time - last_time; + alpha = pow(alpha, std::min(time_since_last_update.seconds(), 1.0)); + } + DataRate multiplicative_increase = + std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000)); + return multiplicative_increase; +} + +DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time, + Timestamp last_time) const { + double time_period_seconds = (at_time - last_time).seconds(); + double data_rate_increase_bps = + GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds; + return DataRate::BitsPerSec(data_rate_increase_bps); +} + +void AimdRateControl::ChangeState(const RateControlInput& input, + Timestamp at_time) { + switch (input.bw_state) { + case BandwidthUsage::kBwNormal: + if (rate_control_state_ == RateControlState::kRcHold) { + time_last_bitrate_change_ = at_time; + rate_control_state_ = RateControlState::kRcIncrease; + } + break; + case BandwidthUsage::kBwOverusing: + if (rate_control_state_ != RateControlState::kRcDecrease) { + rate_control_state_ = RateControlState::kRcDecrease; + } + break; + case BandwidthUsage::kBwUnderusing: + rate_control_state_ = RateControlState::kRcHold; + break; + default: + break; + } +} + +} // namespace webrtc diff --git a/src/qos/aimd_rate_control.h b/src/qos/aimd_rate_control.h new file mode 100644 index 0000000..5bd8d0f --- /dev/null +++ b/src/qos/aimd_rate_control.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ + +#include + +#include + +#include "api/units/data_rate.h" +#include "api/units/timestamp.h" +#include "bwe_defines.h" +#include "link_capacity_estimator.h" +#include "network_types.h" + +namespace webrtc { +// A rate control implementation based on additive increases of +// bitrate when no over-use is detected and multiplicative decreases when +// over-uses are detected. When we think the available bandwidth has changes or +// is unknown, we will switch to a "slow-start mode" where we increase +// multiplicatively. +class AimdRateControl { + public: + explicit AimdRateControl(); + AimdRateControl(bool send_side); + ~AimdRateControl(); + + // Returns true if the target bitrate has been initialized. This happens + // either if it has been explicitly set via SetStartBitrate/SetEstimate, or if + // we have measured a throughput. + bool ValidEstimate() const; + void SetStartBitrate(DataRate start_bitrate); + void SetMinBitrate(DataRate min_bitrate); + TimeDelta GetFeedbackInterval() const; + + // Returns true if the bitrate estimate hasn't been changed for more than + // an RTT, or if the estimated_throughput is less than half of the current + // estimate. Should be used to decide if we should reduce the rate further + // when over-using. + bool TimeToReduceFurther(Timestamp at_time, + DataRate estimated_throughput) const; + // As above. To be used if overusing before we have measured a throughput. + bool InitialTimeToReduceFurther(Timestamp at_time) const; + + DataRate LatestEstimate() const; + void SetRtt(TimeDelta rtt); + DataRate Update(const RateControlInput& input, Timestamp at_time); + void SetInApplicationLimitedRegion(bool in_alr); + void SetEstimate(DataRate bitrate, Timestamp at_time); + + // Returns the increase rate when used bandwidth is near the link capacity. + double GetNearMaxIncreaseRateBpsPerSecond() const; + // Returns the expected time between overuse signals (assuming steady state). + TimeDelta GetExpectedBandwidthPeriod() const; + + private: + enum class RateControlState { kRcHold, kRcIncrease, kRcDecrease }; + + friend class GoogCcStatePrinter; + // Update the target bitrate based on, among other things, the current rate + // control state, the current target bitrate and the estimated throughput. + // When in the "increase" state the bitrate will be increased either + // additively or multiplicatively depending on the rate control region. When + // in the "decrease" state the bitrate will be decreased to slightly below the + // current throughput. When in the "hold" state the bitrate will be kept + // constant to allow built up queues to drain. + void ChangeBitrate(const RateControlInput& input, Timestamp at_time); + + DataRate ClampBitrate(DataRate new_bitrate) const; + DataRate MultiplicativeRateIncrease(Timestamp at_time, Timestamp last_ms, + DataRate current_bitrate) const; + DataRate AdditiveRateIncrease(Timestamp at_time, Timestamp last_time) const; + void UpdateChangePeriod(Timestamp at_time); + void ChangeState(const RateControlInput& input, Timestamp at_time); + + DataRate min_configured_bitrate_; + DataRate max_configured_bitrate_; + DataRate current_bitrate_; + DataRate latest_estimated_throughput_; + LinkCapacityEstimator link_capacity_; + std::optional network_estimate_; + RateControlState rate_control_state_; + Timestamp time_last_bitrate_change_; + Timestamp time_last_bitrate_decrease_; + Timestamp time_first_throughput_estimate_; + bool bitrate_is_initialized_; + double beta_; + bool in_alr_; + TimeDelta rtt_; + const bool send_side_; + // Allow the delay based estimate to only increase as long as application + // limited region (alr) is not detected. + const bool no_bitrate_increase_in_alr_; + // If "Disabled", estimated link capacity is not used as upper bound. + bool disable_estimate_bounded_increase_ = true; + bool use_current_estimate_as_min_upper_bound_ = true; + std::optional last_decrease_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ diff --git a/src/qos/alr_detector.cpp b/src/qos/alr_detector.cc similarity index 90% rename from src/qos/alr_detector.cpp rename to src/qos/alr_detector.cc index ca264b0..bf39b95 100644 --- a/src/qos/alr_detector.cpp +++ b/src/qos/alr_detector.cc @@ -10,17 +10,19 @@ #include "alr_detector.h" -#include #include #include #include #include +#include "rtc_base/time_utils.h" + +namespace webrtc { + AlrDetector::AlrDetector(AlrDetectorConfig config) : conf_(config), alr_budget_(0, true) {} AlrDetector::AlrDetector() : alr_budget_(0, true) {} - AlrDetector::~AlrDetector() {} void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) { @@ -38,10 +40,7 @@ void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) { bool state_changed = false; if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio && !alr_started_time_ms_) { - alr_started_time_ms_.emplace( - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()); + alr_started_time_ms_.emplace(rtc::TimeMillis()); state_changed = true; } else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio && alr_started_time_ms_) { @@ -60,3 +59,5 @@ std::optional AlrDetector::GetApplicationLimitedRegionStartTime() const { return alr_started_time_ms_; } + +} // namespace webrtc diff --git a/src/qos/alr_detector.h b/src/qos/alr_detector.h index 2d3a092..b3cf52b 100644 --- a/src/qos/alr_detector.h +++ b/src/qos/alr_detector.h @@ -1,11 +1,15 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-14 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * 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. */ -#ifndef _ALR_DETECTOR_H_ -#define _ALR_DETECTOR_H_ +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_ #include #include @@ -15,6 +19,10 @@ #include "interval_budget.h" +namespace webrtc { + +class RtcEventLog; + struct AlrDetectorConfig { // Sent traffic ratio as a function of network capacity used to determine // application-limited region. ALR region start when bandwidth usage drops @@ -56,5 +64,6 @@ class AlrDetector { IntervalBudget alr_budget_; std::optional alr_started_time_ms_; }; +} // namespace webrtc -#endif \ No newline at end of file +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_ diff --git a/src/qos/bandwidth_usage.h b/src/qos/bandwidth_usage.h new file mode 100644 index 0000000..e65ac55 --- /dev/null +++ b/src/qos/bandwidth_usage.h @@ -0,0 +1,17 @@ +/* + * @Author: DI JUNKUN + * @Date: 2025-01-15 + * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _BANDWIDTH_USAGE_H_ +#define _BANDWIDTH_USAGE_H_ + +enum class BandwidthUsage { + kBwNormal = 0, + kBwUnderusing = 1, + kBwOverusing = 2, + kLast +}; + +#endif \ No newline at end of file diff --git a/src/qos/bitrate_estimator.cpp b/src/qos/bitrate_estimator.cc similarity index 80% rename from src/qos/bitrate_estimator.cpp rename to src/qos/bitrate_estimator.cc index 1d400ae..d845ed6 100644 --- a/src/qos/bitrate_estimator.cpp +++ b/src/qos/bitrate_estimator.cc @@ -15,6 +15,13 @@ #include #include +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + namespace { constexpr int kInitialRateWindowMs = 500; constexpr int kRateWindowMs = 150; @@ -33,9 +40,9 @@ BitrateEstimator::BitrateEstimator() uncertainty_scale_(10.0), uncertainty_scale_in_alr_(uncertainty_scale_), small_sample_uncertainty_scale_(uncertainty_scale_), - small_sample_threshold_(0), - uncertainty_symmetry_cap_(0), - estimate_floor_(0), + small_sample_threshold_(DataSize::Zero()), + uncertainty_symmetry_cap_(DataRate::Zero()), + estimate_floor_(DataRate::Zero()), current_window_ms_(0), prev_time_ms_(-1), bitrate_estimate_kbps_(-1.0f), @@ -43,14 +50,14 @@ BitrateEstimator::BitrateEstimator() BitrateEstimator::~BitrateEstimator() = default; -void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) { - int rate_window_ms = noninitial_window_ms_.Get(); +void BitrateEstimator::Update(Timestamp at_time, DataSize amount, bool in_alr) { + int rate_window_ms = noninitial_window_ms_; // We use a larger window at the beginning to get a more stable sample that // we can use to initialize the estimate. - if (bitrate_estimate_kbps_ < 0.f) rate_window_ms = initial_window_ms_.Get(); + if (bitrate_estimate_kbps_ < 0.f) rate_window_ms = initial_window_ms_; bool is_small_sample = false; - float bitrate_sample_kbps = - UpdateWindow(at_time, amount, rate_window_ms, &is_small_sample); + float bitrate_sample_kbps = UpdateWindow(at_time.ms(), amount.bytes(), + rate_window_ms, &is_small_sample); if (bitrate_sample_kbps < 0.0f) return; if (bitrate_estimate_kbps_ < 0.0f) { // This is the very first sample we get. Use it to initialize the estimate. @@ -73,8 +80,7 @@ void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) { float sample_uncertainty = scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) / (bitrate_estimate_kbps_ + - std::min(bitrate_sample_kbps, - static_cast(uncertainty_symmetry_cap_))); + std::min(bitrate_sample_kbps, uncertainty_symmetry_cap_.kbps())); float sample_var = sample_uncertainty * sample_uncertainty; // Update a bayesian estimate of the rate, weighting it lower if the sample @@ -86,7 +92,7 @@ void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) { pred_bitrate_estimate_var * bitrate_sample_kbps) / (sample_var + pred_bitrate_estimate_var); bitrate_estimate_kbps_ = - std::max(bitrate_estimate_kbps_, static_cast(estimate_floor_)); + std::max(bitrate_estimate_kbps_, estimate_floor_.kbps()); bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var / (sample_var + pred_bitrate_estimate_var); } @@ -111,7 +117,7 @@ float BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes, prev_time_ms_ = now_ms; float bitrate_sample = -1.0f; if (current_window_ms_ >= rate_window_ms) { - *is_small_sample = sum_ < small_sample_threshold_; + *is_small_sample = sum_ < small_sample_threshold_.bytes(); bitrate_sample = 8.0f * sum_ / static_cast(rate_window_ms); current_window_ms_ -= rate_window_ms; sum_ = 0; @@ -120,13 +126,14 @@ float BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes, return bitrate_sample; } -std::optional BitrateEstimator::bitrate() const { +std::optional BitrateEstimator::bitrate() const { if (bitrate_estimate_kbps_ < 0.f) return std::nullopt; - return static_cast(bitrate_estimate_kbps_); + return DataRate::KilobitsPerSec(bitrate_estimate_kbps_); } -std::optional BitrateEstimator::PeekRate() const { - if (current_window_ms_ > 0) return sum_ / current_window_ms_; +std::optional BitrateEstimator::PeekRate() const { + if (current_window_ms_ > 0) + return DataSize::Bytes(sum_) / TimeDelta::Millis(current_window_ms_); return std::nullopt; } @@ -135,3 +142,5 @@ void BitrateEstimator::ExpectFastRateChange() { // bitrate to change fast for the next few samples. bitrate_estimate_var_ += 200; } + +} // namespace webrtc diff --git a/src/qos/bitrate_estimator.h b/src/qos/bitrate_estimator.h index 74a6c88..fa19cca 100644 --- a/src/qos/bitrate_estimator.h +++ b/src/qos/bitrate_estimator.h @@ -1,18 +1,27 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-14 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * Copyright (c) 2017 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 _BITRATE_ESTIMATOR_H_ -#define _BITRATE_ESTIMATOR_H_ +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_ #include #include +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/timestamp.h" #include "constrained.h" +namespace webrtc { + // Computes a bayesian estimate of the throughput given acks containing // the arrival time and payload size. Samples which are far from the current // estimate or are based on few packets are given a smaller weight, as they @@ -20,12 +29,12 @@ // unrelated to congestion. class BitrateEstimator { public: - explicit BitrateEstimator(); + BitrateEstimator(); virtual ~BitrateEstimator(); - virtual void Update(int64_t at_time, int64_t amount, bool in_alr); + virtual void Update(Timestamp at_time, DataSize amount, bool in_alr); - virtual std::optional bitrate() const; - std::optional PeekRate() const; + virtual std::optional bitrate() const; + std::optional PeekRate() const; virtual void ExpectFastRateChange(); @@ -38,13 +47,15 @@ class BitrateEstimator { double uncertainty_scale_; double uncertainty_scale_in_alr_; double small_sample_uncertainty_scale_; - int64_t small_sample_threshold_; - int64_t uncertainty_symmetry_cap_; - int64_t estimate_floor_; + DataSize small_sample_threshold_; + DataRate uncertainty_symmetry_cap_; + DataRate estimate_floor_; int64_t current_window_ms_; int64_t prev_time_ms_; float bitrate_estimate_kbps_; float bitrate_estimate_var_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_ diff --git a/src/qos/bwe_defines.h b/src/qos/bwe_defines.h new file mode 100644 index 0000000..7326cd6 --- /dev/null +++ b/src/qos/bwe_defines.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ + +#include + +#include + +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "bandwidth_usage.h" + +namespace webrtc { + +inline constexpr DataRate kCongestionControllerMinBitrate = + DataRate::BitsPerSec(5'000); +inline constexpr TimeDelta kBitrateWindow = TimeDelta::Seconds(1); + +extern const char kBweTypeHistogram[]; + +enum BweNames { + kReceiverNoExtension = 0, + kReceiverTOffset = 1, + kReceiverAbsSendTime = 2, + kSendSideTransportSeqNum = 3, + kBweNamesMax = 4 +}; + +struct RateControlInput { + RateControlInput(BandwidthUsage bw_state, + const std::optional& estimated_throughput); + ~RateControlInput(); + + BandwidthUsage bw_state; + std::optional estimated_throughput; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ diff --git a/src/qos/congestion_control.cpp b/src/qos/congestion_control.cpp index 8db3857..4aaae88 100644 --- a/src/qos/congestion_control.cpp +++ b/src/qos/congestion_control.cpp @@ -122,10 +122,6 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback( } } - if (network_estimator_) { - network_estimator_->OnTransportPacketsFeedback(report); - SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate()); - } std::optional probe_bitrate = probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(); if (ignore_probes_lower_than_network_estimate_ && probe_bitrate && diff --git a/src/qos/congestion_control.h b/src/qos/congestion_control.h index 9e77b6a..f1a2641 100644 --- a/src/qos/congestion_control.h +++ b/src/qos/congestion_control.h @@ -7,6 +7,7 @@ #include "acknowledged_bitrate_estimator.h" #include "alr_detector.h" #include "congestion_window_pushback_controller.h" +#include "delay_based_bwe.h" #include "network_types.h" #include "send_side_bandwidth_estimation.h" diff --git a/src/qos/congestion_control_feedback.cpp b/src/qos/congestion_control_feedback.cpp index f3949bf..db7baa1 100644 --- a/src/qos/congestion_control_feedback.cpp +++ b/src/qos/congestion_control_feedback.cpp @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2024 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 "congestion_control_feedback.h" #include @@ -6,10 +16,17 @@ #include #include +#include "api/array_view.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "byte_io.h" +#include "common_header.h" #include "log.h" +#include "rtc_base/network/ecn_marking.h" + +namespace webrtc { +namespace rtcp { -// rfc8888 - RTP Congestion Control Feedback /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P| FMT=11 | PT = 205 | length | @@ -38,23 +55,6 @@ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -// for this implementation, only one stream is supported. -/* - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| FMT=11 | PT = 205 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of RTCP packet sender | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of 1st RTP Stream | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | begin_seq | num_reports | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |R|ECN| Arrival time offset | ... . - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Report Timestamp (32 bits) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - namespace { constexpr size_t kSenderSsrcLength = 4; @@ -78,60 +78,55 @@ constexpr uint16_t kEcnCe = 0x03; // measurement. If the measurement is unavailable or if the arrival time of the // RTP packet is after the time represented by the RTS field, then an ATO value // of 0x1FFF MUST be reported for the packet. -uint16_t To13bitAto(int64_t arrival_time_offset) { - if (arrival_time_offset < 0) { +uint16_t To13bitAto(TimeDelta arrival_time_offset) { + if (arrival_time_offset < TimeDelta::Zero()) { return 0x1FFF; } - return std::min(static_cast(1024 * (arrival_time_offset / 1000)), - int64_t{0x1FFE}); + return std::min( + static_cast(1024 * arrival_time_offset.seconds()), + int64_t{0x1FFE}); } -int64_t AtoToTimeDelta(uint16_t receive_info) { +TimeDelta AtoToTimeDelta(uint16_t receive_info) { // receive_info // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |R|ECN| Arrival time offset | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - // ato -> second const uint16_t ato = receive_info & 0x1FFF; if (ato == 0x1FFE) { - return std::numeric_limits::max(); + return TimeDelta::PlusInfinity(); } if (ato == 0x1FFF) { - return std::numeric_limits::min(); + return TimeDelta::MinusInfinity(); } - return ato / 1024; + return TimeDelta::Seconds(ato) / 1024; } -uint16_t To2BitEcn(EcnMarking ecn_marking) { +uint16_t To2BitEcn(rtc::EcnMarking ecn_marking) { switch (ecn_marking) { - case EcnMarking::kNotEct: + case rtc::EcnMarking::kNotEct: return 0; - case EcnMarking::kEct1: + case rtc::EcnMarking::kEct1: return kEcnEct1 << 13; - case EcnMarking::kEct0: + case rtc::EcnMarking::kEct0: return kEcnEct0 << 13; - case EcnMarking::kCe: + case rtc::EcnMarking::kCe: return kEcnCe << 13; - default: { - LOG_FATAL("Unexpected ecn marking: {}", static_cast(ecn_marking)); - return 0; - } } } -EcnMarking ToEcnMarking(uint16_t receive_info) { +rtc::EcnMarking ToEcnMarking(uint16_t receive_info) { const uint16_t ecn = (receive_info >> 13) & 0b11; if (ecn == kEcnEct1) { - return EcnMarking::kEct1; + return rtc::EcnMarking::kEct1; } if (ecn == kEcnEct0) { - return EcnMarking::kEct0; + return rtc::EcnMarking::kEct0; } if (ecn == kEcnCe) { - return EcnMarking::kCe; + return rtc::EcnMarking::kCe; } - return EcnMarking::kNotEct; + return rtc::EcnMarking::kNotEct; } } // namespace @@ -145,9 +140,9 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position, size_t max_length, PacketReadyCallback callback) const { // Ensure there is enough room for this packet. - // while (*position + BlockLength() > max_length) { - // if (!OnBufferFull(buffer, position, callback)) return false; - // } + while (*position + BlockLength() > max_length) { + if (!OnBufferFull(buffer, position, callback)) return false; + } const size_t position_end = *position + BlockLength(); // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -161,14 +156,14 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position, *position += 4; // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | SSRC of 1st RTP Stream | + // | SSRC of nth RTP Stream | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | begin_seq | num_reports | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |R|ECN| Arrival time offset | ... . // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - auto write_report_for_ssrc = [&](std::vector packets) { + // . . + auto write_report_for_ssrc = [&](rtc::ArrayView packets) { // SSRC of nth RTP stream. ByteWriter::WriteBigEndian(&buffer[*position], packets[0].ssrc); *position += 4; @@ -180,27 +175,18 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position, // num_reports uint16_t num_reports = packets.size(); - if (static_cast(packets[packets.size() - 1].sequence_number - - packets[0].sequence_number + 1) != - packets.size()) { - LOG_FATAL("Expected continous rtp sequence numbers"); - return false; - } - // Each report block MUST NOT include more than 16384 packet metric // blocks (i.e., it MUST NOT report on more than one quarter of the // sequence number space in a single report). if (num_reports > 16384) { - LOG_FATAL("Unexpected number of reports: {}", num_reports); - return false; + LOG_ERROR("Unexpected number of reports:{}", num_reports); + return; } ByteWriter::WriteBigEndian(&buffer[*position], num_reports); *position += 2; for (const PacketInfo& packet : packets) { - bool received = - (packet.arrival_time_offset != std::numeric_limits::min()) && - (packet.arrival_time_offset != std::numeric_limits::max()); + bool received = packet.arrival_time_offset.IsFinite(); uint16_t packet_info = 0; if (received) { packet_info = 0x8000 | To2BitEcn(packet.ecn) | @@ -214,17 +200,20 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position, ByteWriter::WriteBigEndian(&buffer[*position], 0); *position += 2; } - - return true; }; - if (!packets_.empty()) { + rtc::ArrayView remaining(packets_); + while (!remaining.empty()) { int number_of_packets_for_ssrc = 0; - uint32_t ssrc = packets_[0].ssrc; - for (const PacketInfo& packet_info : packets_) { + uint32_t ssrc = remaining[0].ssrc; + for (const PacketInfo& packet_info : remaining) { + if (packet_info.ssrc != ssrc) { + break; + } ++number_of_packets_for_ssrc; } - write_report_for_ssrc(packets_); + write_report_for_ssrc(remaining.subview(0, number_of_packets_for_ssrc)); + remaining = remaining.subview(number_of_packets_for_ssrc); } // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -234,9 +223,6 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position, report_timestamp_compact_ntp_); *position += 4; - if (*position != position_end) { - return false; - } return true; } @@ -257,6 +243,15 @@ size_t CongestionControlFeedback::BlockLength() const { uint32_t ssrc = packets_.front().ssrc; uint16_t first_sequence_number = packets_.front().sequence_number; + for (size_t i = 0; i < packets_.size(); ++i) { + if (packets_[i].ssrc != ssrc) { + uint16_t number_of_packets = + packets_[i - 1].sequence_number - first_sequence_number + 1; + total_size += increase_size_per_ssrc(number_of_packets); + ssrc = packets_[i].ssrc; + first_sequence_number = packets_[i].sequence_number; + } + } uint16_t number_of_packets = packets_.back().sequence_number - first_sequence_number + 1; total_size += increase_size_per_ssrc(number_of_packets); @@ -264,7 +259,7 @@ size_t CongestionControlFeedback::BlockLength() const { return total_size; } -bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) { +bool CongestionControlFeedback::Parse(const rtcp::CommonHeader& packet) { const uint8_t* payload = packet.payload(); const uint8_t* payload_end = packet.payload() + packet.payload_size_bytes(); @@ -300,11 +295,10 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) { uint16_t seq_no = base_seqno + i; bool received = (packet_info & 0x8000); - packets_.push_back(PacketInfo{ssrc, seq_no, - received - ? AtoToTimeDelta(packet_info) - : std::numeric_limits::min(), - ToEcnMarking(packet_info)}); + packets_.push_back( + {ssrc, seq_no, + received ? AtoToTimeDelta(packet_info) : TimeDelta::MinusInfinity(), + ToEcnMarking(packet_info)}); } if (num_reports % 2) { // 2 bytes padding @@ -312,4 +306,6 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) { } } return payload == payload_end; -} \ No newline at end of file +} +} // namespace rtcp +} // namespace webrtc diff --git a/src/qos/congestion_control_feedback.h b/src/qos/congestion_control_feedback.h index fc3869c..641b62e 100644 --- a/src/qos/congestion_control_feedback.h +++ b/src/qos/congestion_control_feedback.h @@ -1,20 +1,28 @@ /* - * @Author: DI JUNKUN - * @Date: 2024-12-18 - * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. + * Copyright (c) 2024 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 _CONGESTION_CONTROL_FEEDBACK_H_ -#define _CONGESTION_CONTROL_FEEDBACK_H_ +#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_ +#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_ #include #include -#include #include -#include "enc_mark.h" +#include "api/array_view.h" +#include "api/units/time_delta.h" +#include "common_header.h" +#include "rtc_base/network/ecn_marking.h" #include "rtp_feedback.h" +namespace webrtc { +namespace rtcp { + // Congestion control feedback message as specified in // https://www.rfc-editor.org/rfc/rfc8888.html class CongestionControlFeedback : public RtpFeedback { @@ -24,8 +32,8 @@ class CongestionControlFeedback : public RtpFeedback { uint16_t sequence_number = 0; // Time offset from report timestamp. Minus infinity if the packet has not // been received. - int64_t arrival_time_offset = std::numeric_limits::min(); - EcnMarking ecn = EcnMarking::kNotEct; + TimeDelta arrival_time_offset = TimeDelta::MinusInfinity(); + rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct; }; static constexpr uint8_t kFeedbackMessageType = 11; @@ -37,9 +45,9 @@ class CongestionControlFeedback : public RtpFeedback { uint32_t report_timestamp_compact_ntp); CongestionControlFeedback() = default; - bool Parse(const RtcpCommonHeader& packet); + bool Parse(const CommonHeader& packet); - std::vector packets() const { return packets_; } + rtc::ArrayView packets() const { return packets_; } uint32_t report_timestamp_compact_ntp() const { return report_timestamp_compact_ntp_; @@ -55,4 +63,7 @@ class CongestionControlFeedback : public RtpFeedback { uint32_t report_timestamp_compact_ntp_ = 0; }; -#endif \ No newline at end of file +} // namespace rtcp +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_ diff --git a/src/qos/congestion_control_feedback_tracker.cpp b/src/qos/congestion_control_feedback_tracker.cc similarity index 61% rename from src/qos/congestion_control_feedback_tracker.cpp rename to src/qos/congestion_control_feedback_tracker.cc index 5f33271..749c546 100644 --- a/src/qos/congestion_control_feedback_tracker.cpp +++ b/src/qos/congestion_control_feedback_tracker.cc @@ -1,14 +1,29 @@ + +/* + * Copyright (c) 2024 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 "congestion_control_feedback_tracker.h" -#include #include #include #include -#include "log.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "congestion_control_feedback.h" +#include "rtp_packet_received.h" + +namespace webrtc { void CongestionControlFeedbackTracker::ReceivedPacket( - RtpPacketReceived& packet) { + const RtpPacketReceived& packet) { int64_t unwrapped_sequence_number = unwrapper_.Unwrap(packet.SequenceNumber()); if (last_sequence_number_in_feedback_ && @@ -26,13 +41,13 @@ void CongestionControlFeedbackTracker::ReceivedPacket( // received. last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1; } - packets_.push_back({packet.Ssrc(), unwrapped_sequence_number, - packet.arrival_time(), packet.ecn()}); + packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number, + packet.arrival_time(), packet.ecn()); } void CongestionControlFeedbackTracker::AddPacketsToFeedback( - int64_t feedback_time, - std::vector& packet_feedback) { + Timestamp feedback_time, + std::vector& packet_feedback) { if (packets_.empty()) { return; } @@ -51,17 +66,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback( for (int64_t sequence_number = *last_sequence_number_in_feedback_ + 1; sequence_number <= packets_.back().unwrapped_sequence_number; ++sequence_number) { - if (packet_it == packets_.end()) { - LOG_FATAL("Invalid packet_it"); - return; - } - if (ssrc != packet_it->ssrc) { - LOG_FATAL("Invalid ssrc"); - return; - } - - EcnMarking ecn = EcnMarking::kNotEct; - int64_t arrival_time_offset = std::numeric_limits::min(); + rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct; + TimeDelta arrival_time_offset = TimeDelta::MinusInfinity(); if (sequence_number == packet_it->unwrapped_sequence_number) { arrival_time_offset = feedback_time - packet_it->arrival_time; @@ -70,22 +76,24 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback( while (packet_it != packets_.end() && packet_it->unwrapped_sequence_number == sequence_number) { // According to RFC 8888: - // If duplicate copies of a particular RTP packet are received, then the - // arrival time of the first copy to arrive MUST be reported. If any of - // the copies of the duplicated packet are ECN-CE marked, then an ECN-CE - // mark MUST be reported for that packet; otherwise, the ECN mark of the - // first copy to arrive is reported. - if (packet_it->ecn == EcnMarking::kCe) { - ecn = EcnMarking::kCe; + // If duplicate copies of a particular RTP packet are received, then + // the arrival time of the first copy to arrive MUST be reported. If + // any of the copies of the duplicated packet are ECN-CE marked, then + // an ECN-CE mark MUST be reported for that packet; otherwise, the ECN + // mark of the first copy to arrive is reported. + if (packet_it->ecn == rtc::EcnMarking::kCe) { + ecn = rtc::EcnMarking::kCe; } - LOG_WARN("Received duplicate packet ssrc: {} seq: {} ecn: {}", ssrc, + LOG_WARN("Received duplicate packet ssrc:{} seq:{} ecn:{}", ssrc, static_cast(sequence_number), static_cast(ecn)); ++packet_it; } } // else - the packet has not been received yet. - packet_feedback.push_back({ssrc, static_cast(sequence_number), - arrival_time_offset, ecn}); + packet_feedback.emplace_back(ssrc, static_cast(sequence_number), + arrival_time_offset, ecn); } last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number; packets_.clear(); } + +} // namespace webrtc diff --git a/src/qos/congestion_control_feedback_tracker.h b/src/qos/congestion_control_feedback_tracker.h index 760e066..3f95584 100644 --- a/src/qos/congestion_control_feedback_tracker.h +++ b/src/qos/congestion_control_feedback_tracker.h @@ -1,40 +1,50 @@ /* - * @Author: DI JUNKUN - * @Date: 2024-12-18 - * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. + * Copyright (c) 2024 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 MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_ -#ifndef _CONGESTION_CONTROL_FEEDBACK_TRACKER_H_ -#define _CONGESTION_CONTROL_FEEDBACK_TRACKER_H_ - +#include #include #include +#include "api/units/timestamp.h" #include "congestion_control_feedback.h" -#include "enc_mark.h" +#include "rtc_base/network/ecn_marking.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" #include "rtp_packet_received.h" -#include "sequence_number_unwrapper.h" +namespace webrtc { +// CongestionControlFeedbackTracker is reponsible for creating and keeping track +// of feedback sent for a specific SSRC when feedback is sent according to +// https://datatracker.ietf.org/doc/rfc8888/ class CongestionControlFeedbackTracker { public: CongestionControlFeedbackTracker() = default; - void ReceivedPacket(RtpPacketReceived& packet); + void ReceivedPacket(const RtpPacketReceived& packet); // Adds received packets to `packet_feedback` // RTP sequence numbers are continous from the last created feedback unless // reordering has occured between feedback packets. If so, the sequence // number range may overlap with previousely sent feedback. void AddPacketsToFeedback( - int64_t feedback_time, - std::vector& packet_feedback); + Timestamp feedback_time, + std::vector& + packet_feedback); private: struct PacketInfo { uint32_t ssrc; int64_t unwrapped_sequence_number = 0; - int64_t arrival_time; - EcnMarking ecn = EcnMarking::kNotEct; + Timestamp arrival_time; + rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct; }; std::optional last_sequence_number_in_feedback_; @@ -43,4 +53,6 @@ class CongestionControlFeedbackTracker { std::vector packets_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_ diff --git a/src/qos/delay_based_bwe.cpp b/src/qos/delay_based_bwe.cc similarity index 66% rename from src/qos/delay_based_bwe.cpp rename to src/qos/delay_based_bwe.cc index d32b799..dc5a2af 100644 --- a/src/qos/delay_based_bwe.cpp +++ b/src/qos/delay_based_bwe.cc @@ -17,11 +17,20 @@ #include #include -#include "api/transport/network_types.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "bwe_defines.h" +#include "inter_arrival_delta.h" +#include "log.h" +#include "network_types.h" +#include "trendline_estimator.h" +namespace webrtc { namespace { -constexpr int64_t kStreamTimeOut = int64_t::Seconds(2); -constexpr int64_t kSendTimeGroupLength = int64_t::Millis(5); +constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2); +constexpr TimeDelta kSendTimeGroupLength = TimeDelta::Millis(5); // This ssrc is used to fulfill the current API but will be removed // after the API has been changed. @@ -30,72 +39,40 @@ constexpr uint32_t kFixedSsrc = 0; constexpr char BweSeparateAudioPacketsSettings::kKey[]; -BweSeparateAudioPacketsSettings::BweSeparateAudioPacketsSettings( - const FieldTrialsView* key_value_config) { - Parser()->Parse( - key_value_config->Lookup(BweSeparateAudioPacketsSettings::kKey)); -} - -std::unique_ptr -BweSeparateAudioPacketsSettings::Parser() { - return StructParametersParser::Create( // - "enabled", &enabled, // - "packet_threshold", &packet_threshold, // - "time_threshold", &time_threshold); -} - DelayBasedBwe::Result::Result() : updated(false), probe(false), - target_bitrate(int64_t::Zero()), + target_bitrate(DataRate::Zero()), recovered_from_overuse(false), delay_detector_state(BandwidthUsage::kBwNormal) {} -DelayBasedBwe::DelayBasedBwe(const FieldTrialsView* key_value_config, - RtcEventLog* event_log, - NetworkStatePredictor* network_state_predictor) - : event_log_(event_log), - key_value_config_(key_value_config), - separate_audio_(key_value_config), - audio_packets_since_last_video_(0), - last_video_packet_recv_time_(int64_t::MinusInfinity()), - network_state_predictor_(network_state_predictor), - video_delay_detector_( - new TrendlineEstimator(key_value_config_, network_state_predictor_)), - audio_delay_detector_( - new TrendlineEstimator(key_value_config_, network_state_predictor_)), +DelayBasedBwe::DelayBasedBwe() + : audio_packets_since_last_video_(0), + last_video_packet_recv_time_(Timestamp::MinusInfinity()), + last_seen_packet_(Timestamp::MinusInfinity()), + video_delay_detector_(new TrendlineEstimator()), + audio_delay_detector_(new TrendlineEstimator()), active_delay_detector_(video_delay_detector_.get()), - last_seen_packet_(int64_t::MinusInfinity()), uma_recorded_(false), - rate_control_(*key_value_config, /*send_side=*/true), - prev_bitrate_(int64_t::Zero()), - prev_state_(BandwidthUsage::kBwNormal) { - RTC_LOG(LS_INFO) - << "Initialized DelayBasedBwe with separate audio overuse detection" - << separate_audio_.Parser()->Encode(); -} + rate_control_(true), + prev_bitrate_(DataRate::Zero()), + prev_state_(BandwidthUsage::kBwNormal) {} DelayBasedBwe::~DelayBasedBwe() {} DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector( - const TransportPacketsFeedback& msg, std::optional acked_bitrate, - std::optional probe_bitrate, - std::optional network_estimate, bool in_alr) { - RTC_DCHECK_RUNS_SERIALIZED(&network_race_); - + const TransportPacketsFeedback& msg, std::optional acked_bitrate, + std::optional probe_bitrate, bool in_alr) { auto packet_feedback_vector = msg.SortedByReceiveTime(); // TODO(holmer): An empty feedback vector here likely means that // all acks were too late and that the send time history had // timed out. We should reduce the rate when this occurs. if (packet_feedback_vector.empty()) { - RTC_LOG(LS_WARNING) << "Very late feedback received."; + LOG_WARN("Very late feedback received"); return DelayBasedBwe::Result(); } if (!uma_recorded_) { - RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, - BweNames::kSendSideTransportSeqNum, - BweNames::kBweNamesMax); uma_recorded_ = true; } bool delayed_feedback = true; @@ -117,14 +94,12 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector( return Result(); } rate_control_.SetInApplicationLimitedRegion(in_alr); - rate_control_.SetNetworkStateEstimate(network_estimate); return MaybeUpdateEstimate(acked_bitrate, probe_bitrate, - std::move(network_estimate), recovered_from_overuse, in_alr, msg.feedback_time); } void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, - int64_t at_time) { + Timestamp at_time) { // Reset if the stream has timed out. if (last_seen_packet_.IsInfinite() || at_time - last_seen_packet_ > kStreamTimeOut) { @@ -133,10 +108,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, audio_inter_arrival_delta_ = std::make_unique(kSendTimeGroupLength); - video_delay_detector_.reset( - new TrendlineEstimator(key_value_config_, network_state_predictor_)); - audio_delay_detector_.reset( - new TrendlineEstimator(key_value_config_, network_state_predictor_)); + video_delay_detector_.reset(new TrendlineEstimator()); + audio_delay_detector_.reset(new TrendlineEstimator()); active_delay_detector_ = video_delay_detector_.get(); } last_seen_packet_ = at_time; @@ -163,8 +136,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, } DataSize packet_size = packet_feedback.sent_packet.size; - int64_t send_delta = int64_t::Zero(); - int64_t recv_delta = int64_t::Zero(); + TimeDelta send_delta = TimeDelta::Zero(); + TimeDelta recv_delta = TimeDelta::Zero(); int size_delta = 0; InterArrivalDelta* inter_arrival_for_packet = @@ -182,16 +155,16 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, packet_size.bytes(), calculated_deltas); } -int64_t DelayBasedBwe::TriggerOveruse(int64_t at_time, - std::optional link_capacity) { +DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time, + std::optional link_capacity) { RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity); return rate_control_.Update(input, at_time); } DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( - std::optional acked_bitrate, std::optional probe_bitrate, - std::optional /* state_estimate */, - bool recovered_from_overuse, bool /* in_alr */, int64_t at_time) { + std::optional acked_bitrate, + std::optional probe_bitrate, bool recovered_from_overuse, + bool /* in_alr */, Timestamp at_time) { Result result; // Currently overusing the bandwidth. @@ -226,12 +199,7 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( BandwidthUsage detector_state = active_delay_detector_->State(); if ((result.updated && prev_bitrate_ != result.target_bitrate) || detector_state != prev_state_) { - int64_t bitrate = result.updated ? result.target_bitrate : prev_bitrate_; - - if (event_log_) { - event_log_->Log(std::make_unique( - bitrate.bps(), detector_state)); - } + DataRate bitrate = result.updated ? result.target_bitrate : prev_bitrate_; prev_bitrate_ = bitrate; prev_state_ = detector_state; @@ -241,26 +209,24 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( return result; } -bool DelayBasedBwe::UpdateEstimate(int64_t at_time, - std::optional acked_bitrate, - int64_t* target_rate) { +bool DelayBasedBwe::UpdateEstimate(Timestamp at_time, + std::optional acked_bitrate, + DataRate* target_rate) { const RateControlInput input(active_delay_detector_->State(), acked_bitrate); *target_rate = rate_control_.Update(input, at_time); return rate_control_.ValidEstimate(); } -void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt) { +void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) { rate_control_.SetRtt(avg_rtt); } bool DelayBasedBwe::LatestEstimate(std::vector* ssrcs, - int64_t* bitrate) const { + DataRate* bitrate) const { // Currently accessed from both the process thread (see // ModuleRtpRtcpImpl::Process()) and the configuration thread (see // Call::GetStats()). Should in the future only be accessed from a single // thread. - RTC_DCHECK(ssrcs); - RTC_DCHECK(bitrate); if (!rate_control_.ValidEstimate()) return false; *ssrcs = {kFixedSsrc}; @@ -268,18 +234,19 @@ bool DelayBasedBwe::LatestEstimate(std::vector* ssrcs, return true; } -void DelayBasedBwe::SetStartBitrate(int64_t start_bitrate) { - RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: " - << ToString(start_bitrate); +void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) { + LOG_INFO("BWE Setting start bitrate to: {}", ToString(start_bitrate)); rate_control_.SetStartBitrate(start_bitrate); } -void DelayBasedBwe::SetMinBitrate(int64_t min_bitrate) { +void DelayBasedBwe::SetMinBitrate(DataRate min_bitrate) { // Called from both the configuration thread and the network thread. Shouldn't // be called from the network thread in the future. rate_control_.SetMinBitrate(min_bitrate); } -int64_t DelayBasedBwe::GetExpectedBwePeriod() const { +TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const { return rate_control_.GetExpectedBandwidthPeriod(); } + +} // namespace webrtc diff --git a/src/qos/delay_based_bwe.h b/src/qos/delay_based_bwe.h index fb714d3..4a292bf 100644 --- a/src/qos/delay_based_bwe.h +++ b/src/qos/delay_based_bwe.h @@ -1,11 +1,15 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-14 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * 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. */ -#ifndef _DELAY_BASED_BWE_H_ -#define _DELAY_BASED_BWE_H_ +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_ #include @@ -13,27 +17,29 @@ #include #include +#include "aimd_rate_control.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "bandwidth_usage.h" +#include "delay_increase_detector_interface.h" +#include "inter_arrival.h" +#include "inter_arrival_delta.h" +#include "link_capacity_estimator.h" #include "network_types.h" +#include "probe_bitrate_estimator.h" -enum class BandwidthUsage { - kBwNormal = 0, - kBwUnderusing = 1, - kBwOverusing = 2, - kLast -}; +namespace webrtc { +class RtcEventLog; struct BweSeparateAudioPacketsSettings { static constexpr char kKey[] = "WebRTC-Bwe-SeparateAudioPackets"; BweSeparateAudioPacketsSettings() = default; - explicit BweSeparateAudioPacketsSettings( - const FieldTrialsView* key_value_config); bool enabled = false; int packet_threshold = 10; - int64_t time_threshold = int64_t::Seconds(1); - - std::unique_ptr Parser(); + TimeDelta time_threshold = TimeDelta::Seconds(1); }; class DelayBasedBwe { @@ -43,60 +49,51 @@ class DelayBasedBwe { ~Result() = default; bool updated; bool probe; - int64_t target_bitrate = int64_t::Zero(); + DataRate target_bitrate = DataRate::Zero(); bool recovered_from_overuse; BandwidthUsage delay_detector_state; }; - explicit DelayBasedBwe(const FieldTrialsView* key_value_config, - RtcEventLog* event_log, - NetworkStatePredictor* network_state_predictor); - - DelayBasedBwe() = delete; + DelayBasedBwe(); DelayBasedBwe(const DelayBasedBwe&) = delete; DelayBasedBwe& operator=(const DelayBasedBwe&) = delete; virtual ~DelayBasedBwe(); - Result IncomingPacketFeedbackVector( - const TransportPacketsFeedback& msg, std::optional acked_bitrate, - std::optional probe_bitrate, - std::optional network_estimate, bool in_alr); - void OnRttUpdate(int64_t avg_rtt); - bool LatestEstimate(std::vector* ssrcs, int64_t* bitrate) const; - void SetStartBitrate(int64_t start_bitrate); - void SetMinBitrate(int64_t min_bitrate); - int64_t GetExpectedBwePeriod() const; - int64_t TriggerOveruse(int64_t at_time, std::optional link_capacity); - int64_t last_estimate() const { return prev_bitrate_; } + Result IncomingPacketFeedbackVector(const TransportPacketsFeedback& msg, + std::optional acked_bitrate, + std::optional probe_bitrate, + bool in_alr); + void OnRttUpdate(TimeDelta avg_rtt); + bool LatestEstimate(std::vector* ssrcs, DataRate* bitrate) const; + void SetStartBitrate(DataRate start_bitrate); + void SetMinBitrate(DataRate min_bitrate); + TimeDelta GetExpectedBwePeriod() const; + DataRate TriggerOveruse(Timestamp at_time, + std::optional link_capacity); + DataRate last_estimate() const { return prev_bitrate_; } BandwidthUsage last_state() const { return prev_state_; } private: friend class GoogCcStatePrinter; void IncomingPacketFeedback(const PacketResult& packet_feedback, - int64_t at_time); - Result MaybeUpdateEstimate(std::optional acked_bitrate, - std::optional probe_bitrate, - std::optional state_estimate, + Timestamp at_time); + Result MaybeUpdateEstimate(std::optional acked_bitrate, + std::optional probe_bitrate, bool recovered_from_overuse, bool in_alr, - int64_t at_time); + Timestamp at_time); // Updates the current remote rate estimate and returns true if a valid // estimate exists. - bool UpdateEstimate(int64_t at_time, std::optional acked_bitrate, - int64_t* target_rate); - - rtc::RaceChecker network_race_; - RtcEventLog* const event_log_; - const FieldTrialsView* const key_value_config_; + bool UpdateEstimate(Timestamp at_time, std::optional acked_bitrate, + DataRate* target_rate); // Alternatively, run two separate overuse detectors for audio and video, // and fall back to the audio one if we haven't seen a video packet in a // while. BweSeparateAudioPacketsSettings separate_audio_; int64_t audio_packets_since_last_video_; - int64_t last_video_packet_recv_time_; + Timestamp last_video_packet_recv_time_; - NetworkStatePredictor* network_state_predictor_; std::unique_ptr video_inter_arrival_; std::unique_ptr video_inter_arrival_delta_; std::unique_ptr video_delay_detector_; @@ -105,11 +102,13 @@ class DelayBasedBwe { std::unique_ptr audio_delay_detector_; DelayIncreaseDetectorInterface* active_delay_detector_; - int64_t last_seen_packet_; + Timestamp last_seen_packet_; bool uma_recorded_; AimdRateControl rate_control_; - int64_t prev_bitrate_; + DataRate prev_bitrate_; BandwidthUsage prev_state_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_ diff --git a/src/qos/delay_increase_detector_interface.h b/src/qos/delay_increase_detector_interface.h new file mode 100644 index 0000000..d305426 --- /dev/null +++ b/src/qos/delay_increase_detector_interface.h @@ -0,0 +1,42 @@ +/* + * 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 MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_ + +#include + +#include + +#include "bandwidth_usage.h" + +namespace webrtc { + +class DelayIncreaseDetectorInterface { + public: + DelayIncreaseDetectorInterface() {} + virtual ~DelayIncreaseDetectorInterface() {} + + DelayIncreaseDetectorInterface(const DelayIncreaseDetectorInterface&) = + delete; + DelayIncreaseDetectorInterface& operator=( + const DelayIncreaseDetectorInterface&) = delete; + + // Update the detector with a new sample. The deltas should represent deltas + // between timestamp groups as defined by the InterArrival class. + virtual void Update(double recv_delta_ms, double send_delta_ms, + int64_t send_time_ms, int64_t arrival_time_ms, + size_t packet_size, bool calculated_deltas) = 0; + + virtual BandwidthUsage State() const = 0; +}; + +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_INCREASE_DETECTOR_INTERFACE_H_ diff --git a/src/qos/inter_arrival.cc b/src/qos/inter_arrival.cc new file mode 100644 index 0000000..4c9b6aa --- /dev/null +++ b/src/qos/inter_arrival.cc @@ -0,0 +1,150 @@ +/* + * 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 "inter_arrival.h" + +#include "log.h" +#include "module_common_types_public.h" + +namespace webrtc { + +static const int kBurstDeltaThresholdMs = 5; +static const int kMaxBurstDurationMs = 100; + +InterArrival::InterArrival(uint32_t timestamp_group_length_ticks, + double timestamp_to_ms_coeff) + : kTimestampGroupLengthTicks(timestamp_group_length_ticks), + current_timestamp_group_(), + prev_timestamp_group_(), + timestamp_to_ms_coeff_(timestamp_to_ms_coeff), + num_consecutive_reordered_packets_(0) {} + +bool InterArrival::ComputeDeltas(uint32_t timestamp, int64_t arrival_time_ms, + int64_t system_time_ms, size_t packet_size, + uint32_t* timestamp_delta, + int64_t* arrival_time_delta_ms, + int* packet_size_delta) { + bool calculated_deltas = false; + if (current_timestamp_group_.IsFirstPacket()) { + // We don't have enough data to update the filter, so we store it until we + // have two frames of data to process. + current_timestamp_group_.timestamp = timestamp; + current_timestamp_group_.first_timestamp = timestamp; + current_timestamp_group_.first_arrival_ms = arrival_time_ms; + } else if (!PacketInOrder(timestamp)) { + return false; + } else if (NewTimestampGroup(arrival_time_ms, timestamp)) { + // First packet of a later frame, the previous frame sample is ready. + if (prev_timestamp_group_.complete_time_ms >= 0) { + *timestamp_delta = + current_timestamp_group_.timestamp - prev_timestamp_group_.timestamp; + *arrival_time_delta_ms = current_timestamp_group_.complete_time_ms - + prev_timestamp_group_.complete_time_ms; + // Check system time differences to see if we have an unproportional jump + // in arrival time. In that case reset the inter-arrival computations. + int64_t system_time_delta_ms = + current_timestamp_group_.last_system_time_ms - + prev_timestamp_group_.last_system_time_ms; + if (*arrival_time_delta_ms - system_time_delta_ms >= + kArrivalTimeOffsetThresholdMs) { + LOG_WARN( + "The arrival time clock offset has changed (diff = {} ms), " + "resetting", + *arrival_time_delta_ms - system_time_delta_ms); + Reset(); + return false; + } + if (*arrival_time_delta_ms < 0) { + // The group of packets has been reordered since receiving its local + // arrival timestamp. + ++num_consecutive_reordered_packets_; + if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) { + LOG_WARN( + "Packets are being reordered on the path from the socket to the " + "bandwidth estimator. Ignoring this packet for bandwidth " + "estimation, resetting"); + Reset(); + } + return false; + } else { + num_consecutive_reordered_packets_ = 0; + } + *packet_size_delta = static_cast(current_timestamp_group_.size) - + static_cast(prev_timestamp_group_.size); + calculated_deltas = true; + } + prev_timestamp_group_ = current_timestamp_group_; + // The new timestamp is now the current frame. + current_timestamp_group_.first_timestamp = timestamp; + current_timestamp_group_.timestamp = timestamp; + current_timestamp_group_.first_arrival_ms = arrival_time_ms; + current_timestamp_group_.size = 0; + } else { + current_timestamp_group_.timestamp = + LatestTimestamp(current_timestamp_group_.timestamp, timestamp); + } + // Accumulate the frame size. + current_timestamp_group_.size += packet_size; + current_timestamp_group_.complete_time_ms = arrival_time_ms; + current_timestamp_group_.last_system_time_ms = system_time_ms; + + return calculated_deltas; +} + +bool InterArrival::PacketInOrder(uint32_t timestamp) { + if (current_timestamp_group_.IsFirstPacket()) { + return true; + } else { + // Assume that a diff which is bigger than half the timestamp interval + // (32 bits) must be due to reordering. This code is almost identical to + // that in IsNewerTimestamp() in module_common_types.h. + uint32_t timestamp_diff = + timestamp - current_timestamp_group_.first_timestamp; + return timestamp_diff < 0x80000000; + } +} + +// Assumes that `timestamp` is not reordered compared to +// `current_timestamp_group_`. +bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms, + uint32_t timestamp) const { + if (current_timestamp_group_.IsFirstPacket()) { + return false; + } else if (BelongsToBurst(arrival_time_ms, timestamp)) { + return false; + } else { + uint32_t timestamp_diff = + timestamp - current_timestamp_group_.first_timestamp; + return timestamp_diff > kTimestampGroupLengthTicks; + } +} + +bool InterArrival::BelongsToBurst(int64_t arrival_time_ms, + uint32_t timestamp) const { + int64_t arrival_time_delta_ms = + arrival_time_ms - current_timestamp_group_.complete_time_ms; + uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp; + int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5; + if (ts_delta_ms == 0) return true; + int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms; + if (propagation_delta_ms < 0 && + arrival_time_delta_ms <= kBurstDeltaThresholdMs && + arrival_time_ms - current_timestamp_group_.first_arrival_ms < + kMaxBurstDurationMs) + return true; + return false; +} + +void InterArrival::Reset() { + num_consecutive_reordered_packets_ = 0; + current_timestamp_group_ = TimestampGroup(); + prev_timestamp_group_ = TimestampGroup(); +} +} // namespace webrtc diff --git a/src/qos/inter_arrival.h b/src/qos/inter_arrival.h new file mode 100644 index 0000000..c395af2 --- /dev/null +++ b/src/qos/inter_arrival.h @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ + +#include +#include + +namespace webrtc { + +// Helper class to compute the inter-arrival time delta and the size delta +// between two timestamp groups. A timestamp is a 32 bit unsigned number with +// a client defined rate. +class InterArrival { + public: + // After this many packet groups received out of order InterArrival will + // reset, assuming that clocks have made a jump. + static constexpr int kReorderedResetThreshold = 3; + static constexpr int64_t kArrivalTimeOffsetThresholdMs = 3000; + + // A timestamp group is defined as all packets with a timestamp which are at + // most timestamp_group_length_ticks older than the first timestamp in that + // group. + InterArrival(uint32_t timestamp_group_length_ticks, + double timestamp_to_ms_coeff); + + InterArrival() = delete; + InterArrival(const InterArrival&) = delete; + InterArrival& operator=(const InterArrival&) = delete; + + // This function returns true if a delta was computed, or false if the current + // group is still incomplete or if only one group has been completed. + // `timestamp` is the timestamp. + // `arrival_time_ms` is the local time at which the packet arrived. + // `packet_size` is the size of the packet. + // `timestamp_delta` (output) is the computed timestamp delta. + // `arrival_time_delta_ms` (output) is the computed arrival-time delta. + // `packet_size_delta` (output) is the computed size delta. + bool ComputeDeltas(uint32_t timestamp, + int64_t arrival_time_ms, + int64_t system_time_ms, + size_t packet_size, + uint32_t* timestamp_delta, + int64_t* arrival_time_delta_ms, + int* packet_size_delta); + + private: + struct TimestampGroup { + TimestampGroup() + : size(0), + first_timestamp(0), + timestamp(0), + first_arrival_ms(-1), + complete_time_ms(-1) {} + + bool IsFirstPacket() const { return complete_time_ms == -1; } + + size_t size; + uint32_t first_timestamp; + uint32_t timestamp; + int64_t first_arrival_ms; + int64_t complete_time_ms; + int64_t last_system_time_ms; + }; + + // Returns true if the packet with timestamp `timestamp` arrived in order. + bool PacketInOrder(uint32_t timestamp); + + // Returns true if the last packet was the end of the current batch and the + // packet with `timestamp` is the first of a new batch. + bool NewTimestampGroup(int64_t arrival_time_ms, uint32_t timestamp) const; + + bool BelongsToBurst(int64_t arrival_time_ms, uint32_t timestamp) const; + + void Reset(); + + const uint32_t kTimestampGroupLengthTicks; + TimestampGroup current_timestamp_group_; + TimestampGroup prev_timestamp_group_; + double timestamp_to_ms_coeff_; + int num_consecutive_reordered_packets_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ diff --git a/src/qos/inter_arrival_delta.cc b/src/qos/inter_arrival_delta.cc new file mode 100644 index 0000000..c908a00 --- /dev/null +++ b/src/qos/inter_arrival_delta.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2020 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 "inter_arrival_delta.h" + +#include +#include + +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "log.h" + +namespace webrtc { + +static constexpr TimeDelta kBurstDeltaThreshold = TimeDelta::Millis(5); +static constexpr TimeDelta kMaxBurstDuration = TimeDelta::Millis(100); +constexpr TimeDelta InterArrivalDelta::kArrivalTimeOffsetThreshold; + +InterArrivalDelta::InterArrivalDelta(TimeDelta send_time_group_length) + : send_time_group_length_(send_time_group_length), + current_timestamp_group_(), + prev_timestamp_group_(), + num_consecutive_reordered_packets_(0) {} + +bool InterArrivalDelta::ComputeDeltas(Timestamp send_time, + Timestamp arrival_time, + Timestamp system_time, size_t packet_size, + TimeDelta* send_time_delta, + TimeDelta* arrival_time_delta, + int* packet_size_delta) { + bool calculated_deltas = false; + if (current_timestamp_group_.IsFirstPacket()) { + // We don't have enough data to update the filter, so we store it until we + // have two frames of data to process. + current_timestamp_group_.send_time = send_time; + current_timestamp_group_.first_send_time = send_time; + current_timestamp_group_.first_arrival = arrival_time; + } else if (current_timestamp_group_.first_send_time > send_time) { + // Reordered packet. + return false; + } else if (NewTimestampGroup(arrival_time, send_time)) { + // First packet of a later send burst, the previous packets sample is ready. + if (prev_timestamp_group_.complete_time.IsFinite()) { + *send_time_delta = + current_timestamp_group_.send_time - prev_timestamp_group_.send_time; + *arrival_time_delta = current_timestamp_group_.complete_time - + prev_timestamp_group_.complete_time; + + TimeDelta system_time_delta = current_timestamp_group_.last_system_time - + prev_timestamp_group_.last_system_time; + + if (*arrival_time_delta - system_time_delta >= + kArrivalTimeOffsetThreshold) { + LOG_WARN( + "The arrival time clock offset has changed (diff = {} ms), " + "resetting.", + arrival_time_delta->ms() - system_time_delta.ms()); + Reset(); + return false; + } + if (*arrival_time_delta < TimeDelta::Zero()) { + // The group of packets has been reordered since receiving its local + // arrival timestamp. + ++num_consecutive_reordered_packets_; + if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) { + LOG_WARN( + "Packets between send burst arrived out of order, resetting: " + "arrival_time_delta_ms={}, send_time_delta_ms={}", + arrival_time_delta->ms(), send_time_delta->ms()); + Reset(); + } + return false; + } else { + num_consecutive_reordered_packets_ = 0; + } + *packet_size_delta = static_cast(current_timestamp_group_.size) - + static_cast(prev_timestamp_group_.size); + calculated_deltas = true; + } + prev_timestamp_group_ = current_timestamp_group_; + // The new timestamp is now the current frame. + current_timestamp_group_.first_send_time = send_time; + current_timestamp_group_.send_time = send_time; + current_timestamp_group_.first_arrival = arrival_time; + current_timestamp_group_.size = 0; + } else { + current_timestamp_group_.send_time = + std::max(current_timestamp_group_.send_time, send_time); + } + // Accumulate the frame size. + current_timestamp_group_.size += packet_size; + current_timestamp_group_.complete_time = arrival_time; + current_timestamp_group_.last_system_time = system_time; + + return calculated_deltas; +} + +// Assumes that `timestamp` is not reordered compared to +// `current_timestamp_group_`. +bool InterArrivalDelta::NewTimestampGroup(Timestamp arrival_time, + Timestamp send_time) const { + if (current_timestamp_group_.IsFirstPacket()) { + return false; + } else if (BelongsToBurst(arrival_time, send_time)) { + return false; + } else { + return send_time - current_timestamp_group_.first_send_time > + send_time_group_length_; + } +} + +bool InterArrivalDelta::BelongsToBurst(Timestamp arrival_time, + Timestamp send_time) const { + TimeDelta arrival_time_delta = + arrival_time - current_timestamp_group_.complete_time; + TimeDelta send_time_delta = send_time - current_timestamp_group_.send_time; + if (send_time_delta.IsZero()) return true; + TimeDelta propagation_delta = arrival_time_delta - send_time_delta; + if (propagation_delta < TimeDelta::Zero() && + arrival_time_delta <= kBurstDeltaThreshold && + arrival_time - current_timestamp_group_.first_arrival < kMaxBurstDuration) + return true; + return false; +} + +void InterArrivalDelta::Reset() { + num_consecutive_reordered_packets_ = 0; + current_timestamp_group_ = SendTimeGroup(); + prev_timestamp_group_ = SendTimeGroup(); +} +} // namespace webrtc diff --git a/src/qos/inter_arrival_delta.h b/src/qos/inter_arrival_delta.h new file mode 100644 index 0000000..d8aa882 --- /dev/null +++ b/src/qos/inter_arrival_delta.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 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 MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_ + +#include + +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +// Helper class to compute the inter-arrival time delta and the size delta +// between two send bursts. This code is branched from +// modules/remote_bitrate_estimator/inter_arrival. +class InterArrivalDelta { + public: + // After this many packet groups received out of order InterArrival will + // reset, assuming that clocks have made a jump. + static constexpr int kReorderedResetThreshold = 3; + static constexpr TimeDelta kArrivalTimeOffsetThreshold = + TimeDelta::Seconds(3); + + // A send time group is defined as all packets with a send time which are at + // most send_time_group_length older than the first timestamp in that + // group. + explicit InterArrivalDelta(TimeDelta send_time_group_length); + + InterArrivalDelta() = delete; + InterArrivalDelta(const InterArrivalDelta&) = delete; + InterArrivalDelta& operator=(const InterArrivalDelta&) = delete; + + // This function returns true if a delta was computed, or false if the current + // group is still incomplete or if only one group has been completed. + // `send_time` is the send time. + // `arrival_time` is the time at which the packet arrived. + // `packet_size` is the size of the packet. + // `timestamp_delta` (output) is the computed send time delta. + // `arrival_time_delta` (output) is the computed arrival-time delta. + // `packet_size_delta` (output) is the computed size delta. + bool ComputeDeltas(Timestamp send_time, Timestamp arrival_time, + Timestamp system_time, size_t packet_size, + TimeDelta* send_time_delta, TimeDelta* arrival_time_delta, + int* packet_size_delta); + + private: + struct SendTimeGroup { + SendTimeGroup() + : size(0), + first_send_time(Timestamp::MinusInfinity()), + send_time(Timestamp::MinusInfinity()), + first_arrival(Timestamp::MinusInfinity()), + complete_time(Timestamp::MinusInfinity()), + last_system_time(Timestamp::MinusInfinity()) {} + + bool IsFirstPacket() const { return complete_time.IsInfinite(); } + + size_t size; + Timestamp first_send_time; + Timestamp send_time; + Timestamp first_arrival; + Timestamp complete_time; + Timestamp last_system_time; + }; + + // Returns true if the last packet was the end of the current batch and the + // packet with `send_time` is the first of a new batch. + bool NewTimestampGroup(Timestamp arrival_time, Timestamp send_time) const; + + bool BelongsToBurst(Timestamp arrival_time, Timestamp send_time) const; + + void Reset(); + + const TimeDelta send_time_group_length_; + SendTimeGroup current_timestamp_group_; + SendTimeGroup prev_timestamp_group_; + int num_consecutive_reordered_packets_; +}; +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_INTER_ARRIVAL_DELTA_H_ diff --git a/src/qos/interval_budget.cpp b/src/qos/interval_budget.cc similarity index 92% rename from src/qos/interval_budget.cpp rename to src/qos/interval_budget.cc index e8b4140..abd69cc 100644 --- a/src/qos/interval_budget.cpp +++ b/src/qos/interval_budget.cc @@ -14,6 +14,9 @@ #include #include +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { namespace { constexpr int64_t kWindowMs = 500; } @@ -51,7 +54,7 @@ void IntervalBudget::UseBudget(size_t bytes) { } size_t IntervalBudget::bytes_remaining() const { - return static_cast(std::max(0, bytes_remaining_)); + return rtc::saturated_cast(std::max(0, bytes_remaining_)); } double IntervalBudget::budget_ratio() const { @@ -60,3 +63,5 @@ double IntervalBudget::budget_ratio() const { } int IntervalBudget::target_rate_kbps() const { return target_rate_kbps_; } + +} // namespace webrtc diff --git a/src/qos/interval_budget.h b/src/qos/interval_budget.h index 0d9894f..faeb1d8 100644 --- a/src/qos/interval_budget.h +++ b/src/qos/interval_budget.h @@ -1,15 +1,21 @@ /* - * @Author: DI JUNKUN - * @Date: 2025-01-14 - * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + * 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. */ -#ifndef _INTERVAL_BUDGET_H_ -#define _INTERVAL_BUDGET_H_ +#ifndef MODULES_PACING_INTERVAL_BUDGET_H_ +#define MODULES_PACING_INTERVAL_BUDGET_H_ #include #include +namespace webrtc { + // TODO(tschumim): Reflector IntervalBudget so that we can set a under- and // over-use budget in ms. class IntervalBudget { @@ -33,4 +39,6 @@ class IntervalBudget { bool can_build_up_underuse_; }; -#endif \ No newline at end of file +} // namespace webrtc + +#endif // MODULES_PACING_INTERVAL_BUDGET_H_ diff --git a/src/qos/link_capacity_estimator.cc b/src/qos/link_capacity_estimator.cc new file mode 100644 index 0000000..d824147 --- /dev/null +++ b/src/qos/link_capacity_estimator.cc @@ -0,0 +1,77 @@ +/* + * Copyright 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 "link_capacity_estimator.h" + +#include +#include + +#include "api/units/data_rate.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +LinkCapacityEstimator::LinkCapacityEstimator() {} + +DataRate LinkCapacityEstimator::UpperBound() const { + if (estimate_kbps_.has_value()) + return DataRate::KilobitsPerSec(estimate_kbps_.value() + + 3 * deviation_estimate_kbps()); + return DataRate::Infinity(); +} + +DataRate LinkCapacityEstimator::LowerBound() const { + if (estimate_kbps_.has_value()) + return DataRate::KilobitsPerSec( + std::max(0.0, estimate_kbps_.value() - 3 * deviation_estimate_kbps())); + return DataRate::Zero(); +} + +void LinkCapacityEstimator::Reset() { estimate_kbps_.reset(); } + +void LinkCapacityEstimator::OnOveruseDetected(DataRate acknowledged_rate) { + Update(acknowledged_rate, 0.05); +} + +void LinkCapacityEstimator::OnProbeRate(DataRate probe_rate) { + Update(probe_rate, 0.5); +} + +void LinkCapacityEstimator::Update(DataRate capacity_sample, double alpha) { + double sample_kbps = capacity_sample.kbps(); + if (!estimate_kbps_.has_value()) { + estimate_kbps_ = sample_kbps; + } else { + estimate_kbps_ = (1 - alpha) * estimate_kbps_.value() + alpha * sample_kbps; + } + // Estimate the variance of the link capacity estimate and normalize the + // variance with the link capacity estimate. + const double norm = std::max(estimate_kbps_.value(), 1.0); + double error_kbps = estimate_kbps_.value() - sample_kbps; + deviation_kbps_ = + (1 - alpha) * deviation_kbps_ + alpha * error_kbps * error_kbps / norm; + // 0.4 ~= 14 kbit/s at 500 kbit/s + // 2.5f ~= 35 kbit/s at 500 kbit/s + deviation_kbps_ = rtc::SafeClamp(deviation_kbps_, 0.4f, 2.5f); +} + +bool LinkCapacityEstimator::has_estimate() const { + return estimate_kbps_.has_value(); +} + +DataRate LinkCapacityEstimator::estimate() const { + return DataRate::KilobitsPerSec(*estimate_kbps_); +} + +double LinkCapacityEstimator::deviation_estimate_kbps() const { + // Calculate the max bit rate std dev given the normalized + // variance and the current throughput bitrate. The standard deviation will + // only be used if estimate_kbps_ has a value. + return sqrt(deviation_kbps_ * estimate_kbps_.value()); +} +} // namespace webrtc diff --git a/src/qos/link_capacity_estimator.h b/src/qos/link_capacity_estimator.h new file mode 100644 index 0000000..5c4cbd4 --- /dev/null +++ b/src/qos/link_capacity_estimator.h @@ -0,0 +1,39 @@ +/* + * Copyright 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 MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_ + +#include + +#include "api/units/data_rate.h" + +namespace webrtc { +class LinkCapacityEstimator { + public: + LinkCapacityEstimator(); + DataRate UpperBound() const; + DataRate LowerBound() const; + void Reset(); + void OnOveruseDetected(DataRate acknowledged_rate); + void OnProbeRate(DataRate probe_rate); + bool has_estimate() const; + DataRate estimate() const; + + private: + friend class GoogCcStatePrinter; + void Update(DataRate capacity_sample, double alpha); + + double deviation_estimate_kbps() const; + std::optional estimate_kbps_; + double deviation_kbps_ = 0.4; +}; +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_ diff --git a/src/qos/overuse_detector.cc b/src/qos/overuse_detector.cc new file mode 100644 index 0000000..9178dc8 --- /dev/null +++ b/src/qos/overuse_detector.cc @@ -0,0 +1,94 @@ +/* + * 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 "overuse_detector.h" + +#include +#include + +#include +#include + +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr double kMaxAdaptOffsetMs = 15.0; +constexpr double kOverUsingTimeThreshold = 10; +constexpr int kMaxNumDeltas = 60; +constexpr double kUp = 0.0087; +constexpr double kDown = 0.039; + +} // namespace + +OveruseDetector::OveruseDetector() = default; + +BandwidthUsage OveruseDetector::State() const { return hypothesis_; } + +BandwidthUsage OveruseDetector::Detect(double offset, double ts_delta, + int num_of_deltas, int64_t now_ms) { + if (num_of_deltas < 2) { + return BandwidthUsage::kBwNormal; + } + const double T = std::min(num_of_deltas, kMaxNumDeltas) * offset; + if (T > threshold_) { + if (time_over_using_ == -1) { + // Initialize the timer. Assume that we've been + // over-using half of the time since the previous + // sample. + time_over_using_ = ts_delta / 2; + } else { + // Increment timer + time_over_using_ += ts_delta; + } + overuse_counter_++; + if (time_over_using_ > kOverUsingTimeThreshold && overuse_counter_ > 1) { + if (offset >= prev_offset_) { + time_over_using_ = 0; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwOverusing; + } + } + } else if (T < -threshold_) { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwUnderusing; + } else { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwNormal; + } + prev_offset_ = offset; + + UpdateThreshold(T, now_ms); + + return hypothesis_; +} + +void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) { + if (last_update_ms_ == -1) last_update_ms_ = now_ms; + + if (fabs(modified_offset) > threshold_ + kMaxAdaptOffsetMs) { + // Avoid adapting the threshold to big latency spikes, caused e.g., + // by a sudden capacity drop. + last_update_ms_ = now_ms; + return; + } + + const double k = fabs(modified_offset) < threshold_ ? kDown : kUp; + const int64_t kMaxTimeDeltaMs = 100; + int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs); + threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms; + threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f); + last_update_ms_ = now_ms; +} + +} // namespace webrtc diff --git a/src/qos/overuse_detector.h b/src/qos/overuse_detector.h new file mode 100644 index 0000000..726a12f --- /dev/null +++ b/src/qos/overuse_detector.h @@ -0,0 +1,52 @@ +/* + * 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. + */ +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ + +#include + +#include "bandwidth_usage.h" + +namespace webrtc { + +class OveruseDetector { + public: + OveruseDetector(); + + OveruseDetector(const OveruseDetector&) = delete; + OveruseDetector& operator=(const OveruseDetector&) = delete; + + ~OveruseDetector() = default; + + // Update the detection state based on the estimated inter-arrival time delta + // offset. `timestamp_delta` is the delta between the last timestamp which the + // estimated offset is based on and the last timestamp on which the last + // offset was based on, representing the time between detector updates. + // `num_of_deltas` is the number of deltas the offset estimate is based on. + // Returns the state after the detection update. + BandwidthUsage Detect(double offset, double timestamp_delta, + int num_of_deltas, int64_t now_ms); + + // Returns the current detector state. + BandwidthUsage State() const; + + private: + void UpdateThreshold(double modified_offset, int64_t now_ms); + + double threshold_ = 12.5; + int64_t last_update_ms_ = -1; + double prev_offset_ = 0.0; + double time_over_using_ = -1; + int overuse_counter_ = 0; + BandwidthUsage hypothesis_ = BandwidthUsage::kBwNormal; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ diff --git a/src/qos/probe_bitrate_estimator.cc b/src/qos/probe_bitrate_estimator.cc new file mode 100644 index 0000000..6161ac8 --- /dev/null +++ b/src/qos/probe_bitrate_estimator.cc @@ -0,0 +1,169 @@ +/* + * 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 "probe_bitrate_estimator.h" + +#include +#include +#include + +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "log.h" +#include "network_types.h" + +namespace webrtc { +namespace { +// The minumum number of probes we need to receive feedback about in percent +// in order to have a valid estimate. +constexpr double kMinReceivedProbesRatio = .80; + +// The minumum number of bytes we need to receive feedback about in percent +// in order to have a valid estimate. +constexpr double kMinReceivedBytesRatio = .80; + +// The maximum |receive rate| / |send rate| ratio for a valid estimate. +constexpr float kMaxValidRatio = 2.0f; + +// The minimum |receive rate| / |send rate| ratio assuming that the link is +// not saturated, i.e. we assume that we will receive at least +// kMinRatioForUnsaturatedLink * |send rate| if |send rate| is less than the +// link capacity. +constexpr float kMinRatioForUnsaturatedLink = 0.9f; + +// The target utilization of the link. If we know true link capacity +// we'd like to send at 95% of that rate. +constexpr float kTargetUtilizationFraction = 0.95f; + +// The maximum time period over which the cluster history is retained. +// This is also the maximum time period beyond which a probing burst is not +// expected to last. +constexpr TimeDelta kMaxClusterHistory = TimeDelta::Seconds(1); + +// The maximum time interval between first and the last probe on a cluster +// on the sender side as well as the receive side. +constexpr TimeDelta kMaxProbeInterval = TimeDelta::Seconds(1); + +} // namespace + +ProbeBitrateEstimator::ProbeBitrateEstimator() {} + +ProbeBitrateEstimator::~ProbeBitrateEstimator() = default; + +std::optional ProbeBitrateEstimator::HandleProbeAndEstimateBitrate( + const PacketResult& packet_feedback) { + int cluster_id = packet_feedback.sent_packet.pacing_info.probe_cluster_id; + + EraseOldClusters(packet_feedback.receive_time); + + AggregatedCluster* cluster = &clusters_[cluster_id]; + + if (packet_feedback.sent_packet.send_time < cluster->first_send) { + cluster->first_send = packet_feedback.sent_packet.send_time; + } + if (packet_feedback.sent_packet.send_time > cluster->last_send) { + cluster->last_send = packet_feedback.sent_packet.send_time; + cluster->size_last_send = packet_feedback.sent_packet.size; + } + if (packet_feedback.receive_time < cluster->first_receive) { + cluster->first_receive = packet_feedback.receive_time; + cluster->size_first_receive = packet_feedback.sent_packet.size; + } + if (packet_feedback.receive_time > cluster->last_receive) { + cluster->last_receive = packet_feedback.receive_time; + } + cluster->size_total += packet_feedback.sent_packet.size; + cluster->num_probes += 1; + + int min_probes = + packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes * + kMinReceivedProbesRatio; + DataSize min_size = + DataSize::Bytes( + packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes) * + kMinReceivedBytesRatio; + if (cluster->num_probes < min_probes || cluster->size_total < min_size) + return std::nullopt; + + TimeDelta send_interval = cluster->last_send - cluster->first_send; + TimeDelta receive_interval = cluster->last_receive - cluster->first_receive; + + if (send_interval <= TimeDelta::Zero() || send_interval > kMaxProbeInterval || + receive_interval <= TimeDelta::Zero() || + receive_interval > kMaxProbeInterval) { + LOG_INFO( + "Probing unsuccessful, invalid send/receive interval [cluster id: {}] " + "[send interval: {}] [receive interval: {}]", + cluster_id, ToString(send_interval), ToString(receive_interval)); + + return std::nullopt; + } + // Since the `send_interval` does not include the time it takes to actually + // send the last packet the size of the last sent packet should not be + // included when calculating the send bitrate. + DataSize send_size = cluster->size_total - cluster->size_last_send; + DataRate send_rate = send_size / send_interval; + + // Since the `receive_interval` does not include the time it takes to + // actually receive the first packet the size of the first received packet + // should not be included when calculating the receive bitrate. + DataSize receive_size = cluster->size_total - cluster->size_first_receive; + DataRate receive_rate = receive_size / receive_interval; + + double ratio = receive_rate / send_rate; + if (ratio > kMaxValidRatio) { + LOG_INFO( + "Probing unsuccessful, receive/send ratio too high [cluster id: {}] " + "[send: {} / {} = {}] [receive: {} / {} = {} ] [ratio: {} / {} = {}> " + "kMaxValidRatio ({})]", + cluster_id, ToString(send_size), ToString(send_interval), + ToString(send_rate), ToString(receive_size), ToString(receive_interval), + ToString(receive_rate), ToString(receive_rate), ToString(send_rate), + ratio, kMaxValidRatio); + return std::nullopt; + } + LOG_INFO( + "Probing successful [cluster id: {}] [send: {} / {} = {} ] [receive: {} " + "/ {} = {}]", + cluster_id, ToString(send_size), ToString(send_interval), + ToString(send_rate), ToString(receive_size), ToString(receive_interval), + ToString(receive_rate)); + + DataRate res = std::min(send_rate, receive_rate); + // If we're receiving at significantly lower bitrate than we were sending at, + // it suggests that we've found the true capacity of the link. In this case, + // set the target bitrate slightly lower to not immediately overuse. + if (receive_rate < kMinRatioForUnsaturatedLink * send_rate) { + res = kTargetUtilizationFraction * receive_rate; + } + + estimated_data_rate_ = res; + return estimated_data_rate_; +} + +std::optional +ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrate() { + std::optional estimated_data_rate = estimated_data_rate_; + estimated_data_rate_.reset(); + return estimated_data_rate; +} + +void ProbeBitrateEstimator::EraseOldClusters(Timestamp timestamp) { + for (auto it = clusters_.begin(); it != clusters_.end();) { + if (it->second.last_receive + kMaxClusterHistory < timestamp) { + it = clusters_.erase(it); + } else { + ++it; + } + } +} +} // namespace webrtc diff --git a/src/qos/probe_bitrate_estimator.h b/src/qos/probe_bitrate_estimator.h new file mode 100644 index 0000000..1218510 --- /dev/null +++ b/src/qos/probe_bitrate_estimator.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_ + +#include +#include + +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/timestamp.h" +#include "network_types.h" + +namespace webrtc { +class RtcEventLog; + +class ProbeBitrateEstimator { + public: + explicit ProbeBitrateEstimator(); + ~ProbeBitrateEstimator(); + + // Should be called for every probe packet we receive feedback about. + // Returns the estimated bitrate if the probe completes a valid cluster. + std::optional HandleProbeAndEstimateBitrate( + const PacketResult& packet_feedback); + + std::optional FetchAndResetLastEstimatedBitrate(); + + private: + struct AggregatedCluster { + int num_probes = 0; + Timestamp first_send = Timestamp::PlusInfinity(); + Timestamp last_send = Timestamp::MinusInfinity(); + Timestamp first_receive = Timestamp::PlusInfinity(); + Timestamp last_receive = Timestamp::MinusInfinity(); + DataSize size_last_send = DataSize::Zero(); + DataSize size_first_receive = DataSize::Zero(); + DataSize size_total = DataSize::Zero(); + }; + + // Erases old cluster data that was seen before `timestamp`. + void EraseOldClusters(Timestamp timestamp); + + std::map clusters_; + std::optional estimated_data_rate_; +}; + +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_ diff --git a/src/qos/transport_feedback_adapter.cpp b/src/qos/transport_feedback_adapter.cpp index 564317b..9f0257f 100644 --- a/src/qos/transport_feedback_adapter.cpp +++ b/src/qos/transport_feedback_adapter.cpp @@ -127,7 +127,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback( packet_info.arrival_time_offset != std::numeric_limits::max()) { result.receive_time = current_offset_ - packet_info.arrival_time_offset; - supports_ecn &= packet_info.ecn != EcnMarking::kNotEct; + supports_ecn &= packet_info.ecn != rtc::EcnMarking::kNotEct; } result.ecn = packet_info.ecn; packet_result_vector.push_back(result); diff --git a/src/qos/transport_feedback_adapter.h b/src/qos/transport_feedback_adapter.h index 0214cd2..52b9d53 100644 --- a/src/qos/transport_feedback_adapter.h +++ b/src/qos/transport_feedback_adapter.h @@ -17,7 +17,7 @@ #include "congestion_control_feedback.h" #include "network_route.h" #include "network_types.h" -#include "sequence_number_unwrapper.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" struct PacketFeedback { PacketFeedback() = default; diff --git a/src/qos/trendline_estimator.cc b/src/qos/trendline_estimator.cc new file mode 100644 index 0000000..2f9631a --- /dev/null +++ b/src/qos/trendline_estimator.cc @@ -0,0 +1,260 @@ +/* + * 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 "trendline_estimator.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +// Parameters for linear least squares fit of regression line to noisy data. +constexpr double kDefaultTrendlineSmoothingCoeff = 0.9; +constexpr double kDefaultTrendlineThresholdGain = 4.0; +const char kBweWindowSizeInPacketsExperiment[] = + "WebRTC-BweWindowSizeInPackets"; + +std::optional LinearFitSlope( + const std::deque& packets) { + // Compute the "center of mass". + double sum_x = 0; + double sum_y = 0; + for (const auto& packet : packets) { + sum_x += packet.arrival_time_ms; + sum_y += packet.smoothed_delay_ms; + } + double x_avg = sum_x / packets.size(); + double y_avg = sum_y / packets.size(); + // Compute the slope k = \sum (x_i-x_avg)(y_i-y_avg) / \sum (x_i-x_avg)^2 + double numerator = 0; + double denominator = 0; + for (const auto& packet : packets) { + double x = packet.arrival_time_ms; + double y = packet.smoothed_delay_ms; + numerator += (x - x_avg) * (y - y_avg); + denominator += (x - x_avg) * (x - x_avg); + } + if (denominator == 0) return std::nullopt; + return numerator / denominator; +} + +std::optional ComputeSlopeCap( + const std::deque& packets, + const TrendlineEstimatorSettings& settings) { + TrendlineEstimator::PacketTiming early = packets[0]; + for (size_t i = 1; i < settings.beginning_packets; ++i) { + if (packets[i].raw_delay_ms < early.raw_delay_ms) early = packets[i]; + } + size_t late_start = packets.size() - settings.end_packets; + TrendlineEstimator::PacketTiming late = packets[late_start]; + for (size_t i = late_start + 1; i < packets.size(); ++i) { + if (packets[i].raw_delay_ms < late.raw_delay_ms) late = packets[i]; + } + if (late.arrival_time_ms - early.arrival_time_ms < 1) { + return std::nullopt; + } + return (late.raw_delay_ms - early.raw_delay_ms) / + (late.arrival_time_ms - early.arrival_time_ms) + + settings.cap_uncertainty; +} + +constexpr double kMaxAdaptOffsetMs = 15.0; +constexpr double kOverUsingTimeThreshold = 10; +constexpr int kMinNumDeltas = 60; +constexpr int kDeltaCounterMax = 1000; + +} // namespace + +constexpr char TrendlineEstimatorSettings::kKey[]; + +TrendlineEstimatorSettings::TrendlineEstimatorSettings() { + if (window_size < 10 || 200 < window_size) { + LOG_WARN("Window size must be between 10 and 200 packets"); + window_size = kDefaultTrendlineWindowSize; + } + if (enable_cap) { + if (beginning_packets < 1 || end_packets < 1 || + beginning_packets > window_size || end_packets > window_size) { + LOG_WARN("Size of beginning and end must be between 1 and {}", + window_size); + enable_cap = false; + beginning_packets = end_packets = 0; + cap_uncertainty = 0.0; + } + if (beginning_packets + end_packets > window_size) { + LOG_WARN("Size of beginning plus end can't exceed the window size"); + enable_cap = false; + beginning_packets = end_packets = 0; + cap_uncertainty = 0.0; + } + if (cap_uncertainty < 0.0 || 0.025 < cap_uncertainty) { + LOG_WARN("Cap uncertainty must be between 0 and 0.025"); + cap_uncertainty = 0.0; + } + } +} + +TrendlineEstimator::TrendlineEstimator() + : smoothing_coef_(kDefaultTrendlineSmoothingCoeff), + threshold_gain_(kDefaultTrendlineThresholdGain), + num_of_deltas_(0), + first_arrival_time_ms_(-1), + accumulated_delay_(0), + smoothed_delay_(0), + delay_hist_(), + k_up_(0.0087), + k_down_(0.039), + overusing_time_threshold_(kOverUsingTimeThreshold), + threshold_(12.5), + prev_modified_trend_(NAN), + last_update_ms_(-1), + prev_trend_(0.0), + time_over_using_(-1), + overuse_counter_(0), + hypothesis_(BandwidthUsage::kBwNormal), + hypothesis_predicted_(BandwidthUsage::kBwNormal) {} + +TrendlineEstimator::~TrendlineEstimator() {} + +void TrendlineEstimator::UpdateTrendline(double recv_delta_ms, + double send_delta_ms, + int64_t /* send_time_ms */, + int64_t arrival_time_ms, + size_t /* packet_size */) { + const double delta_ms = recv_delta_ms - send_delta_ms; + ++num_of_deltas_; + num_of_deltas_ = std::min(num_of_deltas_, kDeltaCounterMax); + if (first_arrival_time_ms_ == -1) first_arrival_time_ms_ = arrival_time_ms; + + // Exponential backoff filter. + accumulated_delay_ += delta_ms; + smoothed_delay_ = smoothing_coef_ * smoothed_delay_ + + (1 - smoothing_coef_) * accumulated_delay_; + + // Maintain packet window + delay_hist_.emplace_back( + static_cast(arrival_time_ms - first_arrival_time_ms_), + smoothed_delay_, accumulated_delay_); + if (settings_.enable_sort) { + for (size_t i = delay_hist_.size() - 1; + i > 0 && + delay_hist_[i].arrival_time_ms < delay_hist_[i - 1].arrival_time_ms; + --i) { + std::swap(delay_hist_[i], delay_hist_[i - 1]); + } + } + if (delay_hist_.size() > settings_.window_size) delay_hist_.pop_front(); + + // Simple linear regression. + double trend = prev_trend_; + if (delay_hist_.size() == settings_.window_size) { + // Update trend_ if it is possible to fit a line to the data. The delay + // trend can be seen as an estimate of (send_rate - capacity)/capacity. + // 0 < trend < 1 -> the delay increases, queues are filling up + // trend == 0 -> the delay does not change + // trend < 0 -> the delay decreases, queues are being emptied + trend = LinearFitSlope(delay_hist_).value_or(trend); + if (settings_.enable_cap) { + std::optional cap = ComputeSlopeCap(delay_hist_, settings_); + // We only use the cap to filter out overuse detections, not + // to detect additional underuses. + if (trend >= 0 && cap.has_value() && trend > cap.value()) { + trend = cap.value(); + } + } + } + + Detect(trend, send_delta_ms, arrival_time_ms); +} + +void TrendlineEstimator::Update(double recv_delta_ms, double send_delta_ms, + int64_t send_time_ms, int64_t arrival_time_ms, + size_t packet_size, bool calculated_deltas) { + if (calculated_deltas) { + UpdateTrendline(recv_delta_ms, send_delta_ms, send_time_ms, arrival_time_ms, + packet_size); + } +} + +BandwidthUsage TrendlineEstimator::State() const { return hypothesis_; } + +void TrendlineEstimator::Detect(double trend, double ts_delta, int64_t now_ms) { + if (num_of_deltas_ < 2) { + hypothesis_ = BandwidthUsage::kBwNormal; + return; + } + const double modified_trend = + std::min(num_of_deltas_, kMinNumDeltas) * trend * threshold_gain_; + prev_modified_trend_ = modified_trend; + if (modified_trend > threshold_) { + if (time_over_using_ == -1) { + // Initialize the timer. Assume that we've been + // over-using half of the time since the previous + // sample. + time_over_using_ = ts_delta / 2; + } else { + // Increment timer + time_over_using_ += ts_delta; + } + overuse_counter_++; + if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) { + if (trend >= prev_trend_) { + time_over_using_ = 0; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwOverusing; + } + } + } else if (modified_trend < -threshold_) { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwUnderusing; + } else { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwNormal; + } + prev_trend_ = trend; + UpdateThreshold(modified_trend, now_ms); +} + +void TrendlineEstimator::UpdateThreshold(double modified_trend, + int64_t now_ms) { + if (last_update_ms_ == -1) last_update_ms_ = now_ms; + + if (fabs(modified_trend) > threshold_ + kMaxAdaptOffsetMs) { + // Avoid adapting the threshold to big latency spikes, caused e.g., + // by a sudden capacity drop. + last_update_ms_ = now_ms; + return; + } + + const double k = fabs(modified_trend) < threshold_ ? k_down_ : k_up_; + const int64_t kMaxTimeDeltaMs = 100; + int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs); + threshold_ += k * (fabs(modified_trend) - threshold_) * time_delta_ms; + threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f); + last_update_ms_ = now_ms; +} + +} // namespace webrtc diff --git a/src/qos/trendline_estimator.h b/src/qos/trendline_estimator.h new file mode 100644 index 0000000..79081e8 --- /dev/null +++ b/src/qos/trendline_estimator.h @@ -0,0 +1,109 @@ +/* + * 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. + */ +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ + +#include +#include + +#include +#include + +#include "bandwidth_usage.h" + +namespace webrtc { + +struct TrendlineEstimatorSettings { + static constexpr char kKey[] = "WebRTC-Bwe-TrendlineEstimatorSettings"; + static constexpr unsigned kDefaultTrendlineWindowSize = 20; + + TrendlineEstimatorSettings(); + + // Sort the packets in the window. Should be redundant, + // but then almost no cost. + bool enable_sort = false; + + // Cap the trendline slope based on the minimum delay seen + // in the beginning_packets and end_packets respectively. + bool enable_cap = false; + unsigned beginning_packets = 7; + unsigned end_packets = 7; + double cap_uncertainty = 0.0; + + // Size (in packets) of the window. + unsigned window_size = kDefaultTrendlineWindowSize; +}; + +class TrendlineEstimator : public DelayIncreaseDetectorInterface { + public: + TrendlineEstimator(); + ~TrendlineEstimator(); + + TrendlineEstimator(const TrendlineEstimator&) = delete; + TrendlineEstimator& operator=(const TrendlineEstimator&) = delete; + + // Update the estimator with a new sample. The deltas should represent deltas + // between timestamp groups as defined by the InterArrival class. + void Update(double recv_delta_ms, double send_delta_ms, int64_t send_time_ms, + int64_t arrival_time_ms, size_t packet_size, + bool calculated_deltas); + + void UpdateTrendline(double recv_delta_ms, double send_delta_ms, + int64_t send_time_ms, int64_t arrival_time_ms, + size_t packet_size); + + BandwidthUsage State() const; + + struct PacketTiming { + PacketTiming(double arrival_time_ms, double smoothed_delay_ms, + double raw_delay_ms) + : arrival_time_ms(arrival_time_ms), + smoothed_delay_ms(smoothed_delay_ms), + raw_delay_ms(raw_delay_ms) {} + double arrival_time_ms; + double smoothed_delay_ms; + double raw_delay_ms; + }; + + private: + friend class GoogCcStatePrinter; + void Detect(double trend, double ts_delta, int64_t now_ms); + + void UpdateThreshold(double modified_trend, int64_t now_ms); + + // Parameters. + TrendlineEstimatorSettings settings_; + const double smoothing_coef_; + const double threshold_gain_; + // Used by the existing threshold. + int num_of_deltas_; + // Keep the arrival times small by using the change from the first packet. + int64_t first_arrival_time_ms_; + // Exponential backoff filtering. + double accumulated_delay_; + double smoothed_delay_; + // Linear least squares regression. + std::deque delay_hist_; + + const double k_up_; + const double k_down_; + double overusing_time_threshold_; + double threshold_; + double prev_modified_trend_; + int64_t last_update_ms_; + double prev_trend_; + double time_over_using_; + int overuse_counter_; + BandwidthUsage hypothesis_; + BandwidthUsage hypothesis_predicted_; +}; +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_TRENDLINE_ESTIMATOR_H_ diff --git a/src/rtcp/rtcp_packet/common_header.cpp b/src/rtcp/rtcp_packet/common_header.cpp index 9290443..6908cce 100644 --- a/src/rtcp/rtcp_packet/common_header.cpp +++ b/src/rtcp/rtcp_packet/common_header.cpp @@ -13,6 +13,8 @@ #include "byte_io.h" #include "log.h" +namespace webrtc { +namespace rtcp { // 0 1 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -24,14 +26,14 @@ // --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Common header for all RTCP packets, 4 octets. -bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) { +bool CommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) { const uint8_t kVersion = 2; if (size_bytes < kHeaderSizeBytes) { LOG_WARN( - "Too little data ({}) remaining in buffer to parse RTCP header (4 " - "bytes)", - size_bytes); + "Too little data ({} byte{}) remaining in buffer to parse RTCP header " + "(4 bytes).", + size_bytes, (size_bytes != 1 ? "s" : "")); return false; } @@ -51,8 +53,8 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) { if (size_bytes < kHeaderSizeBytes + payload_size_) { LOG_WARN( - "Buffer too small ({}) to fit an RtcpPacket with a header and ({} " - "bytes)", + "Buffer too small ({} bytes) to fit an RtcpPacket with a header and {} " + "bytes.", size_bytes, payload_size_); return false; } @@ -60,25 +62,26 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) { if (has_padding) { if (payload_size_ == 0) { LOG_WARN( - "Invalid RTCP header: Padding bit set but 0 payload size " - "specified"); + "Invalid RTCP header: Padding bit set but 0 payload size specified."); return false; } padding_size_ = payload_[payload_size_ - 1]; if (padding_size_ == 0) { LOG_WARN( - "Invalid RTCP header: Padding bit set but 0 padding size specified"); + "Invalid RTCP header: Padding bit set but 0 padding size specified."); return false; } if (padding_size_ > payload_size_) { LOG_WARN( "Invalid RTCP header: Too many padding bytes ({}) for a packet " - "payload size of ({}) bytes", + "payload size of {} bytes.", padding_size_, payload_size_); return false; } payload_size_ -= padding_size_; } return true; -} \ No newline at end of file +} +} // namespace rtcp +} // namespace webrtc diff --git a/src/rtcp/rtcp_packet/common_header.h b/src/rtcp/rtcp_packet/common_header.h index ed382d4..5416406 100644 --- a/src/rtcp/rtcp_packet/common_header.h +++ b/src/rtcp/rtcp_packet/common_header.h @@ -13,13 +13,15 @@ #include #include -class RtcpCommonHeader { +namespace webrtc { +namespace rtcp { +class CommonHeader { public: static constexpr size_t kHeaderSizeBytes = 4; - RtcpCommonHeader() {} - RtcpCommonHeader(const RtcpCommonHeader&) = default; - RtcpCommonHeader& operator=(const RtcpCommonHeader&) = default; + CommonHeader() {} + CommonHeader(const CommonHeader&) = default; + CommonHeader& operator=(const CommonHeader&) = default; bool Parse(const uint8_t* buffer, size_t size_bytes); @@ -45,4 +47,6 @@ class RtcpCommonHeader { uint32_t payload_size_ = 0; const uint8_t* payload_ = nullptr; }; +} // namespace rtcp +} // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_ diff --git a/src/rtp/rtp_packet/rtp_header.h b/src/rtp/rtp_packet/rtp_header.h new file mode 100644 index 0000000..7358227 --- /dev/null +++ b/src/rtp/rtp_packet/rtp_header.h @@ -0,0 +1,44 @@ +/* + * @Author: DI JUNKUN + * @Date: 2025-01-16 + * Copyright (c) 2025 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _RTP_HEADER_H_ +#define _RTP_HEADER_H_ + +#include +#include +#include +#include + +#include "api/units/timestamp.h" + +enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13 + +struct RTPHeader { + RTPHeader() + : markerBit(false), + payloadType(0), + sequenceNumber(0), + timestamp(0), + ssrc(0), + numCSRCs(0), + arrOfCSRCs(), + paddingLength(0), + headerLength(0){}; + RTPHeader(const RTPHeader& other) = default; + RTPHeader& operator=(const RTPHeader& other) = default; + + bool markerBit; + uint8_t payloadType; + uint16_t sequenceNumber; + uint32_t timestamp; + uint32_t ssrc; + uint8_t numCSRCs; + uint32_t arrOfCSRCs[kRtpCsrcSize]; + size_t paddingLength; + size_t headerLength; +}; + +#endif \ No newline at end of file diff --git a/src/rtp/rtp_packet/rtp_packet.cpp b/src/rtp/rtp_packet/rtp_packet.cpp index 228bf25..dc7dbc9 100644 --- a/src/rtp/rtp_packet/rtp_packet.cpp +++ b/src/rtp/rtp_packet/rtp_packet.cpp @@ -35,6 +35,7 @@ void RtpPacket::ParseRtpData() { RtpPacket::RtpPacket() : buffer_(new uint8_t[DEFAULT_MTU]), size_(DEFAULT_MTU) { memset(buffer_, 0, DEFAULT_MTU); + ParseRtpData(); } RtpPacket::RtpPacket(const uint8_t *buffer, uint32_t size) { @@ -48,6 +49,7 @@ RtpPacket::RtpPacket(const uint8_t *buffer, uint32_t size) { size_ = size; // TryToDecodeH264RtpPacket(buffer_); + ParseRtpData(); } } @@ -62,6 +64,7 @@ RtpPacket::RtpPacket(const RtpPacket &rtp_packet) { size_ = rtp_packet.size_; // TryToDecodeH264RtpPacket(buffer_); + ParseRtpData(); } } @@ -72,6 +75,7 @@ RtpPacket::RtpPacket(RtpPacket &&rtp_packet) rtp_packet.size_ = 0; // TryToDecodeH264RtpPacket(buffer_); + ParseRtpData(); } // RtpPacket &RtpPacket::operator=(const RtpPacket &rtp_packet) { diff --git a/src/rtp/rtp_packet/rtp_packet.h b/src/rtp/rtp_packet/rtp_packet.h index 22b8116..2848bc1 100644 --- a/src/rtp/rtp_packet/rtp_packet.h +++ b/src/rtp/rtp_packet/rtp_packet.h @@ -278,48 +278,48 @@ class RtpPacket { public: // Get Header - uint32_t Verion() { - ParseRtpData(); + uint32_t Verion() const { + // // ParseRtpData(); return version_; } - bool HasPadding() { - ParseRtpData(); + bool HasPadding() const { + // ParseRtpData(); return has_padding_; } - bool HasExtension() { - ParseRtpData(); + bool HasExtension() const { + // ParseRtpData(); return has_extension_; } - bool Marker() { - ParseRtpData(); + bool Marker() const { + // // ParseRtpData(); return marker_; } - PAYLOAD_TYPE PayloadType() { - ParseRtpData(); + PAYLOAD_TYPE PayloadType() const { + // ParseRtpData(); return PAYLOAD_TYPE(payload_type_); } - uint16_t SequenceNumber() { - ParseRtpData(); + uint16_t SequenceNumber() const { + // ParseRtpData(); return sequence_number_; } - uint64_t Timestamp() { - ParseRtpData(); + uint64_t Timestamp() const { + // ParseRtpData(); return timestamp_; } - uint32_t Ssrc() { - ParseRtpData(); + uint32_t Ssrc() const { + // ParseRtpData(); return ssrc_; } - std::vector Csrcs() { - ParseRtpData(); + std::vector Csrcs() const { + // ParseRtpData(); return csrcs_; }; - uint16_t ExtensionProfile() { - ParseRtpData(); + uint16_t ExtensionProfile() const { + // ParseRtpData(); return extension_profile_; } const uint8_t *ExtensionData() { - ParseRtpData(); + // ParseRtpData(); return extension_data_; } @@ -336,34 +336,42 @@ class RtpPacket { // Payload const uint8_t *Payload() { - ParseRtpData(); + // ParseRtpData(); return payload_; }; size_t PayloadSize() { - ParseRtpData(); + // ParseRtpData(); return payload_size_; } + size_t headers_size() const { return 12; } + + size_t payload_size() const { return payload_size_; } + + bool has_padding() const { return buffer_[0] & 0x20; } + + size_t padding_size() const { return padding_size_; } + // Entire RTP buffer const uint8_t *Buffer() const { return buffer_; } size_t Size() const { return size_; } // NAL NAL_UNIT_TYPE NalUnitType() { - ParseRtpData(); + // ParseRtpData(); return nal_unit_type_; } bool FuAStart() { - ParseRtpData(); + // ParseRtpData(); return fu_header_.start; } bool FuAEnd() { - ParseRtpData(); + // ParseRtpData(); return fu_header_.end; } bool Av1FrameStart() { - ParseRtpData(); + // ParseRtpData(); int z, y, w, n; GetAv1AggrHeader(z, y, w, n); // return !z && !y; @@ -378,7 +386,7 @@ class RtpPacket { } bool Av1FrameEnd() { - ParseRtpData(); + // ParseRtpData(); int z, y, w, n; GetAv1AggrHeader(z, y, w, n); // return z && !y; @@ -421,6 +429,7 @@ class RtpPacket { // Payload uint8_t *payload_ = nullptr; size_t payload_size_ = 0; + size_t padding_size_ = 0; // Entire RTP buffer uint8_t *buffer_ = nullptr; diff --git a/src/rtp/rtp_packet/rtp_packet_received.cpp b/src/rtp/rtp_packet/rtp_packet_received.cpp index 3201fbc..19e1609 100644 --- a/src/rtp/rtp_packet/rtp_packet_received.cpp +++ b/src/rtp/rtp_packet/rtp_packet_received.cpp @@ -1,6 +1,28 @@ +/* + * Copyright (c) 2017 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 "rtp_packet_received.h" +#include + +#include +#include + +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + RtpPacketReceived::RtpPacketReceived() = default; +RtpPacketReceived::RtpPacketReceived( + webrtc::Timestamp arrival_time /*= webrtc::Timestamp::MinusInfinity()*/) + : RtpPacket(), arrival_time_(arrival_time) {} RtpPacketReceived::RtpPacketReceived(const RtpPacketReceived& packet) = default; RtpPacketReceived::RtpPacketReceived(RtpPacketReceived&& packet) = default; @@ -10,3 +32,20 @@ RtpPacketReceived& RtpPacketReceived::operator=(RtpPacketReceived&& packet) = default; RtpPacketReceived::~RtpPacketReceived() {} + +void RtpPacketReceived::GetHeader(RTPHeader* header) const { + header->markerBit = Marker(); + header->payloadType = PayloadType(); + header->sequenceNumber = SequenceNumber(); + header->timestamp = Timestamp(); + header->ssrc = Ssrc(); + std::vector csrcs = Csrcs(); + header->numCSRCs = rtc::dchecked_cast(csrcs.size()); + for (size_t i = 0; i < csrcs.size(); ++i) { + header->arrOfCSRCs[i] = csrcs[i]; + } + header->paddingLength = padding_size(); + header->headerLength = headers_size(); +} + +} // namespace webrtc diff --git a/src/rtp/rtp_packet/rtp_packet_received.h b/src/rtp/rtp_packet/rtp_packet_received.h index ab4e984..02cf0be 100644 --- a/src/rtp/rtp_packet/rtp_packet_received.h +++ b/src/rtp/rtp_packet/rtp_packet_received.h @@ -1,20 +1,36 @@ /* - * @Author: DI JUNKUN - * @Date: 2024-12-18 - * Copyright (c) 2024 by DI JUNKUN, All Rights Reserved. + * 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. */ +#ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_ +#define MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_ -#ifndef _RTP_PACKET_RECEIVED_H_ -#define _RTP_PACKET_RECEIVED_H_ +#include -#include +#include -#include "enc_mark.h" +#include "api/array_view.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/units/timestamp.h" +#include "rtc_base/network/ecn_marking.h" +#include "rtp_header.h" #include "rtp_packet.h" +namespace webrtc { +// Class to hold rtp packet with metadata for receiver side. +// The metadata is not parsed from the rtp packet, but may be derived from the +// data that is parsed from the rtp packet. class RtpPacketReceived : public RtpPacket { public: RtpPacketReceived(); + explicit RtpPacketReceived( + webrtc::Timestamp arrival_time = webrtc::Timestamp::MinusInfinity()); RtpPacketReceived(const RtpPacketReceived& packet); RtpPacketReceived(RtpPacketReceived&& packet); @@ -23,14 +39,19 @@ class RtpPacketReceived : public RtpPacket { ~RtpPacketReceived(); - public: - int64_t arrival_time() const { return arrival_time_; } - void set_arrival_time(int64_t time) { arrival_time_ = time; } + // TODO(bugs.webrtc.org/15054): Remove this function when all code is updated + // to use RtpPacket directly. + void GetHeader(RTPHeader* header) const; + + // Time in local time base as close as it can to packet arrived on the + // network. + webrtc::Timestamp arrival_time() const { return arrival_time_; } + void set_arrival_time(webrtc::Timestamp time) { arrival_time_ = time; } // Explicit Congestion Notification (ECN), RFC-3168, Section 5. // Used by L4S: https://www.rfc-editor.org/rfc/rfc9331.html - EcnMarking ecn() const { return ecn_; } - void set_ecn(EcnMarking ecn) { ecn_ = ecn; } + rtc::EcnMarking ecn() const { return ecn_; } + void set_ecn(rtc::EcnMarking ecn) { ecn_ = ecn; } // Flag if packet was recovered via RTX or FEC. bool recovered() const { return recovered_; } @@ -41,11 +62,22 @@ class RtpPacketReceived : public RtpPacket { payload_type_frequency_ = value; } + // An application can attach arbitrary data to an RTP packet using + // `additional_data`. The additional data does not affect WebRTC processing. + rtc::scoped_refptr additional_data() const { + return additional_data_; + } + void set_additional_data(rtc::scoped_refptr data) { + additional_data_ = std::move(data); + } + private: - int64_t arrival_time_ = std::numeric_limits::min(); - EcnMarking ecn_ = EcnMarking::kNotEct; + webrtc::Timestamp arrival_time_ = Timestamp::MinusInfinity(); + rtc::EcnMarking ecn_ = rtc::EcnMarking::kNotEct; int payload_type_frequency_ = 0; bool recovered_ = false; + rtc::scoped_refptr additional_data_; }; -#endif \ No newline at end of file +} // namespace webrtc +#endif // MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_ diff --git a/xmake.lua b/xmake.lua index da806ce..e22957a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -41,7 +41,9 @@ target("log") target("common") set_kind("object") - add_files("src/common/common.cpp") + add_files("src/common/common.cpp", + "src/common/rtc_base/numerics/*.cc", + "src/common/api/units/*.cc") add_includedirs("src/common", {public = true}) target("inih") @@ -100,7 +102,7 @@ target("ws") target("rtp") set_kind("object") - add_deps("log", "frame", "ringbuffer", "thread", "rtcp", "fec", "statistics") + add_deps("log", "common", "frame", "ringbuffer", "thread", "rtcp", "fec", "statistics") add_files("src/rtp/*.cpp", "src/rtp/rtp_packet/*.cpp") add_includedirs("src/rtp", @@ -120,7 +122,8 @@ target("rtcp") target("qos") set_kind("object") add_deps("log", "rtp", "rtcp") - add_files("src/qos/*.cpp") + add_files("src/qos/*.cc", + "src/qos/*.cpp") add_includedirs("src/qos", {public = true}) target("channel")