mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
[feat] use original webrtc header defines
This commit is contained in:
@@ -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(
|
||||
|
||||
319
src/common/api/array_view.h
Normal file
319
src/common/api/array_view.h
Normal file
@@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#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<T>. 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<const int> 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<int>(arr, size)); // pointer + size
|
||||
// Contains17(nullptr); // nullptr -> empty ArrayView
|
||||
// ...
|
||||
//
|
||||
// ArrayView<T> stores both a pointer and a size, but you may also use
|
||||
// ArrayView<T, N>, 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<T> and ArrayView<const T> 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<int> will convert to either ArrayView<int> or ArrayView<const
|
||||
// int>, but const vector<int> will convert only to ArrayView<const int>.
|
||||
// (ArrayView itself can be the source type in such conversions, so
|
||||
// ArrayView<int> will convert to ArrayView<const int>.)
|
||||
//
|
||||
// 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 <typename T, std::ptrdiff_t Size>
|
||||
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 <typename T>
|
||||
class ArrayViewBase<T, 0> {
|
||||
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 <typename T>
|
||||
class ArrayViewBase<T, array_view_internal::kArrayViewVarSize> {
|
||||
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 <typename T,
|
||||
std::ptrdiff_t Size = array_view_internal::kArrayViewVarSize>
|
||||
class ArrayView final : public array_view_internal::ArrayViewBase<T, Size> {
|
||||
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 <typename U>
|
||||
ArrayView(U* data, size_t size)
|
||||
: array_view_internal::ArrayViewBase<T, Size>::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<T*>(nullptr), size) {
|
||||
static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize,
|
||||
"");
|
||||
}
|
||||
|
||||
// Construct an ArrayView from a C-style array.
|
||||
template <typename U, size_t N>
|
||||
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<T, N> from a
|
||||
// non-const std::array instance. For an ArrayView with variable size, the
|
||||
// used ctor is ArrayView(U& u) instead.
|
||||
template <typename U, size_t N,
|
||||
typename std::enable_if<
|
||||
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
|
||||
ArrayView(std::array<U, N>& u) // NOLINT
|
||||
: ArrayView(u.data(), u.size()) {}
|
||||
|
||||
// (Only if size is fixed.) Construct a fixed size ArrayView<T, N> 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 <typename U, size_t N,
|
||||
typename std::enable_if<
|
||||
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
|
||||
ArrayView(const std::array<U, N>& 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<T, N> to ArrayView<const T,
|
||||
// N>, but not the other way around. We also don't allow conversion from
|
||||
// ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
|
||||
// N> when M != N.
|
||||
template <typename U, typename std::enable_if<
|
||||
Size != array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(U& u) // NOLINT
|
||||
: ArrayView(u.data(), u.size()) {
|
||||
static_assert(U::size() == Size, "Sizes must match exactly");
|
||||
}
|
||||
template <typename U, typename std::enable_if<
|
||||
Size != array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::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<T> to
|
||||
// ArrayView<const T>, but not the other way around. Other allowed
|
||||
// conversions include
|
||||
// ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
|
||||
// std::vector<T> to ArrayView<T> or ArrayView<const T>,
|
||||
// const std::vector<T> to ArrayView<const T>,
|
||||
// rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
|
||||
// const rtc::Buffer to ArrayView<const uint8_t>.
|
||||
template <typename U, typename std::enable_if<
|
||||
Size == array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::value>::type* = nullptr>
|
||||
ArrayView(U& u) // NOLINT
|
||||
: ArrayView(u.data(), u.size()) {}
|
||||
template <typename U, typename std::enable_if<
|
||||
Size == array_view_internal::kArrayViewVarSize &&
|
||||
HasDataAndSize<U, T>::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<T*> rbegin() const {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
std::reverse_iterator<T*> rend() const {
|
||||
return std::make_reverse_iterator(begin());
|
||||
}
|
||||
std::reverse_iterator<const T*> crbegin() const {
|
||||
return std::make_reverse_iterator(cend());
|
||||
}
|
||||
std::reverse_iterator<const T*> crend() const {
|
||||
return std::make_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
ArrayView<T> subview(size_t offset, size_t size) const {
|
||||
return offset < this->size()
|
||||
? ArrayView<T>(this->data() + offset,
|
||||
std::min(size, this->size() - offset))
|
||||
: ArrayView<T>();
|
||||
}
|
||||
ArrayView<T> 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 <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
|
||||
bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
|
||||
return a.data() == b.data() && a.size() == b.size();
|
||||
}
|
||||
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
|
||||
bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& 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<int>) == 2 * sizeof(int*), "");
|
||||
static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
|
||||
static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
|
||||
|
||||
template <typename T>
|
||||
inline ArrayView<T> MakeArrayView(T* data, size_t size) {
|
||||
return ArrayView<T>(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<target_type>(array_view).
|
||||
template <typename U, typename T, std::ptrdiff_t Size>
|
||||
inline ArrayView<U, Size> reinterpret_array_view(ArrayView<T, Size> 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<T>::value && std::is_fundamental<U>::value,
|
||||
"ArrayView reinterpret_cast is only supported for casting between "
|
||||
"fundamental types.");
|
||||
return ArrayView<U, Size>(reinterpret_cast<U*>(view.data()), view.size());
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // API_ARRAY_VIEW_H_
|
||||
67
src/common/api/ref_count.h
Normal file
67
src/common/api/ref_count.h
Normal file
@@ -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_
|
||||
107
src/common/api/ref_counted_base.h
Normal file
107
src/common/api/ref_counted_base.h
Normal file
@@ -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 <type_traits>
|
||||
|
||||
#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<MyInt> {
|
||||
// int foo_ = 0;
|
||||
// };
|
||||
//
|
||||
// rtc::scoped_refptr<MyInt> my_int(new MyInt());
|
||||
//
|
||||
// sizeof(MyInt) on a 32 bit system would then be 8, int + refcount and no
|
||||
// vtable generated.
|
||||
template <typename T>
|
||||
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<T>::value,
|
||||
"T has virtual methods. RefCountedBase is a better fit.");
|
||||
const auto status = ref_count_.DecRef();
|
||||
if (status == RefCountReleaseStatus::kDroppedLastRef) {
|
||||
delete static_cast<const T*>(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 <typename T>
|
||||
using RefCountedNonVirtual = webrtc::RefCountedNonVirtual<T>;
|
||||
} // namespace rtc
|
||||
|
||||
#endif // API_REF_COUNTED_BASE_H_
|
||||
203
src/common/api/scoped_refptr.h
Normal file
203
src/common/api/scoped_refptr.h
Normal file
@@ -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<MyFoo> {
|
||||
// ...
|
||||
// };
|
||||
//
|
||||
// void some_function() {
|
||||
// scoped_refptr<MyFoo> foo = make_ref_counted<MyFoo>();
|
||||
// foo->Method(param);
|
||||
// // `foo` is released when this function returns
|
||||
// }
|
||||
//
|
||||
// void some_other_function() {
|
||||
// scoped_refptr<MyFoo> foo = make_ref_counted<MyFoo>();
|
||||
// ...
|
||||
// foo = nullptr; // explicitly releases `foo`
|
||||
// ...
|
||||
// if (foo)
|
||||
// foo->Method(param);
|
||||
// }
|
||||
//
|
||||
// The above examples show how scoped_refptr<T> acts like a pointer to T.
|
||||
// Given two scoped_refptr<T> classes, it is also possible to exchange
|
||||
// references between the two objects, like so:
|
||||
//
|
||||
// {
|
||||
// scoped_refptr<MyFoo> a = make_ref_counted<MyFoo>();
|
||||
// scoped_refptr<MyFoo> 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<MyFoo> a = make_ref_counted<MyFoo>();
|
||||
// scoped_refptr<MyFoo> 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 <cstddef>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <class T>
|
||||
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<T>& r) : ptr_(r.ptr_) {}
|
||||
|
||||
template <typename U>
|
||||
scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {}
|
||||
|
||||
// Move constructors.
|
||||
scoped_refptr(scoped_refptr<T>&& r) noexcept : ptr_(std::move(r.ptr_)) {}
|
||||
|
||||
template <typename U>
|
||||
scoped_refptr(scoped_refptr<U>&& 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<T>& operator=(T* p) {
|
||||
ptr_.reset(p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
|
||||
ptr_ = r.ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
|
||||
ptr_ = r.ptr_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
scoped_refptr<T>& operator=(scoped_refptr<T>&& r) noexcept {
|
||||
ptr_ = std::move(r.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
scoped_refptr<T>& operator=(scoped_refptr<U>&& r) noexcept {
|
||||
ptr_ = std::move(r.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(T** pp) noexcept { std::swap(ptr_, *pp); }
|
||||
|
||||
void swap(scoped_refptr<T>& r) noexcept { std::swap(ptr_, r.ptr_); }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<T> ptr_;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const scoped_refptr<T>& a, const scoped_refptr<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const scoped_refptr<T>& a, std::nullptr_t) {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const scoped_refptr<T>& a, std::nullptr_t) {
|
||||
return !(a == nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(std::nullptr_t, const scoped_refptr<T>& a) {
|
||||
return a.get() == nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(std::nullptr_t, const scoped_refptr<T>& a) {
|
||||
return !(a == nullptr);
|
||||
}
|
||||
|
||||
// Comparison with raw pointer.
|
||||
template <typename T, typename U>
|
||||
bool operator==(const scoped_refptr<T>& a, const U* b) {
|
||||
return a.get() == b;
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const scoped_refptr<T>& a, const U* b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const T* a, const scoped_refptr<U>& b) {
|
||||
return a == b.get();
|
||||
}
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const T* a, const scoped_refptr<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
// Ordered comparison, needed for use as a std::map key.
|
||||
template <typename T, typename U>
|
||||
bool operator<(const scoped_refptr<T>& a, const scoped_refptr<U>& 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_
|
||||
30
src/common/api/units/data_rate.cc
Normal file
30
src/common/api/units/data_rate.cc
Normal file
@@ -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 <string>
|
||||
|
||||
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
|
||||
141
src/common/api/units/data_rate.h
Normal file
141
src/common/api/units/data_rate.h
Normal file
@@ -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 <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#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<DataRate> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr DataRate BitsPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr DataRate BytesPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(8, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr DataRate KilobitsPerSec(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1000, value);
|
||||
}
|
||||
static constexpr DataRate Infinity() { return PlusInfinity(); }
|
||||
|
||||
constexpr DataRate() = default;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, DataRate value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T bps() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T bytes_per_sec() const {
|
||||
return ToFraction<8, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
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<DataRate>;
|
||||
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<int64_t>::max() / 8000000;
|
||||
return size.bytes() * 8000000;
|
||||
}
|
||||
|
||||
inline constexpr int64_t MillibytePerSec(const DataRate& size) {
|
||||
constexpr int64_t kMaxBeforeConversion =
|
||||
std::numeric_limits<int64_t>::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<int64_t>();
|
||||
// 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<int64_t>();
|
||||
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 <typename Sink>
|
||||
void AbslStringify(Sink& sink, DataRate value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_DATA_RATE_H_
|
||||
26
src/common/api/units/data_size.cc
Normal file
26
src/common/api/units/data_size.cc
Normal file
@@ -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 <string>
|
||||
|
||||
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
|
||||
60
src/common/api/units/data_size.h
Normal file
60
src/common/api/units/data_size.h
Normal file
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#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<DataSize> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr DataSize Bytes(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
static constexpr DataSize Infinity() { return PlusInfinity(); }
|
||||
|
||||
constexpr DataSize() = default;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, DataSize value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T bytes() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
|
||||
constexpr int64_t bytes_or(int64_t fallback_value) const {
|
||||
return ToValueOr(fallback_value);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class rtc_units_impl::UnitBase<DataSize>;
|
||||
using RelativeUnit::RelativeUnit;
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
std::string ToString(DataSize value);
|
||||
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, DataSize value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_DATA_SIZE_H_
|
||||
27
src/common/api/units/frequency.cc
Normal file
27
src/common/api/units/frequency.cc
Normal file
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace webrtc {
|
||||
std::string ToString(Frequency value) {
|
||||
if (value.IsPlusInfinity()) {
|
||||
return "+inf Hz";
|
||||
} else if (value.IsMinusInfinity()) {
|
||||
return "-inf Hz";
|
||||
} else if (value.millihertz<int64_t>() % 1000 != 0) {
|
||||
return std::to_string(value.hertz<double>()) + " Hz";
|
||||
} else {
|
||||
return std::to_string(value.hertz<int64_t>()) + " Hz";
|
||||
}
|
||||
}
|
||||
} // namespace webrtc
|
||||
89
src/common/api/units/frequency.h
Normal file
89
src/common/api/units/frequency.h
Normal file
@@ -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 <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "unit_base.h" // IWYU pragma: export
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Frequency final : public rtc_units_impl::RelativeUnit<Frequency> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr Frequency MilliHertz(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr Frequency Hertz(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr Frequency KiloHertz(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000'000, value);
|
||||
}
|
||||
|
||||
constexpr Frequency() = default;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, Frequency value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T hertz() const {
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T millihertz() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class rtc_units_impl::UnitBase<Frequency>;
|
||||
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<double>() * time_delta.seconds<double>();
|
||||
}
|
||||
inline constexpr double operator*(TimeDelta time_delta, Frequency frequency) {
|
||||
return frequency * time_delta;
|
||||
}
|
||||
|
||||
std::string ToString(Frequency value);
|
||||
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, Frequency value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // API_UNITS_FREQUENCY_H_
|
||||
32
src/common/api/units/time_delta.cc
Normal file
32
src/common/api/units/time_delta.cc
Normal file
@@ -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 <string>
|
||||
|
||||
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
|
||||
104
src/common/api/units/time_delta.h
Normal file
104
src/common/api/units/time_delta.h
Normal file
@@ -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 <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#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<TimeDelta> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Minutes(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return Seconds(value * 60);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Seconds(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000'000, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Millis(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr TimeDelta Micros(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
|
||||
constexpr TimeDelta() = default;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, TimeDelta value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T seconds() const {
|
||||
return ToFraction<1000000, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T ms() const {
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T us() const {
|
||||
return ToValue<T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
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<TimeDelta>;
|
||||
using RelativeUnit::RelativeUnit;
|
||||
static constexpr bool one_sided = false;
|
||||
};
|
||||
|
||||
std::string ToString(TimeDelta value);
|
||||
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, TimeDelta value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_TIME_DELTA_H_
|
||||
30
src/common/api/units/timestamp.cc
Normal file
30
src/common/api/units/timestamp.cc
Normal file
@@ -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 <string>
|
||||
|
||||
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
|
||||
120
src/common/api/units/timestamp.h
Normal file
120
src/common/api/units/timestamp.h
Normal file
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#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<Timestamp> {
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr Timestamp Seconds(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000'000, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr Timestamp Millis(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromFraction(1'000, value);
|
||||
}
|
||||
template <typename T>
|
||||
static constexpr Timestamp Micros(T value) {
|
||||
static_assert(std::is_arithmetic<T>::value, "");
|
||||
return FromValue(value);
|
||||
}
|
||||
|
||||
Timestamp() = delete;
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, Timestamp value);
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr T seconds() const {
|
||||
return ToFraction<1000000, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T ms() const {
|
||||
return ToFraction<1000, T>();
|
||||
}
|
||||
template <typename T = int64_t>
|
||||
constexpr T us() const {
|
||||
return ToValue<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 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<Timestamp>;
|
||||
using UnitBase::UnitBase;
|
||||
static constexpr bool one_sided = true;
|
||||
};
|
||||
|
||||
std::string ToString(Timestamp value);
|
||||
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, Timestamp value) {
|
||||
sink.Append(ToString(value));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_UNITS_TIMESTAMP_H_
|
||||
275
src/common/api/units/unit_base.h
Normal file
275
src/common/api/units/unit_base.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#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<MyUnit> {...};
|
||||
//
|
||||
// Unit_T is the subclass representing the specific unit.
|
||||
template <class Unit_T>
|
||||
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<Unit_T>& other) const {
|
||||
return value_ == other.value_;
|
||||
}
|
||||
constexpr bool operator!=(const UnitBase<Unit_T>& other) const {
|
||||
return value_ != other.value_;
|
||||
}
|
||||
constexpr bool operator<=(const UnitBase<Unit_T>& other) const {
|
||||
return value_ <= other.value_;
|
||||
}
|
||||
constexpr bool operator>=(const UnitBase<Unit_T>& other) const {
|
||||
return value_ >= other.value_;
|
||||
}
|
||||
constexpr bool operator>(const UnitBase<Unit_T>& other) const {
|
||||
return value_ > other.value_;
|
||||
}
|
||||
constexpr bool operator<(const UnitBase<Unit_T>& 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 <typename T, typename std::enable_if<
|
||||
std::is_integral<T>::value>::type* = nullptr>
|
||||
static constexpr Unit_T FromValue(T value) {
|
||||
return Unit_T(rtc::dchecked_cast<int64_t>(value));
|
||||
}
|
||||
template <typename T, typename std::enable_if<
|
||||
std::is_floating_point<T>::value>::type* = nullptr>
|
||||
static constexpr Unit_T FromValue(T value) {
|
||||
if (value == std::numeric_limits<T>::infinity()) {
|
||||
return PlusInfinity();
|
||||
} else if (value == -std::numeric_limits<T>::infinity()) {
|
||||
return MinusInfinity();
|
||||
} else {
|
||||
return FromValue(rtc::dchecked_cast<int64_t>(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
std::is_integral<T>::value>::type* = nullptr>
|
||||
static constexpr Unit_T FromFraction(int64_t denominator, T value) {
|
||||
return Unit_T(rtc::dchecked_cast<int64_t>(value * denominator));
|
||||
}
|
||||
template <typename T, typename std::enable_if<
|
||||
std::is_floating_point<T>::value>::type* = nullptr>
|
||||
static constexpr Unit_T FromFraction(int64_t denominator, T value) {
|
||||
return FromValue(value * denominator);
|
||||
}
|
||||
|
||||
template <typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
ToValue() const {
|
||||
return rtc::dchecked_cast<T>(value_);
|
||||
}
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||
ToValue() const {
|
||||
return IsPlusInfinity() ? std::numeric_limits<T>::infinity()
|
||||
: IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
|
||||
: value_;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr T ToValueOr(T fallback_value) const {
|
||||
return IsFinite() ? value_ : fallback_value;
|
||||
}
|
||||
|
||||
template <int64_t Denominator, typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
ToFraction() const {
|
||||
return rtc::dchecked_cast<T>(DivideRoundToNearest(value_, Denominator));
|
||||
}
|
||||
template <int64_t Denominator, typename T>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||
ToFraction() const {
|
||||
return ToValue<T>() * (1 / static_cast<T>(Denominator));
|
||||
}
|
||||
|
||||
template <int64_t Denominator>
|
||||
constexpr int64_t ToFractionOr(int64_t fallback_value) const {
|
||||
return IsFinite() ? DivideRoundToNearest(value_, Denominator)
|
||||
: fallback_value;
|
||||
}
|
||||
|
||||
template <int64_t Factor, typename T = int64_t>
|
||||
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
ToMultiple() const {
|
||||
return rtc::dchecked_cast<T>(ToValue() * Factor);
|
||||
}
|
||||
template <int64_t Factor, typename T>
|
||||
constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||
ToMultiple() const {
|
||||
return ToValue<T>() * Factor;
|
||||
}
|
||||
|
||||
explicit constexpr UnitBase(int64_t value) : value_(value) {}
|
||||
|
||||
private:
|
||||
template <class RelativeUnit_T>
|
||||
friend class RelativeUnit;
|
||||
|
||||
static inline constexpr int64_t PlusInfinityVal() {
|
||||
return std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
static inline constexpr int64_t MinusInfinityVal() {
|
||||
return std::numeric_limits<int64_t>::min();
|
||||
}
|
||||
|
||||
constexpr Unit_T& AsSubClassRef() { return static_cast<Unit_T&>(*this); }
|
||||
constexpr const Unit_T& AsSubClassRef() const {
|
||||
return static_cast<const Unit_T&>(*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 Unit_T>
|
||||
class RelativeUnit : public UnitBase<Unit_T> {
|
||||
public:
|
||||
constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const {
|
||||
return std::max(min_value,
|
||||
std::min(UnitBase<Unit_T>::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<Unit_T>::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<Unit_T>::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<Unit_T>::template ToValue<double>() /
|
||||
other.template ToValue<double>();
|
||||
}
|
||||
template <typename T,
|
||||
typename std::enable_if_t<std::is_floating_point_v<T>>* = nullptr>
|
||||
constexpr Unit_T operator/(T scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(std::llround(this->ToValue() / scalar));
|
||||
}
|
||||
template <typename T,
|
||||
typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
|
||||
constexpr Unit_T operator/(T scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() / scalar);
|
||||
}
|
||||
constexpr Unit_T operator*(double scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(std::llround(this->ToValue() * scalar));
|
||||
}
|
||||
constexpr Unit_T operator*(int64_t scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
constexpr Unit_T operator*(int32_t scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
constexpr Unit_T operator*(size_t scalar) const {
|
||||
return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
|
||||
}
|
||||
|
||||
protected:
|
||||
using UnitBase<Unit_T>::UnitBase;
|
||||
constexpr RelativeUnit() : UnitBase<Unit_T>(0) {}
|
||||
};
|
||||
|
||||
template <class Unit_T>
|
||||
inline constexpr Unit_T operator*(double scalar, RelativeUnit<Unit_T> other) {
|
||||
return other * scalar;
|
||||
}
|
||||
template <class Unit_T>
|
||||
inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit<Unit_T> other) {
|
||||
return other * scalar;
|
||||
}
|
||||
template <class Unit_T>
|
||||
inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit<Unit_T> other) {
|
||||
return other * scalar;
|
||||
}
|
||||
template <class Unit_T>
|
||||
inline constexpr Unit_T operator*(size_t scalar, RelativeUnit<Unit_T> other) {
|
||||
return other * scalar;
|
||||
}
|
||||
|
||||
template <class Unit_T>
|
||||
inline constexpr Unit_T operator-(RelativeUnit<Unit_T> other) {
|
||||
if (other.IsPlusInfinity()) return UnitBase<Unit_T>::MinusInfinity();
|
||||
if (other.IsMinusInfinity()) return UnitBase<Unit_T>::PlusInfinity();
|
||||
return -1 * other;
|
||||
}
|
||||
|
||||
} // namespace rtc_units_impl
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_UNITS_UNIT_BASE_H_
|
||||
@@ -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
|
||||
61
src/common/module_common_types_public.h
Normal file
61
src/common/module_common_types_public.h
Normal file
@@ -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 <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <typename U>
|
||||
inline bool IsNewer(U value, U prev_value) {
|
||||
static_assert(!std::numeric_limits<U>::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<U>::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<U>(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_
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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<int64_t>::max();
|
||||
// Deprecated, use TargetTransferRate::target_rate instead.
|
||||
int64_t bandwidth = std::numeric_limits<int64_t>::max();
|
||||
int64_t round_trip_time = std::numeric_limits<int64_t>::max();
|
||||
int64_t bwe_period = std::numeric_limits<int64_t>::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<int64_t>::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<bool> 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<bool> enable_repeated_initial_probing;
|
||||
std::optional<double> pacing_factor;
|
||||
|
||||
// TODO(srte): Use BitrateAllocationLimits here.
|
||||
std::optional<DataRate> min_total_allocated_bitrate;
|
||||
std::optional<DataRate> max_padding_rate;
|
||||
std::optional<DataRate> 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<DataRate> min_data_rate;
|
||||
std::optional<DataRate> 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<DataRate> 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<int64_t> congestion_window;
|
||||
// std::optional<PacerConfig> pacer_config;
|
||||
// std::vector<ProbeClusterConfig> probe_cluster_configs;
|
||||
std::optional<TargetTransferRate> 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<int64_t>::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<int64_t>::max();
|
||||
}
|
||||
inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); }
|
||||
|
||||
SentPacket sent_packet;
|
||||
int64_t receive_time = std::numeric_limits<int64_t>::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<int64_t>::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<PacketResult> packet_feedbacks;
|
||||
|
||||
// Arrival times for messages without send time information.
|
||||
std::vector<int64_t> sendless_arrival_times;
|
||||
std::vector<Timestamp> sendless_arrival_times;
|
||||
|
||||
std::vector<PacketResult> ReceivedWithSendInfo() const {
|
||||
std::vector<PacketResult> res;
|
||||
for (const PacketResult& fb : packet_feedbacks) {
|
||||
if (fb.IsReceived()) {
|
||||
res.push_back(fb);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
std::vector<PacketResult> LostWithSendInfo() const {
|
||||
std::vector<PacketResult> res;
|
||||
for (const PacketResult& fb : packet_feedbacks) {
|
||||
if (!fb.IsReceived()) {
|
||||
res.push_back(fb);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<PacketResult> PacketsWithFeedback() const {
|
||||
return packet_feedbacks;
|
||||
}
|
||||
std::vector<PacketResult> SortedByReceiveTime() const {
|
||||
std::vector<PacketResult> 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<PacketResult> ReceivedWithSendInfo() const;
|
||||
std::vector<PacketResult> LostWithSendInfo() const;
|
||||
std::vector<PacketResult> PacketsWithFeedback() const;
|
||||
std::vector<PacketResult> SortedByReceiveTime() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
// 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<DataSize> congestion_window;
|
||||
std::optional<PacerConfig> pacer_config;
|
||||
std::vector<ProbeClusterConfig> probe_cluster_configs;
|
||||
std::optional<TargetTransferRate> target_rate;
|
||||
};
|
||||
|
||||
// Process control
|
||||
struct ProcessInterval {
|
||||
Timestamp at_time = Timestamp::PlusInfinity();
|
||||
std::optional<DataSize> 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_
|
||||
|
||||
61
src/common/rtc_base/module_common_types_public.h
Normal file
61
src/common/rtc_base/module_common_types_public.h
Normal file
@@ -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 <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <typename U>
|
||||
inline bool IsNewer(U value, U prev_value) {
|
||||
static_assert(!std::numeric_limits<U>::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<U>::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<U>(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_
|
||||
44
src/common/rtc_base/network/ecn_marking.h
Normal file
44
src/common/rtc_base/network/ecn_marking.h
Normal file
@@ -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_
|
||||
56
src/common/rtc_base/numerics/divide_round.h
Normal file
56
src/common/rtc_base/numerics/divide_round.h
Normal file
@@ -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 <type_traits>
|
||||
|
||||
#include "safe_compare.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <typename Dividend, typename Divisor>
|
||||
inline auto constexpr DivideRoundUp(Dividend dividend, Divisor divisor) {
|
||||
static_assert(std::is_integral<Dividend>(), "");
|
||||
static_assert(std::is_integral<Divisor>(), "");
|
||||
|
||||
auto quotient = dividend / divisor;
|
||||
auto remainder = dividend % divisor;
|
||||
return quotient + (remainder > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
template <typename Dividend, typename Divisor>
|
||||
inline auto constexpr DivideRoundToNearest(Dividend dividend, Divisor divisor) {
|
||||
static_assert(std::is_integral<Dividend>(), "");
|
||||
static_assert(std::is_integral<Divisor>(), "");
|
||||
|
||||
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_
|
||||
@@ -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 <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
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<double>(half_time) / log(2);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void EventBasedExponentialMovingAverage::Reset() {
|
||||
value_ = std::nan("uninit");
|
||||
sample_variance_ = std::numeric_limits<double>::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<double>::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
|
||||
@@ -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 <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
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<double>::infinity();
|
||||
// This is the ratio between variance of the estimate and variance of samples.
|
||||
double estimator_variance_ = 1;
|
||||
std::optional<int64_t> last_observation_timestamp_;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_EVENT_BASED_EXPONENTIAL_MOVING_AVERAGE_H_
|
||||
41
src/common/rtc_base/numerics/exp_filter.cc
Normal file
41
src/common/rtc_base/numerics/exp_filter.cc
Normal file
@@ -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 <cmath>
|
||||
|
||||
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
|
||||
48
src/common/rtc_base/numerics/exp_filter.h
Normal file
48
src/common/rtc_base/numerics/exp_filter.h
Normal file
@@ -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_
|
||||
72
src/common/rtc_base/numerics/histogram_percentile_counter.cc
Normal file
72
src/common/rtc_base/numerics/histogram_percentile_counter.cc
Normal file
@@ -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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<uint32_t> HistogramPercentileCounter::GetPercentile(
|
||||
float fraction) {
|
||||
if (total_elements_ == 0) return std::nullopt;
|
||||
size_t elements_to_skip = static_cast<size_t>(
|
||||
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
|
||||
44
src/common/rtc_base/numerics/histogram_percentile_counter.h
Normal file
44
src/common/rtc_base/numerics/histogram_percentile_counter.h
Normal file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
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<uint32_t> GetPercentile(float fraction);
|
||||
|
||||
private:
|
||||
std::vector<size_t> histogram_low_;
|
||||
std::map<uint32_t, size_t> 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_
|
||||
70
src/common/rtc_base/numerics/math_utils.h
Normal file
70
src/common/rtc_base/numerics/math_utils.h
Normal file
@@ -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 <limits>
|
||||
#include <type_traits>
|
||||
|
||||
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 T>
|
||||
typename std::make_unsigned<T>::type unsigned_difference(T x, T y) {
|
||||
static_assert(
|
||||
std::is_signed<T>::value,
|
||||
"Function unsigned_difference is only meaningful for signed types.");
|
||||
typedef typename std::make_unsigned<T>::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<unsigned_type>(x) - static_cast<unsigned_type>(y);
|
||||
}
|
||||
|
||||
// Provide neutral element with respect to min().
|
||||
// Typically used as an initial value for running minimum.
|
||||
template <typename T,
|
||||
typename std::enable_if<std::numeric_limits<T>::has_infinity>::type* =
|
||||
nullptr>
|
||||
constexpr T infinity_or_max() {
|
||||
return std::numeric_limits<T>::infinity();
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!std::numeric_limits<
|
||||
T>::has_infinity>::type* = nullptr>
|
||||
constexpr T infinity_or_max() {
|
||||
// Fallback to max().
|
||||
return std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
// Provide neutral element with respect to max().
|
||||
// Typically used as an initial value for running maximum.
|
||||
template <typename T,
|
||||
typename std::enable_if<std::numeric_limits<T>::has_infinity>::type* =
|
||||
nullptr>
|
||||
constexpr T minus_infinity_or_min() {
|
||||
static_assert(std::is_signed<T>::value, "Unsupported. Please open a bug.");
|
||||
return -std::numeric_limits<T>::infinity();
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!std::numeric_limits<
|
||||
T>::has_infinity>::type* = nullptr>
|
||||
constexpr T minus_infinity_or_min() {
|
||||
// Fallback to min().
|
||||
return std::numeric_limits<T>::min();
|
||||
}
|
||||
|
||||
} // namespace webrtc_impl
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_NUMERICS_MATH_UTILS_H_
|
||||
132
src/common/rtc_base/numerics/mod_ops.h
Normal file
132
src/common/rtc_base/numerics/mod_ops.h
Normal file
@@ -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 <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <unsigned long M> // 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 <unsigned long M> // 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 <typename T, T M>
|
||||
inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Type must be an unsigned integer.");
|
||||
return a <= b ? b - a : M - (a - b);
|
||||
}
|
||||
|
||||
template <typename T, T M>
|
||||
inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Type must be an unsigned integer.");
|
||||
return b - a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T ForwardDiff(T a, T b) {
|
||||
return ForwardDiff<T, 0>(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 <typename T, T M>
|
||||
inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Type must be an unsigned integer.");
|
||||
return b <= a ? a - b : M - (b - a);
|
||||
}
|
||||
|
||||
template <typename T, T M>
|
||||
inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Type must be an unsigned integer.");
|
||||
return a - b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T ReverseDiff(T a, T b) {
|
||||
return ReverseDiff<T, 0>(a, b);
|
||||
}
|
||||
|
||||
// Calculates the minimum distance between to wrapping numbers.
|
||||
//
|
||||
// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b))
|
||||
template <typename T, T M = 0>
|
||||
inline T MinDiff(T a, T b) {
|
||||
static_assert(std::is_unsigned<T>::value,
|
||||
"Type must be an unsigned integer.");
|
||||
return std::min(ForwardDiff<T, M>(a, b), ReverseDiff<T, M>(a, b));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_MOD_OPS_H_
|
||||
115
src/common/rtc_base/numerics/moving_max_counter.h
Normal file
115
src/common/rtc_base/numerics/moving_max_counter.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
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 T>
|
||||
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<T> 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<std::pair<int64_t, T>> samples_;
|
||||
#if RTC_DCHECK_IS_ON
|
||||
int64_t last_call_time_ms_ = std::numeric_limits<int64_t>::min();
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T>
|
||||
MovingMaxCounter<T>::MovingMaxCounter(int64_t window_length_ms)
|
||||
: window_length_ms_(window_length_ms) {}
|
||||
|
||||
template <class T>
|
||||
void MovingMaxCounter<T>::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 <class T>
|
||||
std::optional<T> MovingMaxCounter<T>::Max(int64_t current_time_ms) {
|
||||
RollWindow(current_time_ms);
|
||||
std::optional<T> res;
|
||||
if (!samples_.empty()) {
|
||||
res.emplace(samples_.front().second);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void MovingMaxCounter<T>::Reset() {
|
||||
samples_.clear();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void MovingMaxCounter<T>::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_
|
||||
100
src/common/rtc_base/numerics/moving_percentile_filter.h
Normal file
100
src/common/rtc_base/numerics/moving_percentile_filter.h
Normal file
@@ -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 <stddef.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
|
||||
#include "percentile_filter.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Class to efficiently get moving percentile filter from a stream of samples.
|
||||
template <typename T>
|
||||
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<T> percentile_filter_;
|
||||
std::list<T> samples_;
|
||||
size_t samples_stored_;
|
||||
const size_t window_size_;
|
||||
};
|
||||
|
||||
// Convenience type for the common median case.
|
||||
template <typename T>
|
||||
class MovingMedianFilter : public MovingPercentileFilter<T> {
|
||||
public:
|
||||
explicit MovingMedianFilter(size_t window_size)
|
||||
: MovingPercentileFilter<T>(0.5f, window_size) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
MovingPercentileFilter<T>::MovingPercentileFilter(float percentile,
|
||||
size_t window_size)
|
||||
: percentile_filter_(percentile),
|
||||
samples_stored_(0),
|
||||
window_size_(window_size) {}
|
||||
|
||||
template <typename T>
|
||||
void MovingPercentileFilter<T>::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 <typename T>
|
||||
T MovingPercentileFilter<T>::GetFilteredValue() const {
|
||||
return percentile_filter_.GetPercentileValue();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void MovingPercentileFilter<T>::Reset() {
|
||||
percentile_filter_.Reset();
|
||||
samples_.clear();
|
||||
samples_stored_ = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t MovingPercentileFilter<T>::GetNumberOfSamplesStored() const {
|
||||
return samples_stored_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // RTC_BASE_NUMERICS_MOVING_PERCENTILE_FILTER_H_
|
||||
116
src/common/rtc_base/numerics/percentile_filter.h
Normal file
116
src/common/rtc_base/numerics/percentile_filter.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
|
||||
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 <typename T>
|
||||
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<T> set_;
|
||||
// Maintain iterator and index of current target percentile value.
|
||||
typename std::multiset<T>::iterator percentile_it_;
|
||||
int64_t percentile_index_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
PercentileFilter<T>::PercentileFilter(float percentile)
|
||||
: percentile_(percentile),
|
||||
percentile_it_(set_.begin()),
|
||||
percentile_index_(0) {}
|
||||
|
||||
template <typename T>
|
||||
void PercentileFilter<T>::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 <typename T>
|
||||
bool PercentileFilter<T>::Erase(const T& value) {
|
||||
typename std::multiset<T>::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 <typename T>
|
||||
void PercentileFilter<T>::UpdatePercentileIterator() {
|
||||
if (set_.empty()) return;
|
||||
const int64_t index = static_cast<int64_t>(percentile_ * (set_.size() - 1));
|
||||
std::advance(percentile_it_, index - percentile_index_);
|
||||
percentile_index_ = index;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T PercentileFilter<T>::GetPercentileValue() const {
|
||||
return set_.empty() ? T() : *percentile_it_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PercentileFilter<T>::Reset() {
|
||||
set_.clear();
|
||||
percentile_it_ = set_.begin();
|
||||
percentile_index_ = 0;
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_PERCENTILE_FILTER_H_
|
||||
28
src/common/rtc_base/numerics/rational.h
Normal file
28
src/common/rtc_base/numerics/rational.h
Normal file
@@ -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_
|
||||
169
src/common/rtc_base/numerics/running_statistics.h
Normal file
169
src/common/rtc_base/numerics/running_statistics.h
Normal file
@@ -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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#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 <typename T>
|
||||
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<T>& 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<T>& 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<T> 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<T> GetMax() const {
|
||||
if (size_ == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return max_;
|
||||
}
|
||||
|
||||
// Returns sum in O(1) time.
|
||||
std::optional<double> GetSum() const {
|
||||
if (size_ == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return sum_;
|
||||
}
|
||||
|
||||
// Returns mean in O(1) time.
|
||||
std::optional<double> GetMean() const {
|
||||
if (size_ == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return mean_;
|
||||
}
|
||||
|
||||
// Returns unbiased sample variance in O(1) time.
|
||||
std::optional<double> GetVariance() const {
|
||||
if (size_ == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return cumul_ / size_;
|
||||
}
|
||||
|
||||
// Returns unbiased standard deviation in O(1) time.
|
||||
std::optional<double> 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>();
|
||||
T max_ = minus_infinity_or_min<T>();
|
||||
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_
|
||||
165
src/common/rtc_base/numerics/safe_compare.h
Normal file
165
src/common/rtc_base/numerics/safe_compare.h
Normal file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rtc_base/type_traits.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
namespace safe_cmp_impl {
|
||||
|
||||
template <size_t N>
|
||||
struct LargerIntImpl : std::false_type {};
|
||||
template <>
|
||||
struct LargerIntImpl<sizeof(int8_t)> : std::true_type {
|
||||
using type = int16_t;
|
||||
};
|
||||
template <>
|
||||
struct LargerIntImpl<sizeof(int16_t)> : std::true_type {
|
||||
using type = int32_t;
|
||||
};
|
||||
template <>
|
||||
struct LargerIntImpl<sizeof(int32_t)> : std::true_type {
|
||||
using type = int64_t;
|
||||
};
|
||||
|
||||
// LargerInt<T1, T2>::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<T1, T2>::type is an alias
|
||||
// for it.
|
||||
template <typename T1, typename T2>
|
||||
struct LargerInt
|
||||
: LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*)
|
||||
? sizeof(T1)
|
||||
: 0> {};
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::make_unsigned<T>::type MakeUnsigned(T a) {
|
||||
return static_cast<typename std::make_unsigned<T>::type>(a);
|
||||
}
|
||||
|
||||
// Overload for when both T1 and T2 have the same signedness.
|
||||
template <typename Op, typename T1, typename T2,
|
||||
typename std::enable_if<std::is_signed<T1>::value ==
|
||||
std::is_signed<T2>::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 <typename Op, typename T1, typename T2,
|
||||
typename std::enable_if<std::is_signed<T1>::value &&
|
||||
std::is_unsigned<T2>::value &&
|
||||
LargerInt<T2, T1>::value>::type* = nullptr>
|
||||
constexpr bool Cmp(T1 a, T2 b) {
|
||||
return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b));
|
||||
}
|
||||
|
||||
// Overload for unsigned - signed comparison that can be promoted to a bigger
|
||||
// signed type.
|
||||
template <typename Op, typename T1, typename T2,
|
||||
typename std::enable_if<std::is_unsigned<T1>::value &&
|
||||
std::is_signed<T2>::value &&
|
||||
LargerInt<T1, T2>::value>::type* = nullptr>
|
||||
constexpr bool Cmp(T1 a, T2 b) {
|
||||
return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b);
|
||||
}
|
||||
|
||||
// Overload for signed - unsigned comparison that can't be promoted to a bigger
|
||||
// signed type.
|
||||
template <typename Op, typename T1, typename T2,
|
||||
typename std::enable_if<std::is_signed<T1>::value &&
|
||||
std::is_unsigned<T2>::value &&
|
||||
!LargerInt<T2, T1>::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 <typename Op, typename T1, typename T2,
|
||||
typename std::enable_if<std::is_unsigned<T1>::value &&
|
||||
std::is_signed<T2>::value &&
|
||||
!LargerInt<T1, T2>::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 <typename T1, typename T2> \
|
||||
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 <typename T1, typename T2> \
|
||||
constexpr \
|
||||
typename std::enable_if<IsIntlike<T1>::value && IsIntlike<T2>::value, \
|
||||
bool>::type Safe##name(T1 a, T2 b) { \
|
||||
/* Unary plus here turns enums into real integral types. */ \
|
||||
return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b); \
|
||||
} \
|
||||
template <typename T1, typename T2> \
|
||||
constexpr \
|
||||
typename std::enable_if<!IsIntlike<T1>::value || !IsIntlike<T2>::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_
|
||||
70
src/common/rtc_base/numerics/safe_conversions.h
Normal file
70
src/common/rtc_base/numerics/safe_conversions.h
Normal file
@@ -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 <limits>
|
||||
|
||||
#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 <typename Dst, typename Src>
|
||||
inline constexpr bool IsValueInRangeForNumericType(Src value) {
|
||||
return internal::RangeCheck<Dst>(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 <typename Dst, typename Src>
|
||||
inline constexpr Dst checked_cast(Src value) {
|
||||
return static_cast<Dst>(value);
|
||||
}
|
||||
template <typename Dst, typename Src>
|
||||
inline constexpr Dst dchecked_cast(Src value) {
|
||||
return static_cast<Dst>(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 <typename Dst, typename Src>
|
||||
inline constexpr Dst saturated_cast(Src value) {
|
||||
// Optimization for floating point values, which already saturate.
|
||||
if (std::numeric_limits<Dst>::is_iec559) return static_cast<Dst>(value);
|
||||
|
||||
switch (internal::RangeCheck<Dst>(value)) {
|
||||
case internal::TYPE_VALID:
|
||||
return static_cast<Dst>(value);
|
||||
|
||||
case internal::TYPE_UNDERFLOW:
|
||||
return std::numeric_limits<Dst>::min();
|
||||
|
||||
case internal::TYPE_OVERFLOW:
|
||||
return std::numeric_limits<Dst>::max();
|
||||
|
||||
// Should fail only on attempting to assign NaN to a saturated integer.
|
||||
case internal::TYPE_INVALID:
|
||||
return static_cast<Dst>(value);
|
||||
}
|
||||
|
||||
return static_cast<Dst>(value);
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
179
src/common/rtc_base/numerics/safe_conversions_impl.h
Normal file
179
src/common/rtc_base/numerics/safe_conversions_impl.h
Normal file
@@ -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 <limits>
|
||||
|
||||
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 <typename Dst,
|
||||
typename Src,
|
||||
DstSign IsDstSigned =
|
||||
std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED,
|
||||
SrcSign IsSrcSigned =
|
||||
std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED>
|
||||
struct StaticRangeCheck {};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
typedef std::numeric_limits<Src> 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 <typename Dst, typename Src>
|
||||
struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> {
|
||||
static const DstRange value =
|
||||
sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE;
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
typedef std::numeric_limits<Src> 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 <typename Dst, typename Src>
|
||||
struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
|
||||
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 <typename Dst,
|
||||
typename Src,
|
||||
DstSign IsDstSigned =
|
||||
std::numeric_limits<Dst>::is_signed ? DST_SIGNED : DST_UNSIGNED,
|
||||
SrcSign IsSrcSigned =
|
||||
std::numeric_limits<Src>::is_signed ? SRC_SIGNED : SRC_UNSIGNED,
|
||||
DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::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 <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned>
|
||||
struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> {
|
||||
static constexpr RangeCheckResult Check(Src /* value */) {
|
||||
return TYPE_VALID;
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to signed narrowing.
|
||||
template <typename Dst, typename Src>
|
||||
struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
|
||||
static constexpr RangeCheckResult Check(Src value) {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
return DstLimits::is_iec559
|
||||
? BASE_NUMERIC_RANGE_CHECK_RESULT(
|
||||
value <= static_cast<Src>(DstLimits::max()),
|
||||
value >= static_cast<Src>(DstLimits::max() * -1))
|
||||
: BASE_NUMERIC_RANGE_CHECK_RESULT(
|
||||
value <= static_cast<Src>(DstLimits::max()),
|
||||
value >= static_cast<Src>(DstLimits::min()));
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to unsigned narrowing.
|
||||
template <typename Dst, typename Src>
|
||||
struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
|
||||
static constexpr RangeCheckResult Check(Src value) {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
return BASE_NUMERIC_RANGE_CHECK_RESULT(
|
||||
value <= static_cast<Src>(DstLimits::max()), true);
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to signed.
|
||||
template <typename Dst, typename Src>
|
||||
struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
|
||||
static constexpr RangeCheckResult Check(Src value) {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
return sizeof(Dst) > sizeof(Src)
|
||||
? TYPE_VALID
|
||||
: BASE_NUMERIC_RANGE_CHECK_RESULT(
|
||||
value <= static_cast<Src>(DstLimits::max()), true);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to unsigned.
|
||||
template <typename Dst, typename Src>
|
||||
struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
|
||||
typedef std::numeric_limits<Dst> DstLimits;
|
||||
typedef std::numeric_limits<Src> 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<Src>(0))
|
||||
: BASE_NUMERIC_RANGE_CHECK_RESULT(
|
||||
value <= static_cast<Src>(DstLimits::max()),
|
||||
value >= static_cast<Src>(0));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
inline constexpr RangeCheckResult RangeCheck(Src value) {
|
||||
static_assert(std::numeric_limits<Src>::is_specialized,
|
||||
"argument must be numeric");
|
||||
static_assert(std::numeric_limits<Dst>::is_specialized,
|
||||
"result must be numeric");
|
||||
return RangeCheckImpl<Dst, Src>::Check(value);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
318
src/common/rtc_base/numerics/safe_minmax.h
Normal file
318
src/common/rtc_base/numerics/safe_minmax.h
Normal file
@@ -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<int>(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 <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#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 <typename T>
|
||||
struct Limits {
|
||||
static constexpr T lowest = std::numeric_limits<T>::lowest();
|
||||
static constexpr T max = std::numeric_limits<T>::max();
|
||||
};
|
||||
|
||||
template <typename T, bool is_enum = std::is_enum<T>::value>
|
||||
struct UnderlyingType;
|
||||
|
||||
template <typename T>
|
||||
struct UnderlyingType<T, false> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct UnderlyingType<T, true> {
|
||||
using type = typename std::underlying_type<T>::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 <typename T1, typename T2, bool int1 = IsIntlike<T1>::value,
|
||||
bool int2 = IsIntlike<T2>::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 <typename T1, typename T2>
|
||||
struct MType<T1, T2, false, false> {
|
||||
using min_t = typename std::common_type<T1, T2>::type;
|
||||
static_assert(std::is_same<min_t, T1>::value ||
|
||||
std::is_same<min_t, T2>::value,
|
||||
"");
|
||||
|
||||
using max_t = typename std::common_type<T1, T2>::type;
|
||||
static_assert(std::is_same<max_t, T1>::value ||
|
||||
std::is_same<max_t, T2>::value,
|
||||
"");
|
||||
};
|
||||
|
||||
// Specialization for when both types are integral.
|
||||
template <typename T1, typename T2>
|
||||
struct MType<T1, T2, true, true> {
|
||||
// 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<T1>::lowest, Limits<T2>::lowest), T1,
|
||||
typename std::conditional<
|
||||
SafeGt(Limits<T1>::lowest, Limits<T2>::lowest), T2,
|
||||
typename std::conditional<SafeLe(Limits<T1>::max, Limits<T2>::max),
|
||||
T1, T2>::type>::type>::type;
|
||||
static_assert(std::is_same<min_t, T1>::value ||
|
||||
std::is_same<min_t, T2>::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<T1>::max, Limits<T2>::max) ||
|
||||
SafeEq(Limits<T1>::lowest, Limits<T2>::lowest),
|
||||
"integer types with the same max should have the same min");
|
||||
using max_t =
|
||||
typename std::conditional<SafeGe(Limits<T1>::max, Limits<T2>::max), T1,
|
||||
T2>::type;
|
||||
static_assert(std::is_same<max_t, T1>::value ||
|
||||
std::is_same<max_t, T2>::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 <typename A, typename B>
|
||||
struct TypeOr {
|
||||
using type = typename std::conditional<std::is_same<A, DefaultType>::value, B,
|
||||
A>::type;
|
||||
static_assert(SafeLe(Limits<type>::lowest, Limits<B>::lowest) &&
|
||||
SafeGe(Limits<type>::max, Limits<B>::max),
|
||||
"The specified type isn't large enough");
|
||||
static_assert(IsIntlike<type>::value == IsIntlike<B>::value &&
|
||||
std::is_floating_point<type>::value ==
|
||||
std::is_floating_point<type>::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<T1>::type,
|
||||
typename safe_minmax_impl::UnderlyingType<T2>::type>::min_t>::type>
|
||||
constexpr R2 SafeMin(T1 a, T2 b) {
|
||||
static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value,
|
||||
"The first argument must be integral or floating-point");
|
||||
static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value,
|
||||
"The second argument must be integral or floating-point");
|
||||
return SafeLt(a, b) ? static_cast<R2>(a) : static_cast<R2>(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<T1>::type,
|
||||
typename safe_minmax_impl::UnderlyingType<T2>::type>::max_t>::type>
|
||||
constexpr R2 SafeMax(T1 a, T2 b) {
|
||||
static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value,
|
||||
"The first argument must be integral or floating-point");
|
||||
static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value,
|
||||
"The second argument must be integral or floating-point");
|
||||
return SafeGt(a, b) ? static_cast<R2>(a) : static_cast<R2>(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 <typename T, typename L, typename H, bool int1 = IsIntlike<T>::value,
|
||||
bool int2 = IsIntlike<L>::value, bool int3 = IsIntlike<H>::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 <typename T, typename L, typename H>
|
||||
struct ClampType<T, L, H, false, false, false> {
|
||||
using type = typename std::common_type<T, L, H>::type;
|
||||
};
|
||||
|
||||
// Specialization for when all three types are integral.
|
||||
template <typename T, typename L, typename H>
|
||||
struct ClampType<T, L, H, true, true, true> {
|
||||
private:
|
||||
// Range of the return value. The return type must be able to represent this
|
||||
// full range.
|
||||
static constexpr auto r_min =
|
||||
SafeMax(Limits<L>::lowest, SafeMin(Limits<H>::lowest, Limits<T>::lowest));
|
||||
static constexpr auto r_max =
|
||||
SafeMin(Limits<H>::max, SafeMax(Limits<L>::max, Limits<T>::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 <typename A>
|
||||
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<A>::lowest, r_min) && SafeLe(r_max, Limits<A>::max);
|
||||
|
||||
public:
|
||||
static constexpr bool value = not_too_large && range_contained;
|
||||
};
|
||||
|
||||
using best_signed_type = typename std::conditional<
|
||||
AcceptableType<int8_t>::value, int8_t,
|
||||
typename std::conditional<
|
||||
AcceptableType<int16_t>::value, int16_t,
|
||||
typename std::conditional<AcceptableType<int32_t>::value, int32_t,
|
||||
int64_t>::type>::type>::type;
|
||||
|
||||
using best_unsigned_type = typename std::conditional<
|
||||
AcceptableType<uint8_t>::value, uint8_t,
|
||||
typename std::conditional<
|
||||
AcceptableType<uint16_t>::value, uint16_t,
|
||||
typename std::conditional<AcceptableType<uint32_t>::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<T>::value,
|
||||
typename std::conditional<AcceptableType<best_signed_type>::value,
|
||||
best_signed_type, best_unsigned_type>::type,
|
||||
typename std::conditional<AcceptableType<best_unsigned_type>::value,
|
||||
best_unsigned_type,
|
||||
best_signed_type>::type>::type;
|
||||
static_assert(AcceptableType<type>::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<T>::type,
|
||||
typename safe_minmax_impl::UnderlyingType<L>::type,
|
||||
typename safe_minmax_impl::UnderlyingType<H>::type>::type>::type>
|
||||
R2 SafeClamp(T x, L min, H max) {
|
||||
static_assert(IsIntlike<H>::value || std::is_floating_point<H>::value,
|
||||
"The first argument must be integral or floating-point");
|
||||
static_assert(IsIntlike<T>::value || std::is_floating_point<T>::value,
|
||||
"The second argument must be integral or floating-point");
|
||||
static_assert(IsIntlike<L>::value || std::is_floating_point<L>::value,
|
||||
"The third argument must be integral or floating-point");
|
||||
return SafeLe(x, min) ? static_cast<R2>(min)
|
||||
: SafeGe(x, max) ? static_cast<R2>(max)
|
||||
: static_cast<R2>(x);
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_SAFE_MINMAX_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 <stdint.h>
|
||||
|
||||
@@ -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 <typename T, T M = 0>
|
||||
@@ -70,4 +76,6 @@ class SeqNumUnwrapper {
|
||||
using RtpTimestampUnwrapper = SeqNumUnwrapper<uint32_t>;
|
||||
using RtpSequenceNumberUnwrapper = SeqNumUnwrapper<uint16_t>;
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_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 <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#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<T, M>(b, a); }
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_
|
||||
75
src/common/rtc_base/ref_counter.h
Normal file
75
src/common/rtc_base/ref_counter.h
Normal file
@@ -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 <atomic>
|
||||
|
||||
#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<int> ref_count_;
|
||||
};
|
||||
|
||||
} // namespace webrtc_impl
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_REF_COUNTER_H_
|
||||
97
src/common/rtc_base/system_time.cc
Normal file
97
src/common/rtc_base/system_time.cc
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#if defined(WEBRTC_POSIX)
|
||||
#include <sys/time.h>
|
||||
#if defined(WEBRTC_MAC)
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
// clang-format off
|
||||
// clang formatting would put <windows.h> last,
|
||||
// which leads to compilation failure.
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <sys/timeb.h>
|
||||
// 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<int64_t>(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<int64_t>(ts.tv_sec) +
|
||||
static_cast<int64_t>(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
|
||||
24
src/common/rtc_base/system_time.h
Normal file
24
src/common/rtc_base/system_time.h
Normal file
@@ -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 <cstdint>
|
||||
|
||||
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_
|
||||
238
src/common/rtc_base/time_utils.cc
Normal file
238
src/common/rtc_base/time_utils.cc
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#if defined(WEBRTC_POSIX)
|
||||
#include <sys/time.h>
|
||||
#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 <minwinbase.h>
|
||||
#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<int64_t>(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<int64_t>(
|
||||
(rtc::dchecked_cast<uint64_t>(qpcnt.QuadPart) * 100000 /
|
||||
rtc::dchecked_cast<uint64_t>(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<int64_t>(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<int64_t>(qpfreq.QuadPart);
|
||||
|
||||
LARGE_INTEGER qpcnt;
|
||||
QueryPerformanceCounter(&qpcnt);
|
||||
time_since_os_start_ns_ = rtc::dchecked_cast<int64_t>(
|
||||
(rtc::dchecked_cast<uint64_t>(qpcnt.QuadPart) * 100000 /
|
||||
rtc::dchecked_cast<uint64_t>(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<int64_t>(SystemTimeNanos() / kNumNanosecsPerMillisec);
|
||||
}
|
||||
|
||||
int64_t TimeNanos() {
|
||||
if (g_clock) {
|
||||
return g_clock->TimeNanos();
|
||||
}
|
||||
return SystemTimeNanos();
|
||||
}
|
||||
|
||||
uint32_t Time32() {
|
||||
return static_cast<uint32_t>(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<int64_t>(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<int64_t>(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
|
||||
132
src/common/rtc_base/time_utils.h
Normal file
132
src/common/rtc_base/time_utils.h
Normal file
@@ -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 <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#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_
|
||||
@@ -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 <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
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 <typename DS, typename T>
|
||||
@@ -130,4 +136,6 @@ static_assert(!IsIntlike<S>::value, "");
|
||||
|
||||
} // namespace test_enum_intlike
|
||||
|
||||
#endif
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_TYPE_TRAITS_H_
|
||||
@@ -16,48 +16,46 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<BitrateEstimator>()) {}
|
||||
|
||||
AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
|
||||
|
||||
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
|
||||
std::unique_ptr<BitrateEstimator> bitrate_estimator)
|
||||
: in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {}
|
||||
|
||||
void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector(
|
||||
const std::vector<PacketResult>& 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<int64_t> AcknowledgedBitrateEstimator::bitrate() const {
|
||||
std::optional<DataRate> AcknowledgedBitrateEstimator::bitrate() const {
|
||||
return bitrate_estimator_->bitrate();
|
||||
}
|
||||
|
||||
std::optional<int64_t> AcknowledgedBitrateEstimator::PeekRate() const {
|
||||
std::optional<DataRate> 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
|
||||
@@ -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 <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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<PacketResult>& packet_feedback_vector);
|
||||
std::optional<int64_t> bitrate() const;
|
||||
std::optional<int64_t> PeekRate() const;
|
||||
std::optional<DataRate> bitrate() const;
|
||||
std::optional<DataRate> PeekRate() const;
|
||||
void SetAlr(bool in_alr);
|
||||
void SetAlrEndedTime(int64_t alr_ended_time);
|
||||
void SetAlrEndedTime(Timestamp alr_ended_time);
|
||||
|
||||
private:
|
||||
std::optional<int64_t> alr_ended_time_;
|
||||
std::optional<Timestamp> alr_ended_time_;
|
||||
bool in_alr_;
|
||||
std::unique_ptr<BitrateEstimator> bitrate_estimator_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
|
||||
|
||||
330
src/qos/aimd_rate_control.cc
Normal file
330
src/qos/aimd_rate_control.cc
Normal file
@@ -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 <inttypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#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>();
|
||||
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<DataRate> 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<double>(), 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>();
|
||||
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
|
||||
109
src/qos/aimd_rate_control.h
Normal file
109
src/qos/aimd_rate_control.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#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<NetworkStateEstimate> 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<DataRate> last_decrease_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_
|
||||
@@ -10,17 +10,19 @@
|
||||
|
||||
#include "alr_detector.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#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::milliseconds>(
|
||||
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<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
|
||||
const {
|
||||
return alr_started_time_ms_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -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<int64_t> alr_started_time_ms_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
|
||||
|
||||
17
src/qos/bandwidth_usage.h
Normal file
17
src/qos/bandwidth_usage.h
Normal file
@@ -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
|
||||
@@ -15,6 +15,13 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#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<float>(uncertainty_symmetry_cap_)));
|
||||
std::min(bitrate_sample_kbps, uncertainty_symmetry_cap_.kbps<float>()));
|
||||
|
||||
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<float>(estimate_floor_));
|
||||
std::max(bitrate_estimate_kbps_, estimate_floor_.kbps<float>());
|
||||
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<float>(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<int64_t> BitrateEstimator::bitrate() const {
|
||||
std::optional<DataRate> BitrateEstimator::bitrate() const {
|
||||
if (bitrate_estimate_kbps_ < 0.f) return std::nullopt;
|
||||
return static_cast<int64_t>(bitrate_estimate_kbps_);
|
||||
return DataRate::KilobitsPerSec(bitrate_estimate_kbps_);
|
||||
}
|
||||
|
||||
std::optional<int64_t> BitrateEstimator::PeekRate() const {
|
||||
if (current_window_ms_ > 0) return sum_ / current_window_ms_;
|
||||
std::optional<DataRate> 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
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#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<int64_t> bitrate() const;
|
||||
std::optional<int64_t> PeekRate() const;
|
||||
virtual std::optional<DataRate> bitrate() const;
|
||||
std::optional<DataRate> 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
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
|
||||
|
||||
48
src/qos/bwe_defines.h
Normal file
48
src/qos/bwe_defines.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#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<DataRate>& estimated_throughput);
|
||||
~RateControlInput();
|
||||
|
||||
BandwidthUsage bw_state;
|
||||
std::optional<DataRate> estimated_throughput;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_
|
||||
@@ -122,10 +122,6 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
|
||||
}
|
||||
}
|
||||
|
||||
if (network_estimator_) {
|
||||
network_estimator_->OnTransportPacketsFeedback(report);
|
||||
SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
|
||||
}
|
||||
std::optional<int64_t> probe_bitrate =
|
||||
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
|
||||
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 <algorithm>
|
||||
@@ -6,10 +16,17 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<int64_t>(1024 * (arrival_time_offset / 1000)),
|
||||
return std::min(
|
||||
static_cast<int64_t>(1024 * arrival_time_offset.seconds<float>()),
|
||||
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<int64_t>::max();
|
||||
return TimeDelta::PlusInfinity();
|
||||
}
|
||||
if (ato == 0x1FFF) {
|
||||
return std::numeric_limits<int64_t>::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<int>(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<PacketInfo> packets) {
|
||||
// . .
|
||||
auto write_report_for_ssrc = [&](rtc::ArrayView<const PacketInfo> packets) {
|
||||
// SSRC of nth RTP stream.
|
||||
ByteWriter<uint32_t>::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<uint16_t>(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<uint16_t>::WriteBigEndian(&buffer[*position], num_reports);
|
||||
*position += 2;
|
||||
|
||||
for (const PacketInfo& packet : packets) {
|
||||
bool received =
|
||||
(packet.arrival_time_offset != std::numeric_limits<int64_t>::min()) &&
|
||||
(packet.arrival_time_offset != std::numeric_limits<int64_t>::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<uint16_t>::WriteBigEndian(&buffer[*position], 0);
|
||||
*position += 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!packets_.empty()) {
|
||||
rtc::ArrayView<const PacketInfo> 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,10 +295,9 @@ 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<int64_t>::min(),
|
||||
packets_.push_back(
|
||||
{ssrc, seq_no,
|
||||
received ? AtoToTimeDelta(packet_info) : TimeDelta::MinusInfinity(),
|
||||
ToEcnMarking(packet_info)});
|
||||
}
|
||||
if (num_reports % 2) {
|
||||
@@ -313,3 +307,5 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
|
||||
}
|
||||
return payload == payload_end;
|
||||
}
|
||||
} // namespace rtcp
|
||||
} // namespace webrtc
|
||||
|
||||
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#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<int64_t>::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<PacketInfo> packets() const { return packets_; }
|
||||
rtc::ArrayView<const PacketInfo> 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
|
||||
} // namespace rtcp
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_
|
||||
|
||||
@@ -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 <algorithm>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#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<CongestionControlFeedback::PacketInfo>& packet_feedback) {
|
||||
Timestamp feedback_time,
|
||||
std::vector<rtcp::CongestionControlFeedback::PacketInfo>& 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<int64_t>::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,
|
||||
static_cast<uint16_t>(sequence_number), static_cast<int>(ecn));
|
||||
++packet_it;
|
||||
}
|
||||
} // else - the packet has not been received yet.
|
||||
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number),
|
||||
arrival_time_offset, ecn});
|
||||
packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
|
||||
arrival_time_offset, ecn);
|
||||
}
|
||||
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
|
||||
packets_.clear();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@@ -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 <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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<CongestionControlFeedback::PacketInfo>& packet_feedback);
|
||||
Timestamp feedback_time,
|
||||
std::vector<rtcp::CongestionControlFeedback::PacketInfo>&
|
||||
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<int64_t> last_sequence_number_in_feedback_;
|
||||
@@ -43,4 +53,6 @@ class CongestionControlFeedbackTracker {
|
||||
std::vector<PacketInfo> packets_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
|
||||
|
||||
@@ -17,11 +17,20 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<StructParametersParser>
|
||||
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<int64_t> acked_bitrate,
|
||||
std::optional<int64_t> probe_bitrate,
|
||||
std::optional<NetworkStateEstimate> network_estimate, bool in_alr) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
|
||||
|
||||
const TransportPacketsFeedback& msg, std::optional<DataRate> acked_bitrate,
|
||||
std::optional<DataRate> 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<InterArrivalDelta>(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<int64_t> link_capacity) {
|
||||
DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,
|
||||
std::optional<DataRate> link_capacity) {
|
||||
RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity);
|
||||
return rate_control_.Update(input, at_time);
|
||||
}
|
||||
|
||||
DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
|
||||
std::optional<int64_t> acked_bitrate, std::optional<int64_t> probe_bitrate,
|
||||
std::optional<NetworkStateEstimate> /* state_estimate */,
|
||||
bool recovered_from_overuse, bool /* in_alr */, int64_t at_time) {
|
||||
std::optional<DataRate> acked_bitrate,
|
||||
std::optional<DataRate> 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<RtcEventBweUpdateDelayBased>(
|
||||
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<int64_t> acked_bitrate,
|
||||
int64_t* target_rate) {
|
||||
bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
|
||||
std::optional<DataRate> 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<uint32_t>* 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<uint32_t>* 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
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
@@ -13,27 +17,29 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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<StructParametersParser> 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<int64_t> acked_bitrate,
|
||||
std::optional<int64_t> probe_bitrate,
|
||||
std::optional<NetworkStateEstimate> network_estimate, bool in_alr);
|
||||
void OnRttUpdate(int64_t avg_rtt);
|
||||
bool LatestEstimate(std::vector<uint32_t>* 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<int64_t> link_capacity);
|
||||
int64_t last_estimate() const { return prev_bitrate_; }
|
||||
Result IncomingPacketFeedbackVector(const TransportPacketsFeedback& msg,
|
||||
std::optional<DataRate> acked_bitrate,
|
||||
std::optional<DataRate> probe_bitrate,
|
||||
bool in_alr);
|
||||
void OnRttUpdate(TimeDelta avg_rtt);
|
||||
bool LatestEstimate(std::vector<uint32_t>* ssrcs, DataRate* bitrate) const;
|
||||
void SetStartBitrate(DataRate start_bitrate);
|
||||
void SetMinBitrate(DataRate min_bitrate);
|
||||
TimeDelta GetExpectedBwePeriod() const;
|
||||
DataRate TriggerOveruse(Timestamp at_time,
|
||||
std::optional<DataRate> 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<int64_t> acked_bitrate,
|
||||
std::optional<int64_t> probe_bitrate,
|
||||
std::optional<NetworkStateEstimate> state_estimate,
|
||||
Timestamp at_time);
|
||||
Result MaybeUpdateEstimate(std::optional<DataRate> acked_bitrate,
|
||||
std::optional<DataRate> 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<int64_t> 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<DataRate> 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<InterArrival> video_inter_arrival_;
|
||||
std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_;
|
||||
std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_;
|
||||
@@ -105,11 +102,13 @@ class DelayBasedBwe {
|
||||
std::unique_ptr<DelayIncreaseDetectorInterface> 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
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
|
||||
|
||||
42
src/qos/delay_increase_detector_interface.h
Normal file
42
src/qos/delay_increase_detector_interface.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#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_
|
||||
150
src/qos/inter_arrival.cc
Normal file
150
src/qos/inter_arrival.cc
Normal file
@@ -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<int>(current_timestamp_group_.size) -
|
||||
static_cast<int>(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
|
||||
93
src/qos/inter_arrival.h
Normal file
93
src/qos/inter_arrival.h
Normal file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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_
|
||||
138
src/qos/inter_arrival_delta.cc
Normal file
138
src/qos/inter_arrival_delta.cc
Normal file
@@ -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 <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
#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<int>(current_timestamp_group_.size) -
|
||||
static_cast<int>(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
|
||||
89
src/qos/inter_arrival_delta.h
Normal file
89
src/qos/inter_arrival_delta.h
Normal file
@@ -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 <cstddef>
|
||||
|
||||
#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_
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#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<size_t>(std::max<int64_t>(0, bytes_remaining_));
|
||||
return rtc::saturated_cast<size_t>(std::max<int64_t>(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
|
||||
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_PACING_INTERVAL_BUDGET_H_
|
||||
|
||||
77
src/qos/link_capacity_estimator.cc
Normal file
77
src/qos/link_capacity_estimator.cc
Normal file
@@ -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 <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#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
|
||||
39
src/qos/link_capacity_estimator.h
Normal file
39
src/qos/link_capacity_estimator.h
Normal file
@@ -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 <optional>
|
||||
|
||||
#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<double> estimate_kbps_;
|
||||
double deviation_kbps_ = 0.4;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LINK_CAPACITY_ESTIMATOR_H_
|
||||
94
src/qos/overuse_detector.cc
Normal file
94
src/qos/overuse_detector.cc
Normal file
@@ -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 <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#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
|
||||
52
src/qos/overuse_detector.h
Normal file
52
src/qos/overuse_detector.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
#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_
|
||||
169
src/qos/probe_bitrate_estimator.cc
Normal file
169
src/qos/probe_bitrate_estimator.cc
Normal file
@@ -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 <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#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<DataRate> 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<DataRate>
|
||||
ProbeBitrateEstimator::FetchAndResetLastEstimatedBitrate() {
|
||||
std::optional<DataRate> 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
|
||||
58
src/qos/probe_bitrate_estimator.h
Normal file
58
src/qos/probe_bitrate_estimator.h
Normal file
@@ -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 <map>
|
||||
#include <optional>
|
||||
|
||||
#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<DataRate> HandleProbeAndEstimateBitrate(
|
||||
const PacketResult& packet_feedback);
|
||||
|
||||
std::optional<DataRate> 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<int, AggregatedCluster> clusters_;
|
||||
std::optional<DataRate> estimated_data_rate_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_PROBE_BITRATE_ESTIMATOR_H_
|
||||
@@ -127,7 +127,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
||||
packet_info.arrival_time_offset !=
|
||||
std::numeric_limits<int64_t>::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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
260
src/qos/trendline_estimator.cc
Normal file
260
src/qos/trendline_estimator.cc
Normal file
@@ -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 <math.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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<double> LinearFitSlope(
|
||||
const std::deque<TrendlineEstimator::PacketTiming>& 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<double> ComputeSlopeCap(
|
||||
const std::deque<TrendlineEstimator::PacketTiming>& 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<double>(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<double> 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
|
||||
109
src/qos/trendline_estimator.h
Normal file
109
src/qos/trendline_estimator.h
Normal file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#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<PacketTiming> 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_
|
||||
@@ -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,21 +62,20 @@ 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;
|
||||
}
|
||||
@@ -82,3 +83,5 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace rtcp
|
||||
} // namespace webrtc
|
||||
|
||||
@@ -13,13 +13,15 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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_
|
||||
|
||||
44
src/rtp/rtp_packet/rtp_header.h
Normal file
44
src/rtp/rtp_packet/rtp_header.h
Normal file
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<uint32_t> Csrcs() {
|
||||
ParseRtpData();
|
||||
std::vector<uint32_t> 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;
|
||||
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#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<uint32_t> csrcs = Csrcs();
|
||||
header->numCSRCs = rtc::dchecked_cast<uint8_t>(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
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#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<rtc::RefCountedBase> additional_data() const {
|
||||
return additional_data_;
|
||||
}
|
||||
void set_additional_data(rtc::scoped_refptr<rtc::RefCountedBase> data) {
|
||||
additional_data_ = std::move(data);
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t arrival_time_ = std::numeric_limits<int64_t>::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<rtc::RefCountedBase> additional_data_;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user