[feat] use original webrtc header defines

This commit is contained in:
dijunkun
2025-01-16 17:33:46 +08:00
parent a8e9609736
commit 6e2a52e506
90 changed files with 6959 additions and 546 deletions

View File

@@ -50,7 +50,7 @@ void RtpVideoReceiver::InsertRtpPacket(RtpPacket& rtp_packet) {
rtp_packet_received.set_arrival_time( rtp_packet_received.set_arrival_time(
std::chrono::system_clock::now().time_since_epoch().count()); 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_recovered(false);
rtp_packet_received.set_payload_type_frequency(0); rtp_packet_received.set_payload_type_frequency(0);
receive_side_congestion_controller_.OnReceivedPacket( receive_side_congestion_controller_.OnReceivedPacket(

319
src/common/api/array_view.h Normal file
View 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_

View 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_

View 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_

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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_

View File

@@ -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

View 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_

View File

@@ -1,72 +1,104 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-13 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef API_TRANSPORT_NETWORK_TYPES_H_
#define _NETWORK_TYPES_H_ #define API_TRANSPORT_NETWORK_TYPES_H_
#include <stdint.h>
#include <algorithm> #include <cmath>
#include <limits> #include <optional>
#include <vector> #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 { namespace webrtc {
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();
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 { // Use StreamsConfig for information about streams that is required for specific
int64_t at_time = std::numeric_limits<int64_t>::max(); // adjustments to the algorithms in network controllers. Especially useful
// The estimate on which the target rate is based on. // for experiments.
NetworkEstimate network_estimate; struct StreamsConfig {
int64_t target_rate = 0; StreamsConfig();
int64_t stable_target_rate = 0; StreamsConfig(const StreamsConfig&);
double cwnd_reduce_ratio = 0; ~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 { struct TargetRateConstraints {
NetworkControlUpdate() = default; TargetRateConstraints();
NetworkControlUpdate(const NetworkControlUpdate&) = default; TargetRateConstraints(const TargetRateConstraints&);
~NetworkControlUpdate() = default; ~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 { // Send side information
// return congestion_window.has_value() || pacer_config.has_value() ||
// !probe_cluster_configs.empty() ||
return target_rate.has_value();
}
std::optional<int64_t> congestion_window; struct NetworkAvailability {
// std::optional<PacerConfig> pacer_config; Timestamp at_time = Timestamp::PlusInfinity();
// std::vector<ProbeClusterConfig> probe_cluster_configs; bool network_available = false;
std::optional<TargetTransferRate> target_rate; };
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 { struct PacedPacketInfo {
PacedPacketInfo() = default; PacedPacketInfo();
PacedPacketInfo(int probe_cluster_id, int probe_cluster_min_probes, PacedPacketInfo(int probe_cluster_id, int probe_cluster_min_probes,
int probe_cluster_min_bytes) 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) {}
bool operator==(const PacedPacketInfo& rhs) const { 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;
}
// TODO(srte): Move probing info to a separate, optional struct. // TODO(srte): Move probing info to a separate, optional struct.
static constexpr int kNotAProbe = -1; static constexpr int kNotAProbe = -1;
int64_t send_bitrate = 0; DataRate send_bitrate = DataRate::BitsPerSec(0);
int probe_cluster_id = kNotAProbe; int probe_cluster_id = kNotAProbe;
int probe_cluster_min_probes = -1; int probe_cluster_min_probes = -1;
int probe_cluster_min_bytes = -1; int probe_cluster_min_bytes = -1;
@@ -74,11 +106,11 @@ struct PacedPacketInfo {
}; };
struct SentPacket { 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. // 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. // 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 // Probe cluster id and parameters including bitrate, number of packets and
// number of bytes. // number of bytes.
PacedPacketInfo pacing_info; PacedPacketInfo pacing_info;
@@ -89,73 +121,172 @@ struct SentPacket {
// each packet. // each packet.
int64_t sequence_number; int64_t sequence_number;
// Tracked data in flight when the packet was sent, excluding unacked data. // 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 { struct PacketResult {
class ReceiveTimeOrder { class ReceiveTimeOrder {
public: public:
bool operator()(const PacketResult& lhs, const PacketResult& rhs); bool operator()(const PacketResult& lhs, const PacketResult& rhs);
}; };
PacketResult() = default; PacketResult();
PacketResult(const PacketResult&) = default; PacketResult(const PacketResult&);
~PacketResult() = default; ~PacketResult();
inline bool IsReceived() const { inline bool IsReceived() const { return !receive_time.IsPlusInfinity(); }
return receive_time != std::numeric_limits<int64_t>::max();
}
SentPacket sent_packet; SentPacket sent_packet;
int64_t receive_time = std::numeric_limits<int64_t>::max(); Timestamp receive_time = Timestamp::PlusInfinity();
EcnMarking ecn = EcnMarking::kNotEct; rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
}; };
struct TransportPacketsFeedback { struct TransportPacketsFeedback {
TransportPacketsFeedback() = default; TransportPacketsFeedback();
TransportPacketsFeedback(const TransportPacketsFeedback& other) = default; TransportPacketsFeedback(const TransportPacketsFeedback& other);
~TransportPacketsFeedback() = default; ~TransportPacketsFeedback();
int64_t feedback_time = std::numeric_limits<int64_t>::max(); Timestamp feedback_time = Timestamp::PlusInfinity();
int64_t data_in_flight = 0; DataSize data_in_flight = DataSize::Zero();
bool transport_supports_ecn = false; bool transport_supports_ecn = false;
std::vector<PacketResult> packet_feedbacks; std::vector<PacketResult> packet_feedbacks;
// Arrival times for messages without send time information. // 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> ReceivedWithSendInfo() const;
std::vector<PacketResult> res; std::vector<PacketResult> LostWithSendInfo() const;
for (const PacketResult& fb : packet_feedbacks) { std::vector<PacketResult> PacketsWithFeedback() const;
if (fb.IsReceived()) { std::vector<PacketResult> SortedByReceiveTime() const;
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;
}
}; };
#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_

View 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_

View 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_

View 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_

View File

@@ -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

View File

@@ -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&section=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_

View 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

View 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_

View 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

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View File

@@ -1,11 +1,15 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-08 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_
#define _SEQUENCE_NUMBER_UNWRAPPER_H_ #define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_
#include <stdint.h> #include <stdint.h>
@@ -15,6 +19,8 @@
#include "sequence_number_util.h" #include "sequence_number_util.h"
namespace webrtc {
// A sequence number unwrapper where the first unwrapped value equals the // A sequence number unwrapper where the first unwrapped value equals the
// first value being unwrapped. // first value being unwrapped.
template <typename T, T M = 0> template <typename T, T M = 0>
@@ -70,4 +76,6 @@ class SeqNumUnwrapper {
using RtpTimestampUnwrapper = SeqNumUnwrapper<uint32_t>; using RtpTimestampUnwrapper = SeqNumUnwrapper<uint32_t>;
using RtpSequenceNumberUnwrapper = SeqNumUnwrapper<uint16_t>; using RtpSequenceNumberUnwrapper = SeqNumUnwrapper<uint16_t>;
#endif } // namespace webrtc
#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UNWRAPPER_H_

View File

@@ -1,17 +1,23 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-08 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_
#define _SEQUENCE_NUMBER_UTIL_H_ #define RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include "mod_ops.h" #include "mod_ops.h"
namespace webrtc {
// Test if the sequence number `a` is ahead or at sequence number `b`. // 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 // 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); } bool operator()(T a, T b) const { return AheadOf<T, M>(b, a); }
}; };
#endif } // namespace webrtc
#endif // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_

View 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_

View 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

View 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_

View 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

View 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_

View File

@@ -1,16 +1,22 @@
/* /*
* @Author: DI JUNKUN * Copyright 2016 The WebRTC Project Authors. All rights reserved.
* @Date: 2024-12-18 *
* 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 _TYPE_TRAITS_H_ #ifndef RTC_BASE_TYPE_TRAITS_H_
#define _TYPE_TRAITS_H_ #define RTC_BASE_TYPE_TRAITS_H_
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
namespace rtc {
// Determines if the given class has zero-argument .data() and .size() methods // Determines if the given class has zero-argument .data() and .size() methods
// whose return values are convertible to T* and size_t, respectively. // whose return values are convertible to T* and size_t, respectively.
template <typename DS, typename T> template <typename DS, typename T>
@@ -130,4 +136,6 @@ static_assert(!IsIntlike<S>::value, "");
} // namespace test_enum_intlike } // namespace test_enum_intlike
#endif } // namespace rtc
#endif // RTC_BASE_TYPE_TRAITS_H_

View File

@@ -16,48 +16,46 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "bitrate_estimator.h" #include "api/units/data_rate.h"
#include "log.h" #include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "network_types.h" #include "network_types.h"
namespace webrtc {
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator() AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator()
: AcknowledgedBitrateEstimator(std::make_unique<BitrateEstimator>()) {} : AcknowledgedBitrateEstimator(std::make_unique<BitrateEstimator>()) {}
AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {}
AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator( AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator(
std::unique_ptr<BitrateEstimator> bitrate_estimator) std::unique_ptr<BitrateEstimator> bitrate_estimator)
: in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {} : in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {}
void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector( void AcknowledgedBitrateEstimator::IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector) { 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) { for (const auto& packet : packet_feedback_vector) {
if (alr_ended_time_ && packet.sent_packet.send_time > *alr_ended_time_) { if (alr_ended_time_ && packet.sent_packet.send_time > *alr_ended_time_) {
bitrate_estimator_->ExpectFastRateChange(); bitrate_estimator_->ExpectFastRateChange();
alr_ended_time_.reset(); 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; acknowledged_estimate += packet.sent_packet.prior_unacked_data;
bitrate_estimator_->Update(packet.receive_time, acknowledged_estimate, bitrate_estimator_->Update(packet.receive_time, acknowledged_estimate,
in_alr_); in_alr_);
} }
} }
std::optional<int64_t> AcknowledgedBitrateEstimator::bitrate() const { std::optional<DataRate> AcknowledgedBitrateEstimator::bitrate() const {
return bitrate_estimator_->bitrate(); return bitrate_estimator_->bitrate();
} }
std::optional<int64_t> AcknowledgedBitrateEstimator::PeekRate() const { std::optional<DataRate> AcknowledgedBitrateEstimator::PeekRate() const {
return bitrate_estimator_->PeekRate(); 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); alr_ended_time_.emplace(alr_ended_time);
} }
void AcknowledgedBitrateEstimator::SetAlr(bool in_alr) { in_alr_ = in_alr; } void AcknowledgedBitrateEstimator::SetAlr(bool in_alr) { in_alr_ = in_alr; }
} // namespace webrtc

View File

@@ -1,19 +1,27 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-14 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#define _ACKNOWLEDGED_BITRATE_ESTIMATOR_H_ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "bitrate_estimator.h" #include "bitrate_estimator.h"
#include "network_types.h" #include "network_types.h"
namespace webrtc {
class AcknowledgedBitrateEstimator { class AcknowledgedBitrateEstimator {
public: public:
AcknowledgedBitrateEstimator( AcknowledgedBitrateEstimator(
@@ -24,15 +32,17 @@ class AcknowledgedBitrateEstimator {
void IncomingPacketFeedbackVector( void IncomingPacketFeedbackVector(
const std::vector<PacketResult>& packet_feedback_vector); const std::vector<PacketResult>& packet_feedback_vector);
std::optional<int64_t> bitrate() const; std::optional<DataRate> bitrate() const;
std::optional<int64_t> PeekRate() const; std::optional<DataRate> PeekRate() const;
void SetAlr(bool in_alr); void SetAlr(bool in_alr);
void SetAlrEndedTime(int64_t alr_ended_time); void SetAlrEndedTime(Timestamp alr_ended_time);
private: private:
std::optional<int64_t> alr_ended_time_; std::optional<Timestamp> alr_ended_time_;
bool in_alr_; bool in_alr_;
std::unique_ptr<BitrateEstimator> bitrate_estimator_; std::unique_ptr<BitrateEstimator> bitrate_estimator_;
}; };
#endif } // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_H_

View 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
View 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_

View File

@@ -10,17 +10,19 @@
#include "alr_detector.h" #include "alr_detector.h"
#include <chrono>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include "rtc_base/time_utils.h"
namespace webrtc {
AlrDetector::AlrDetector(AlrDetectorConfig config) AlrDetector::AlrDetector(AlrDetectorConfig config)
: conf_(config), alr_budget_(0, true) {} : conf_(config), alr_budget_(0, true) {}
AlrDetector::AlrDetector() : alr_budget_(0, true) {} AlrDetector::AlrDetector() : alr_budget_(0, true) {}
AlrDetector::~AlrDetector() {} AlrDetector::~AlrDetector() {}
void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t send_time_ms) { 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; bool state_changed = false;
if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio && if (alr_budget_.budget_ratio() > conf_.start_budget_level_ratio &&
!alr_started_time_ms_) { !alr_started_time_ms_) {
alr_started_time_ms_.emplace( alr_started_time_ms_.emplace(rtc::TimeMillis());
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
state_changed = true; state_changed = true;
} else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio && } else if (alr_budget_.budget_ratio() < conf_.stop_budget_level_ratio &&
alr_started_time_ms_) { alr_started_time_ms_) {
@@ -60,3 +59,5 @@ std::optional<int64_t> AlrDetector::GetApplicationLimitedRegionStartTime()
const { const {
return alr_started_time_ms_; return alr_started_time_ms_;
} }
} // namespace webrtc

View File

@@ -1,11 +1,15 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-14 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#define _ALR_DETECTOR_H_ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ALR_DETECTOR_H_
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -15,6 +19,10 @@
#include "interval_budget.h" #include "interval_budget.h"
namespace webrtc {
class RtcEventLog;
struct AlrDetectorConfig { struct AlrDetectorConfig {
// Sent traffic ratio as a function of network capacity used to determine // Sent traffic ratio as a function of network capacity used to determine
// application-limited region. ALR region start when bandwidth usage drops // application-limited region. ALR region start when bandwidth usage drops
@@ -56,5 +64,6 @@ class AlrDetector {
IntervalBudget alr_budget_; IntervalBudget alr_budget_;
std::optional<int64_t> alr_started_time_ms_; 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
View 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

View File

@@ -15,6 +15,13 @@
#include <cstdint> #include <cstdint>
#include <optional> #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 { namespace {
constexpr int kInitialRateWindowMs = 500; constexpr int kInitialRateWindowMs = 500;
constexpr int kRateWindowMs = 150; constexpr int kRateWindowMs = 150;
@@ -33,9 +40,9 @@ BitrateEstimator::BitrateEstimator()
uncertainty_scale_(10.0), uncertainty_scale_(10.0),
uncertainty_scale_in_alr_(uncertainty_scale_), uncertainty_scale_in_alr_(uncertainty_scale_),
small_sample_uncertainty_scale_(uncertainty_scale_), small_sample_uncertainty_scale_(uncertainty_scale_),
small_sample_threshold_(0), small_sample_threshold_(DataSize::Zero()),
uncertainty_symmetry_cap_(0), uncertainty_symmetry_cap_(DataRate::Zero()),
estimate_floor_(0), estimate_floor_(DataRate::Zero()),
current_window_ms_(0), current_window_ms_(0),
prev_time_ms_(-1), prev_time_ms_(-1),
bitrate_estimate_kbps_(-1.0f), bitrate_estimate_kbps_(-1.0f),
@@ -43,14 +50,14 @@ BitrateEstimator::BitrateEstimator()
BitrateEstimator::~BitrateEstimator() = default; BitrateEstimator::~BitrateEstimator() = default;
void BitrateEstimator::Update(int64_t at_time, int64_t amount, bool in_alr) { void BitrateEstimator::Update(Timestamp at_time, DataSize amount, bool in_alr) {
int rate_window_ms = noninitial_window_ms_.Get(); int rate_window_ms = noninitial_window_ms_;
// We use a larger window at the beginning to get a more stable sample that // We use a larger window at the beginning to get a more stable sample that
// we can use to initialize the estimate. // 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; bool is_small_sample = false;
float bitrate_sample_kbps = float bitrate_sample_kbps = UpdateWindow(at_time.ms(), amount.bytes(),
UpdateWindow(at_time, amount, rate_window_ms, &is_small_sample); rate_window_ms, &is_small_sample);
if (bitrate_sample_kbps < 0.0f) return; if (bitrate_sample_kbps < 0.0f) return;
if (bitrate_estimate_kbps_ < 0.0f) { if (bitrate_estimate_kbps_ < 0.0f) {
// This is the very first sample we get. Use it to initialize the estimate. // 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 = float sample_uncertainty =
scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) / scale * std::abs(bitrate_estimate_kbps_ - bitrate_sample_kbps) /
(bitrate_estimate_kbps_ + (bitrate_estimate_kbps_ +
std::min(bitrate_sample_kbps, std::min(bitrate_sample_kbps, uncertainty_symmetry_cap_.kbps<float>()));
static_cast<float>(uncertainty_symmetry_cap_)));
float sample_var = sample_uncertainty * sample_uncertainty; float sample_var = sample_uncertainty * sample_uncertainty;
// Update a bayesian estimate of the rate, weighting it lower if the sample // 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) / pred_bitrate_estimate_var * bitrate_sample_kbps) /
(sample_var + pred_bitrate_estimate_var); (sample_var + pred_bitrate_estimate_var);
bitrate_estimate_kbps_ = 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 / bitrate_estimate_var_ = sample_var * pred_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; prev_time_ms_ = now_ms;
float bitrate_sample = -1.0f; float bitrate_sample = -1.0f;
if (current_window_ms_ >= rate_window_ms) { 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); bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
current_window_ms_ -= rate_window_ms; current_window_ms_ -= rate_window_ms;
sum_ = 0; sum_ = 0;
@@ -120,13 +126,14 @@ float BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes,
return bitrate_sample; 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; 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 { std::optional<DataRate> BitrateEstimator::PeekRate() const {
if (current_window_ms_ > 0) return sum_ / current_window_ms_; if (current_window_ms_ > 0)
return DataSize::Bytes(sum_) / TimeDelta::Millis(current_window_ms_);
return std::nullopt; return std::nullopt;
} }
@@ -135,3 +142,5 @@ void BitrateEstimator::ExpectFastRateChange() {
// bitrate to change fast for the next few samples. // bitrate to change fast for the next few samples.
bitrate_estimate_var_ += 200; bitrate_estimate_var_ += 200;
} }
} // namespace webrtc

View File

@@ -1,18 +1,27 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-14 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#define _BITRATE_ESTIMATOR_H_ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_
#include <stdint.h> #include <stdint.h>
#include <optional> #include <optional>
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "constrained.h" #include "constrained.h"
namespace webrtc {
// Computes a bayesian estimate of the throughput given acks containing // Computes a bayesian estimate of the throughput given acks containing
// the arrival time and payload size. Samples which are far from the current // 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 // estimate or are based on few packets are given a smaller weight, as they
@@ -20,12 +29,12 @@
// unrelated to congestion. // unrelated to congestion.
class BitrateEstimator { class BitrateEstimator {
public: public:
explicit BitrateEstimator(); BitrateEstimator();
virtual ~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; virtual std::optional<DataRate> bitrate() const;
std::optional<int64_t> PeekRate() const; std::optional<DataRate> PeekRate() const;
virtual void ExpectFastRateChange(); virtual void ExpectFastRateChange();
@@ -38,13 +47,15 @@ class BitrateEstimator {
double uncertainty_scale_; double uncertainty_scale_;
double uncertainty_scale_in_alr_; double uncertainty_scale_in_alr_;
double small_sample_uncertainty_scale_; double small_sample_uncertainty_scale_;
int64_t small_sample_threshold_; DataSize small_sample_threshold_;
int64_t uncertainty_symmetry_cap_; DataRate uncertainty_symmetry_cap_;
int64_t estimate_floor_; DataRate estimate_floor_;
int64_t current_window_ms_; int64_t current_window_ms_;
int64_t prev_time_ms_; int64_t prev_time_ms_;
float bitrate_estimate_kbps_; float bitrate_estimate_kbps_;
float bitrate_estimate_var_; float bitrate_estimate_var_;
}; };
#endif } // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_BITRATE_ESTIMATOR_H_

48
src/qos/bwe_defines.h Normal file
View 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_

View File

@@ -122,10 +122,6 @@ NetworkControlUpdate CongestionControl::OnTransportPacketsFeedback(
} }
} }
if (network_estimator_) {
network_estimator_->OnTransportPacketsFeedback(report);
SetNetworkStateEstimate(network_estimator_->GetCurrentEstimate());
}
std::optional<int64_t> probe_bitrate = std::optional<int64_t> probe_bitrate =
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate(); probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate && if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&

View File

@@ -7,6 +7,7 @@
#include "acknowledged_bitrate_estimator.h" #include "acknowledged_bitrate_estimator.h"
#include "alr_detector.h" #include "alr_detector.h"
#include "congestion_window_pushback_controller.h" #include "congestion_window_pushback_controller.h"
#include "delay_based_bwe.h"
#include "network_types.h" #include "network_types.h"
#include "send_side_bandwidth_estimation.h" #include "send_side_bandwidth_estimation.h"

View File

@@ -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 "congestion_control_feedback.h"
#include <algorithm> #include <algorithm>
@@ -6,10 +16,17 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "api/array_view.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "byte_io.h" #include "byte_io.h"
#include "common_header.h"
#include "log.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 | |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 { namespace {
constexpr size_t kSenderSsrcLength = 4; 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 // 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 // RTP packet is after the time represented by the RTS field, then an ATO value
// of 0x1FFF MUST be reported for the packet. // of 0x1FFF MUST be reported for the packet.
uint16_t To13bitAto(int64_t arrival_time_offset) { uint16_t To13bitAto(TimeDelta arrival_time_offset) {
if (arrival_time_offset < 0) { if (arrival_time_offset < TimeDelta::Zero()) {
return 0x1FFF; 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{0x1FFE});
} }
int64_t AtoToTimeDelta(uint16_t receive_info) { TimeDelta AtoToTimeDelta(uint16_t receive_info) {
// receive_info // receive_info
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset | // |R|ECN| Arrival time offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// ato -> second
const uint16_t ato = receive_info & 0x1FFF; const uint16_t ato = receive_info & 0x1FFF;
if (ato == 0x1FFE) { if (ato == 0x1FFE) {
return std::numeric_limits<int64_t>::max(); return TimeDelta::PlusInfinity();
} }
if (ato == 0x1FFF) { 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) { switch (ecn_marking) {
case EcnMarking::kNotEct: case rtc::EcnMarking::kNotEct:
return 0; return 0;
case EcnMarking::kEct1: case rtc::EcnMarking::kEct1:
return kEcnEct1 << 13; return kEcnEct1 << 13;
case EcnMarking::kEct0: case rtc::EcnMarking::kEct0:
return kEcnEct0 << 13; return kEcnEct0 << 13;
case EcnMarking::kCe: case rtc::EcnMarking::kCe:
return kEcnCe << 13; 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; const uint16_t ecn = (receive_info >> 13) & 0b11;
if (ecn == kEcnEct1) { if (ecn == kEcnEct1) {
return EcnMarking::kEct1; return rtc::EcnMarking::kEct1;
} }
if (ecn == kEcnEct0) { if (ecn == kEcnEct0) {
return EcnMarking::kEct0; return rtc::EcnMarking::kEct0;
} }
if (ecn == kEcnCe) { if (ecn == kEcnCe) {
return EcnMarking::kCe; return rtc::EcnMarking::kCe;
} }
return EcnMarking::kNotEct; return rtc::EcnMarking::kNotEct;
} }
} // namespace } // namespace
@@ -145,9 +140,9 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
size_t max_length, size_t max_length,
PacketReadyCallback callback) const { PacketReadyCallback callback) const {
// Ensure there is enough room for this packet. // Ensure there is enough room for this packet.
// while (*position + BlockLength() > max_length) { while (*position + BlockLength() > max_length) {
// if (!OnBufferFull(buffer, position, callback)) return false; if (!OnBufferFull(buffer, position, callback)) return false;
// } }
const size_t position_end = *position + BlockLength(); const size_t position_end = *position + BlockLength();
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -161,14 +156,14 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
*position += 4; *position += 4;
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of 1st RTP Stream | // | SSRC of nth RTP Stream |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | num_reports | // | begin_seq | num_reports |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset | ... . // |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. // SSRC of nth RTP stream.
ByteWriter<uint32_t>::WriteBigEndian(&buffer[*position], packets[0].ssrc); ByteWriter<uint32_t>::WriteBigEndian(&buffer[*position], packets[0].ssrc);
*position += 4; *position += 4;
@@ -180,27 +175,18 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, size_t* position,
// num_reports // num_reports
uint16_t num_reports = packets.size(); 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 // 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 // blocks (i.e., it MUST NOT report on more than one quarter of the
// sequence number space in a single report). // sequence number space in a single report).
if (num_reports > 16384) { if (num_reports > 16384) {
LOG_FATAL("Unexpected number of reports: {}", num_reports); LOG_ERROR("Unexpected number of reports:{}", num_reports);
return false; return;
} }
ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], num_reports); ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], num_reports);
*position += 2; *position += 2;
for (const PacketInfo& packet : packets) { for (const PacketInfo& packet : packets) {
bool received = bool received = packet.arrival_time_offset.IsFinite();
(packet.arrival_time_offset != std::numeric_limits<int64_t>::min()) &&
(packet.arrival_time_offset != std::numeric_limits<int64_t>::max());
uint16_t packet_info = 0; uint16_t packet_info = 0;
if (received) { if (received) {
packet_info = 0x8000 | To2BitEcn(packet.ecn) | 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); ByteWriter<uint16_t>::WriteBigEndian(&buffer[*position], 0);
*position += 2; *position += 2;
} }
return true;
}; };
if (!packets_.empty()) { rtc::ArrayView<const PacketInfo> remaining(packets_);
while (!remaining.empty()) {
int number_of_packets_for_ssrc = 0; int number_of_packets_for_ssrc = 0;
uint32_t ssrc = packets_[0].ssrc; uint32_t ssrc = remaining[0].ssrc;
for (const PacketInfo& packet_info : packets_) { for (const PacketInfo& packet_info : remaining) {
if (packet_info.ssrc != ssrc) {
break;
}
++number_of_packets_for_ssrc; ++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_); report_timestamp_compact_ntp_);
*position += 4; *position += 4;
if (*position != position_end) {
return false;
}
return true; return true;
} }
@@ -257,6 +243,15 @@ size_t CongestionControlFeedback::BlockLength() const {
uint32_t ssrc = packets_.front().ssrc; uint32_t ssrc = packets_.front().ssrc;
uint16_t first_sequence_number = packets_.front().sequence_number; 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 = uint16_t number_of_packets =
packets_.back().sequence_number - first_sequence_number + 1; packets_.back().sequence_number - first_sequence_number + 1;
total_size += increase_size_per_ssrc(number_of_packets); total_size += increase_size_per_ssrc(number_of_packets);
@@ -264,7 +259,7 @@ size_t CongestionControlFeedback::BlockLength() const {
return total_size; 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 = packet.payload();
const uint8_t* payload_end = packet.payload() + packet.payload_size_bytes(); 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; uint16_t seq_no = base_seqno + i;
bool received = (packet_info & 0x8000); bool received = (packet_info & 0x8000);
packets_.push_back(PacketInfo{ssrc, seq_no, packets_.push_back(
received {ssrc, seq_no,
? AtoToTimeDelta(packet_info) received ? AtoToTimeDelta(packet_info) : TimeDelta::MinusInfinity(),
: std::numeric_limits<int64_t>::min(),
ToEcnMarking(packet_info)}); ToEcnMarking(packet_info)});
} }
if (num_reports % 2) { if (num_reports % 2) {
@@ -313,3 +307,5 @@ bool CongestionControlFeedback::Parse(const RtcpCommonHeader& packet) {
} }
return payload == payload_end; return payload == payload_end;
} }
} // namespace rtcp
} // namespace webrtc

View File

@@ -1,20 +1,28 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
* @Date: 2024-12-18 *
* Copyright (c) 2024 by DI JUNKUN, 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_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_
#ifndef _CONGESTION_CONTROL_FEEDBACK_H_ #define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_
#define _CONGESTION_CONTROL_FEEDBACK_H_
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <limits>
#include <vector> #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" #include "rtp_feedback.h"
namespace webrtc {
namespace rtcp {
// Congestion control feedback message as specified in // Congestion control feedback message as specified in
// https://www.rfc-editor.org/rfc/rfc8888.html // https://www.rfc-editor.org/rfc/rfc8888.html
class CongestionControlFeedback : public RtpFeedback { class CongestionControlFeedback : public RtpFeedback {
@@ -24,8 +32,8 @@ class CongestionControlFeedback : public RtpFeedback {
uint16_t sequence_number = 0; uint16_t sequence_number = 0;
// Time offset from report timestamp. Minus infinity if the packet has not // Time offset from report timestamp. Minus infinity if the packet has not
// been received. // been received.
int64_t arrival_time_offset = std::numeric_limits<int64_t>::min(); TimeDelta arrival_time_offset = TimeDelta::MinusInfinity();
EcnMarking ecn = EcnMarking::kNotEct; rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
}; };
static constexpr uint8_t kFeedbackMessageType = 11; static constexpr uint8_t kFeedbackMessageType = 11;
@@ -37,9 +45,9 @@ class CongestionControlFeedback : public RtpFeedback {
uint32_t report_timestamp_compact_ntp); uint32_t report_timestamp_compact_ntp);
CongestionControlFeedback() = default; 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 { uint32_t report_timestamp_compact_ntp() const {
return report_timestamp_compact_ntp_; return report_timestamp_compact_ntp_;
@@ -55,4 +63,7 @@ class CongestionControlFeedback : public RtpFeedback {
uint32_t report_timestamp_compact_ntp_ = 0; uint32_t report_timestamp_compact_ntp_ = 0;
}; };
#endif } // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_CONGESTION_CONTROL_FEEDBACK_H_

View File

@@ -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 "congestion_control_feedback_tracker.h"
#include <algorithm>
#include <cstdint> #include <cstdint>
#include <tuple> #include <tuple>
#include <vector> #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( void CongestionControlFeedbackTracker::ReceivedPacket(
RtpPacketReceived& packet) { const RtpPacketReceived& packet) {
int64_t unwrapped_sequence_number = int64_t unwrapped_sequence_number =
unwrapper_.Unwrap(packet.SequenceNumber()); unwrapper_.Unwrap(packet.SequenceNumber());
if (last_sequence_number_in_feedback_ && if (last_sequence_number_in_feedback_ &&
@@ -26,13 +41,13 @@ void CongestionControlFeedbackTracker::ReceivedPacket(
// received. // received.
last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1; last_sequence_number_in_feedback_ = unwrapped_sequence_number - 1;
} }
packets_.push_back({packet.Ssrc(), unwrapped_sequence_number, packets_.emplace_back(packet.Ssrc(), unwrapped_sequence_number,
packet.arrival_time(), packet.ecn()}); packet.arrival_time(), packet.ecn());
} }
void CongestionControlFeedbackTracker::AddPacketsToFeedback( void CongestionControlFeedbackTracker::AddPacketsToFeedback(
int64_t feedback_time, Timestamp feedback_time,
std::vector<CongestionControlFeedback::PacketInfo>& packet_feedback) { std::vector<rtcp::CongestionControlFeedback::PacketInfo>& packet_feedback) {
if (packets_.empty()) { if (packets_.empty()) {
return; return;
} }
@@ -51,17 +66,8 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
for (int64_t sequence_number = *last_sequence_number_in_feedback_ + 1; for (int64_t sequence_number = *last_sequence_number_in_feedback_ + 1;
sequence_number <= packets_.back().unwrapped_sequence_number; sequence_number <= packets_.back().unwrapped_sequence_number;
++sequence_number) { ++sequence_number) {
if (packet_it == packets_.end()) { rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
LOG_FATAL("Invalid packet_it"); TimeDelta arrival_time_offset = TimeDelta::MinusInfinity();
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();
if (sequence_number == packet_it->unwrapped_sequence_number) { if (sequence_number == packet_it->unwrapped_sequence_number) {
arrival_time_offset = feedback_time - packet_it->arrival_time; arrival_time_offset = feedback_time - packet_it->arrival_time;
@@ -70,22 +76,24 @@ void CongestionControlFeedbackTracker::AddPacketsToFeedback(
while (packet_it != packets_.end() && while (packet_it != packets_.end() &&
packet_it->unwrapped_sequence_number == sequence_number) { packet_it->unwrapped_sequence_number == sequence_number) {
// According to RFC 8888: // According to RFC 8888:
// If duplicate copies of a particular RTP packet are received, then the // If duplicate copies of a particular RTP packet are received, then
// arrival time of the first copy to arrive MUST be reported. If any of // the arrival time of the first copy to arrive MUST be reported. If
// the copies of the duplicated packet are ECN-CE marked, then an ECN-CE // any of the copies of the duplicated packet are ECN-CE marked, then
// mark MUST be reported for that packet; otherwise, the ECN mark of the // an ECN-CE mark MUST be reported for that packet; otherwise, the ECN
// first copy to arrive is reported. // mark of the first copy to arrive is reported.
if (packet_it->ecn == EcnMarking::kCe) { if (packet_it->ecn == rtc::EcnMarking::kCe) {
ecn = EcnMarking::kCe; ecn = rtc::EcnMarking::kCe;
} }
LOG_WARN("Received duplicate packet ssrc:{} seq:{} ecn:{}", ssrc, LOG_WARN("Received duplicate packet ssrc:{} seq:{} ecn:{}", ssrc,
static_cast<uint16_t>(sequence_number), static_cast<int>(ecn)); static_cast<uint16_t>(sequence_number), static_cast<int>(ecn));
++packet_it; ++packet_it;
} }
} // else - the packet has not been received yet. } // else - the packet has not been received yet.
packet_feedback.push_back({ssrc, static_cast<uint16_t>(sequence_number), packet_feedback.emplace_back(ssrc, static_cast<uint16_t>(sequence_number),
arrival_time_offset, ecn}); arrival_time_offset, ecn);
} }
last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number; last_sequence_number_in_feedback_ = packets_.back().unwrapped_sequence_number;
packets_.clear(); packets_.clear();
} }
} // namespace webrtc

View File

@@ -1,40 +1,50 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
* @Date: 2024-12-18 *
* Copyright (c) 2024 by DI JUNKUN, 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_ #include <cstdint>
#define _CONGESTION_CONTROL_FEEDBACK_TRACKER_H_
#include <optional> #include <optional>
#include <vector> #include <vector>
#include "api/units/timestamp.h"
#include "congestion_control_feedback.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 "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 { class CongestionControlFeedbackTracker {
public: public:
CongestionControlFeedbackTracker() = default; CongestionControlFeedbackTracker() = default;
void ReceivedPacket(RtpPacketReceived& packet); void ReceivedPacket(const RtpPacketReceived& packet);
// Adds received packets to `packet_feedback` // Adds received packets to `packet_feedback`
// RTP sequence numbers are continous from the last created feedback unless // RTP sequence numbers are continous from the last created feedback unless
// reordering has occured between feedback packets. If so, the sequence // reordering has occured between feedback packets. If so, the sequence
// number range may overlap with previousely sent feedback. // number range may overlap with previousely sent feedback.
void AddPacketsToFeedback( void AddPacketsToFeedback(
int64_t feedback_time, Timestamp feedback_time,
std::vector<CongestionControlFeedback::PacketInfo>& packet_feedback); std::vector<rtcp::CongestionControlFeedback::PacketInfo>&
packet_feedback);
private: private:
struct PacketInfo { struct PacketInfo {
uint32_t ssrc; uint32_t ssrc;
int64_t unwrapped_sequence_number = 0; int64_t unwrapped_sequence_number = 0;
int64_t arrival_time; Timestamp arrival_time;
EcnMarking ecn = EcnMarking::kNotEct; rtc::EcnMarking ecn = rtc::EcnMarking::kNotEct;
}; };
std::optional<int64_t> last_sequence_number_in_feedback_; std::optional<int64_t> last_sequence_number_in_feedback_;
@@ -43,4 +53,6 @@ class CongestionControlFeedbackTracker {
std::vector<PacketInfo> packets_; std::vector<PacketInfo> packets_;
}; };
#endif } // namespace webrtc
#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_CONGESTION_CONTROL_FEEDBACK_TRACKER_H_

View File

@@ -17,11 +17,20 @@
#include <utility> #include <utility>
#include <vector> #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 { namespace {
constexpr int64_t kStreamTimeOut = int64_t::Seconds(2); constexpr TimeDelta kStreamTimeOut = TimeDelta::Seconds(2);
constexpr int64_t kSendTimeGroupLength = int64_t::Millis(5); constexpr TimeDelta kSendTimeGroupLength = TimeDelta::Millis(5);
// This ssrc is used to fulfill the current API but will be removed // This ssrc is used to fulfill the current API but will be removed
// after the API has been changed. // after the API has been changed.
@@ -30,72 +39,40 @@ constexpr uint32_t kFixedSsrc = 0;
constexpr char BweSeparateAudioPacketsSettings::kKey[]; 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() DelayBasedBwe::Result::Result()
: updated(false), : updated(false),
probe(false), probe(false),
target_bitrate(int64_t::Zero()), target_bitrate(DataRate::Zero()),
recovered_from_overuse(false), recovered_from_overuse(false),
delay_detector_state(BandwidthUsage::kBwNormal) {} delay_detector_state(BandwidthUsage::kBwNormal) {}
DelayBasedBwe::DelayBasedBwe(const FieldTrialsView* key_value_config, DelayBasedBwe::DelayBasedBwe()
RtcEventLog* event_log, : audio_packets_since_last_video_(0),
NetworkStatePredictor* network_state_predictor) last_video_packet_recv_time_(Timestamp::MinusInfinity()),
: event_log_(event_log), last_seen_packet_(Timestamp::MinusInfinity()),
key_value_config_(key_value_config), video_delay_detector_(new TrendlineEstimator()),
separate_audio_(key_value_config), audio_delay_detector_(new TrendlineEstimator()),
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_)),
active_delay_detector_(video_delay_detector_.get()), active_delay_detector_(video_delay_detector_.get()),
last_seen_packet_(int64_t::MinusInfinity()),
uma_recorded_(false), uma_recorded_(false),
rate_control_(*key_value_config, /*send_side=*/true), rate_control_(true),
prev_bitrate_(int64_t::Zero()), prev_bitrate_(DataRate::Zero()),
prev_state_(BandwidthUsage::kBwNormal) { prev_state_(BandwidthUsage::kBwNormal) {}
RTC_LOG(LS_INFO)
<< "Initialized DelayBasedBwe with separate audio overuse detection"
<< separate_audio_.Parser()->Encode();
}
DelayBasedBwe::~DelayBasedBwe() {} DelayBasedBwe::~DelayBasedBwe() {}
DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector( DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
const TransportPacketsFeedback& msg, std::optional<int64_t> acked_bitrate, const TransportPacketsFeedback& msg, std::optional<DataRate> acked_bitrate,
std::optional<int64_t> probe_bitrate, std::optional<DataRate> probe_bitrate, bool in_alr) {
std::optional<NetworkStateEstimate> network_estimate, bool in_alr) {
RTC_DCHECK_RUNS_SERIALIZED(&network_race_);
auto packet_feedback_vector = msg.SortedByReceiveTime(); auto packet_feedback_vector = msg.SortedByReceiveTime();
// TODO(holmer): An empty feedback vector here likely means that // TODO(holmer): An empty feedback vector here likely means that
// all acks were too late and that the send time history had // all acks were too late and that the send time history had
// timed out. We should reduce the rate when this occurs. // timed out. We should reduce the rate when this occurs.
if (packet_feedback_vector.empty()) { if (packet_feedback_vector.empty()) {
RTC_LOG(LS_WARNING) << "Very late feedback received."; LOG_WARN("Very late feedback received");
return DelayBasedBwe::Result(); return DelayBasedBwe::Result();
} }
if (!uma_recorded_) { if (!uma_recorded_) {
RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
BweNames::kSendSideTransportSeqNum,
BweNames::kBweNamesMax);
uma_recorded_ = true; uma_recorded_ = true;
} }
bool delayed_feedback = true; bool delayed_feedback = true;
@@ -117,14 +94,12 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
return Result(); return Result();
} }
rate_control_.SetInApplicationLimitedRegion(in_alr); rate_control_.SetInApplicationLimitedRegion(in_alr);
rate_control_.SetNetworkStateEstimate(network_estimate);
return MaybeUpdateEstimate(acked_bitrate, probe_bitrate, return MaybeUpdateEstimate(acked_bitrate, probe_bitrate,
std::move(network_estimate),
recovered_from_overuse, in_alr, msg.feedback_time); recovered_from_overuse, in_alr, msg.feedback_time);
} }
void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
int64_t at_time) { Timestamp at_time) {
// Reset if the stream has timed out. // Reset if the stream has timed out.
if (last_seen_packet_.IsInfinite() || if (last_seen_packet_.IsInfinite() ||
at_time - last_seen_packet_ > kStreamTimeOut) { at_time - last_seen_packet_ > kStreamTimeOut) {
@@ -133,10 +108,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
audio_inter_arrival_delta_ = audio_inter_arrival_delta_ =
std::make_unique<InterArrivalDelta>(kSendTimeGroupLength); std::make_unique<InterArrivalDelta>(kSendTimeGroupLength);
video_delay_detector_.reset( video_delay_detector_.reset(new TrendlineEstimator());
new TrendlineEstimator(key_value_config_, network_state_predictor_)); audio_delay_detector_.reset(new TrendlineEstimator());
audio_delay_detector_.reset(
new TrendlineEstimator(key_value_config_, network_state_predictor_));
active_delay_detector_ = video_delay_detector_.get(); active_delay_detector_ = video_delay_detector_.get();
} }
last_seen_packet_ = at_time; last_seen_packet_ = at_time;
@@ -163,8 +136,8 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
} }
DataSize packet_size = packet_feedback.sent_packet.size; DataSize packet_size = packet_feedback.sent_packet.size;
int64_t send_delta = int64_t::Zero(); TimeDelta send_delta = TimeDelta::Zero();
int64_t recv_delta = int64_t::Zero(); TimeDelta recv_delta = TimeDelta::Zero();
int size_delta = 0; int size_delta = 0;
InterArrivalDelta* inter_arrival_for_packet = InterArrivalDelta* inter_arrival_for_packet =
@@ -182,16 +155,16 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback,
packet_size.bytes(), calculated_deltas); packet_size.bytes(), calculated_deltas);
} }
int64_t DelayBasedBwe::TriggerOveruse(int64_t at_time, DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time,
std::optional<int64_t> link_capacity) { std::optional<DataRate> link_capacity) {
RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity); RateControlInput input(BandwidthUsage::kBwOverusing, link_capacity);
return rate_control_.Update(input, at_time); return rate_control_.Update(input, at_time);
} }
DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
std::optional<int64_t> acked_bitrate, std::optional<int64_t> probe_bitrate, std::optional<DataRate> acked_bitrate,
std::optional<NetworkStateEstimate> /* state_estimate */, std::optional<DataRate> probe_bitrate, bool recovered_from_overuse,
bool recovered_from_overuse, bool /* in_alr */, int64_t at_time) { bool /* in_alr */, Timestamp at_time) {
Result result; Result result;
// Currently overusing the bandwidth. // Currently overusing the bandwidth.
@@ -226,12 +199,7 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
BandwidthUsage detector_state = active_delay_detector_->State(); BandwidthUsage detector_state = active_delay_detector_->State();
if ((result.updated && prev_bitrate_ != result.target_bitrate) || if ((result.updated && prev_bitrate_ != result.target_bitrate) ||
detector_state != prev_state_) { detector_state != prev_state_) {
int64_t bitrate = result.updated ? result.target_bitrate : prev_bitrate_; DataRate bitrate = result.updated ? result.target_bitrate : prev_bitrate_;
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventBweUpdateDelayBased>(
bitrate.bps(), detector_state));
}
prev_bitrate_ = bitrate; prev_bitrate_ = bitrate;
prev_state_ = detector_state; prev_state_ = detector_state;
@@ -241,26 +209,24 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(
return result; return result;
} }
bool DelayBasedBwe::UpdateEstimate(int64_t at_time, bool DelayBasedBwe::UpdateEstimate(Timestamp at_time,
std::optional<int64_t> acked_bitrate, std::optional<DataRate> acked_bitrate,
int64_t* target_rate) { DataRate* target_rate) {
const RateControlInput input(active_delay_detector_->State(), acked_bitrate); const RateControlInput input(active_delay_detector_->State(), acked_bitrate);
*target_rate = rate_control_.Update(input, at_time); *target_rate = rate_control_.Update(input, at_time);
return rate_control_.ValidEstimate(); return rate_control_.ValidEstimate();
} }
void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt) { void DelayBasedBwe::OnRttUpdate(TimeDelta avg_rtt) {
rate_control_.SetRtt(avg_rtt); rate_control_.SetRtt(avg_rtt);
} }
bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs, bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
int64_t* bitrate) const { DataRate* bitrate) const {
// Currently accessed from both the process thread (see // Currently accessed from both the process thread (see
// ModuleRtpRtcpImpl::Process()) and the configuration thread (see // ModuleRtpRtcpImpl::Process()) and the configuration thread (see
// Call::GetStats()). Should in the future only be accessed from a single // Call::GetStats()). Should in the future only be accessed from a single
// thread. // thread.
RTC_DCHECK(ssrcs);
RTC_DCHECK(bitrate);
if (!rate_control_.ValidEstimate()) return false; if (!rate_control_.ValidEstimate()) return false;
*ssrcs = {kFixedSsrc}; *ssrcs = {kFixedSsrc};
@@ -268,18 +234,19 @@ bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
return true; return true;
} }
void DelayBasedBwe::SetStartBitrate(int64_t start_bitrate) { void DelayBasedBwe::SetStartBitrate(DataRate start_bitrate) {
RTC_LOG(LS_INFO) << "BWE Setting start bitrate to: " LOG_INFO("BWE Setting start bitrate to: {}", ToString(start_bitrate));
<< ToString(start_bitrate);
rate_control_.SetStartBitrate(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 // Called from both the configuration thread and the network thread. Shouldn't
// be called from the network thread in the future. // be called from the network thread in the future.
rate_control_.SetMinBitrate(min_bitrate); rate_control_.SetMinBitrate(min_bitrate);
} }
int64_t DelayBasedBwe::GetExpectedBwePeriod() const { TimeDelta DelayBasedBwe::GetExpectedBwePeriod() const {
return rate_control_.GetExpectedBandwidthPeriod(); return rate_control_.GetExpectedBandwidthPeriod();
} }
} // namespace webrtc

View File

@@ -1,11 +1,15 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-14 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#define _DELAY_BASED_BWE_H_ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_
#include <stdint.h> #include <stdint.h>
@@ -13,27 +17,29 @@
#include <optional> #include <optional>
#include <vector> #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 "network_types.h"
#include "probe_bitrate_estimator.h"
enum class BandwidthUsage { namespace webrtc {
kBwNormal = 0, class RtcEventLog;
kBwUnderusing = 1,
kBwOverusing = 2,
kLast
};
struct BweSeparateAudioPacketsSettings { struct BweSeparateAudioPacketsSettings {
static constexpr char kKey[] = "WebRTC-Bwe-SeparateAudioPackets"; static constexpr char kKey[] = "WebRTC-Bwe-SeparateAudioPackets";
BweSeparateAudioPacketsSettings() = default; BweSeparateAudioPacketsSettings() = default;
explicit BweSeparateAudioPacketsSettings(
const FieldTrialsView* key_value_config);
bool enabled = false; bool enabled = false;
int packet_threshold = 10; int packet_threshold = 10;
int64_t time_threshold = int64_t::Seconds(1); TimeDelta time_threshold = TimeDelta::Seconds(1);
std::unique_ptr<StructParametersParser> Parser();
}; };
class DelayBasedBwe { class DelayBasedBwe {
@@ -43,60 +49,51 @@ class DelayBasedBwe {
~Result() = default; ~Result() = default;
bool updated; bool updated;
bool probe; bool probe;
int64_t target_bitrate = int64_t::Zero(); DataRate target_bitrate = DataRate::Zero();
bool recovered_from_overuse; bool recovered_from_overuse;
BandwidthUsage delay_detector_state; BandwidthUsage delay_detector_state;
}; };
explicit DelayBasedBwe(const FieldTrialsView* key_value_config, DelayBasedBwe();
RtcEventLog* event_log,
NetworkStatePredictor* network_state_predictor);
DelayBasedBwe() = delete;
DelayBasedBwe(const DelayBasedBwe&) = delete; DelayBasedBwe(const DelayBasedBwe&) = delete;
DelayBasedBwe& operator=(const DelayBasedBwe&) = delete; DelayBasedBwe& operator=(const DelayBasedBwe&) = delete;
virtual ~DelayBasedBwe(); virtual ~DelayBasedBwe();
Result IncomingPacketFeedbackVector( Result IncomingPacketFeedbackVector(const TransportPacketsFeedback& msg,
const TransportPacketsFeedback& msg, std::optional<int64_t> acked_bitrate, std::optional<DataRate> acked_bitrate,
std::optional<int64_t> probe_bitrate, std::optional<DataRate> probe_bitrate,
std::optional<NetworkStateEstimate> network_estimate, bool in_alr); bool in_alr);
void OnRttUpdate(int64_t avg_rtt); void OnRttUpdate(TimeDelta avg_rtt);
bool LatestEstimate(std::vector<uint32_t>* ssrcs, int64_t* bitrate) const; bool LatestEstimate(std::vector<uint32_t>* ssrcs, DataRate* bitrate) const;
void SetStartBitrate(int64_t start_bitrate); void SetStartBitrate(DataRate start_bitrate);
void SetMinBitrate(int64_t min_bitrate); void SetMinBitrate(DataRate min_bitrate);
int64_t GetExpectedBwePeriod() const; TimeDelta GetExpectedBwePeriod() const;
int64_t TriggerOveruse(int64_t at_time, std::optional<int64_t> link_capacity); DataRate TriggerOveruse(Timestamp at_time,
int64_t last_estimate() const { return prev_bitrate_; } std::optional<DataRate> link_capacity);
DataRate last_estimate() const { return prev_bitrate_; }
BandwidthUsage last_state() const { return prev_state_; } BandwidthUsage last_state() const { return prev_state_; }
private: private:
friend class GoogCcStatePrinter; friend class GoogCcStatePrinter;
void IncomingPacketFeedback(const PacketResult& packet_feedback, void IncomingPacketFeedback(const PacketResult& packet_feedback,
int64_t at_time); Timestamp at_time);
Result MaybeUpdateEstimate(std::optional<int64_t> acked_bitrate, Result MaybeUpdateEstimate(std::optional<DataRate> acked_bitrate,
std::optional<int64_t> probe_bitrate, std::optional<DataRate> probe_bitrate,
std::optional<NetworkStateEstimate> state_estimate,
bool recovered_from_overuse, bool in_alr, 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 // Updates the current remote rate estimate and returns true if a valid
// estimate exists. // estimate exists.
bool UpdateEstimate(int64_t at_time, std::optional<int64_t> acked_bitrate, bool UpdateEstimate(Timestamp at_time, std::optional<DataRate> acked_bitrate,
int64_t* target_rate); DataRate* target_rate);
rtc::RaceChecker network_race_;
RtcEventLog* const event_log_;
const FieldTrialsView* const key_value_config_;
// Alternatively, run two separate overuse detectors for audio and video, // 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 // and fall back to the audio one if we haven't seen a video packet in a
// while. // while.
BweSeparateAudioPacketsSettings separate_audio_; BweSeparateAudioPacketsSettings separate_audio_;
int64_t audio_packets_since_last_video_; 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<InterArrival> video_inter_arrival_;
std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_; std::unique_ptr<InterArrivalDelta> video_inter_arrival_delta_;
std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_; std::unique_ptr<DelayIncreaseDetectorInterface> video_delay_detector_;
@@ -105,11 +102,13 @@ class DelayBasedBwe {
std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_; std::unique_ptr<DelayIncreaseDetectorInterface> audio_delay_detector_;
DelayIncreaseDetectorInterface* active_delay_detector_; DelayIncreaseDetectorInterface* active_delay_detector_;
int64_t last_seen_packet_; Timestamp last_seen_packet_;
bool uma_recorded_; bool uma_recorded_;
AimdRateControl rate_control_; AimdRateControl rate_control_;
int64_t prev_bitrate_; DataRate prev_bitrate_;
BandwidthUsage prev_state_; BandwidthUsage prev_state_;
}; };
#endif } // namespace webrtc
#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_DELAY_BASED_BWE_H_

View 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
View 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
View 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_

View 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

View 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_

View File

@@ -14,6 +14,9 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace { namespace {
constexpr int64_t kWindowMs = 500; constexpr int64_t kWindowMs = 500;
} }
@@ -51,7 +54,7 @@ void IntervalBudget::UseBudget(size_t bytes) {
} }
size_t IntervalBudget::bytes_remaining() const { 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 { double IntervalBudget::budget_ratio() const {
@@ -60,3 +63,5 @@ double IntervalBudget::budget_ratio() const {
} }
int IntervalBudget::target_rate_kbps() const { return target_rate_kbps_; } int IntervalBudget::target_rate_kbps() const { return target_rate_kbps_; }
} // namespace webrtc

View File

@@ -1,15 +1,21 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
* @Date: 2025-01-14 *
* Copyright (c) 2025 by DI JUNKUN, 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_ #ifndef MODULES_PACING_INTERVAL_BUDGET_H_
#define _INTERVAL_BUDGET_H_ #define MODULES_PACING_INTERVAL_BUDGET_H_
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
namespace webrtc {
// TODO(tschumim): Reflector IntervalBudget so that we can set a under- and // TODO(tschumim): Reflector IntervalBudget so that we can set a under- and
// over-use budget in ms. // over-use budget in ms.
class IntervalBudget { class IntervalBudget {
@@ -33,4 +39,6 @@ class IntervalBudget {
bool can_build_up_underuse_; bool can_build_up_underuse_;
}; };
#endif } // namespace webrtc
#endif // MODULES_PACING_INTERVAL_BUDGET_H_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View File

@@ -127,7 +127,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback(
packet_info.arrival_time_offset != packet_info.arrival_time_offset !=
std::numeric_limits<int64_t>::max()) { std::numeric_limits<int64_t>::max()) {
result.receive_time = current_offset_ - packet_info.arrival_time_offset; 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; result.ecn = packet_info.ecn;
packet_result_vector.push_back(result); packet_result_vector.push_back(result);

View File

@@ -17,7 +17,7 @@
#include "congestion_control_feedback.h" #include "congestion_control_feedback.h"
#include "network_route.h" #include "network_route.h"
#include "network_types.h" #include "network_types.h"
#include "sequence_number_unwrapper.h" #include "rtc_base/numerics/sequence_number_unwrapper.h"
struct PacketFeedback { struct PacketFeedback {
PacketFeedback() = default; PacketFeedback() = default;

View 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

View 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_

View File

@@ -13,6 +13,8 @@
#include "byte_io.h" #include "byte_io.h"
#include "log.h" #include "log.h"
namespace webrtc {
namespace rtcp {
// 0 1 1 2 3 // 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 // 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. // 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; const uint8_t kVersion = 2;
if (size_bytes < kHeaderSizeBytes) { if (size_bytes < kHeaderSizeBytes) {
LOG_WARN( LOG_WARN(
"Too little data ({}) remaining in buffer to parse RTCP header (4 " "Too little data ({} byte{}) remaining in buffer to parse RTCP header "
"bytes)", "(4 bytes).",
size_bytes); size_bytes, (size_bytes != 1 ? "s" : ""));
return false; return false;
} }
@@ -51,8 +53,8 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
if (size_bytes < kHeaderSizeBytes + payload_size_) { if (size_bytes < kHeaderSizeBytes + payload_size_) {
LOG_WARN( LOG_WARN(
"Buffer too small ({}) to fit an RtcpPacket with a header and ({} " "Buffer too small ({} bytes) to fit an RtcpPacket with a header and {} "
"bytes)", "bytes.",
size_bytes, payload_size_); size_bytes, payload_size_);
return false; return false;
} }
@@ -60,21 +62,20 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
if (has_padding) { if (has_padding) {
if (payload_size_ == 0) { if (payload_size_ == 0) {
LOG_WARN( LOG_WARN(
"Invalid RTCP header: Padding bit set but 0 payload size " "Invalid RTCP header: Padding bit set but 0 payload size specified.");
"specified");
return false; return false;
} }
padding_size_ = payload_[payload_size_ - 1]; padding_size_ = payload_[payload_size_ - 1];
if (padding_size_ == 0) { if (padding_size_ == 0) {
LOG_WARN( 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; return false;
} }
if (padding_size_ > payload_size_) { if (padding_size_ > payload_size_) {
LOG_WARN( LOG_WARN(
"Invalid RTCP header: Too many padding bytes ({}) for a packet " "Invalid RTCP header: Too many padding bytes ({}) for a packet "
"payload size of ({}) bytes", "payload size of {} bytes.",
padding_size_, payload_size_); padding_size_, payload_size_);
return false; return false;
} }
@@ -82,3 +83,5 @@ bool RtcpCommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
} }
return true; return true;
} }
} // namespace rtcp
} // namespace webrtc

View File

@@ -13,13 +13,15 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
class RtcpCommonHeader { namespace webrtc {
namespace rtcp {
class CommonHeader {
public: public:
static constexpr size_t kHeaderSizeBytes = 4; static constexpr size_t kHeaderSizeBytes = 4;
RtcpCommonHeader() {} CommonHeader() {}
RtcpCommonHeader(const RtcpCommonHeader&) = default; CommonHeader(const CommonHeader&) = default;
RtcpCommonHeader& operator=(const RtcpCommonHeader&) = default; CommonHeader& operator=(const CommonHeader&) = default;
bool Parse(const uint8_t* buffer, size_t size_bytes); bool Parse(const uint8_t* buffer, size_t size_bytes);
@@ -45,4 +47,6 @@ class RtcpCommonHeader {
uint32_t payload_size_ = 0; uint32_t payload_size_ = 0;
const uint8_t* payload_ = nullptr; const uint8_t* payload_ = nullptr;
}; };
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_ #endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_

View 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

View File

@@ -35,6 +35,7 @@ void RtpPacket::ParseRtpData() {
RtpPacket::RtpPacket() : buffer_(new uint8_t[DEFAULT_MTU]), size_(DEFAULT_MTU) { RtpPacket::RtpPacket() : buffer_(new uint8_t[DEFAULT_MTU]), size_(DEFAULT_MTU) {
memset(buffer_, 0, DEFAULT_MTU); memset(buffer_, 0, DEFAULT_MTU);
ParseRtpData();
} }
RtpPacket::RtpPacket(const uint8_t *buffer, uint32_t size) { RtpPacket::RtpPacket(const uint8_t *buffer, uint32_t size) {
@@ -48,6 +49,7 @@ RtpPacket::RtpPacket(const uint8_t *buffer, uint32_t size) {
size_ = size; size_ = size;
// TryToDecodeH264RtpPacket(buffer_); // TryToDecodeH264RtpPacket(buffer_);
ParseRtpData();
} }
} }
@@ -62,6 +64,7 @@ RtpPacket::RtpPacket(const RtpPacket &rtp_packet) {
size_ = rtp_packet.size_; size_ = rtp_packet.size_;
// TryToDecodeH264RtpPacket(buffer_); // TryToDecodeH264RtpPacket(buffer_);
ParseRtpData();
} }
} }
@@ -72,6 +75,7 @@ RtpPacket::RtpPacket(RtpPacket &&rtp_packet)
rtp_packet.size_ = 0; rtp_packet.size_ = 0;
// TryToDecodeH264RtpPacket(buffer_); // TryToDecodeH264RtpPacket(buffer_);
ParseRtpData();
} }
// RtpPacket &RtpPacket::operator=(const RtpPacket &rtp_packet) { // RtpPacket &RtpPacket::operator=(const RtpPacket &rtp_packet) {

View File

@@ -278,48 +278,48 @@ class RtpPacket {
public: public:
// Get Header // Get Header
uint32_t Verion() { uint32_t Verion() const {
ParseRtpData(); // // ParseRtpData();
return version_; return version_;
} }
bool HasPadding() { bool HasPadding() const {
ParseRtpData(); // ParseRtpData();
return has_padding_; return has_padding_;
} }
bool HasExtension() { bool HasExtension() const {
ParseRtpData(); // ParseRtpData();
return has_extension_; return has_extension_;
} }
bool Marker() { bool Marker() const {
ParseRtpData(); // // ParseRtpData();
return marker_; return marker_;
} }
PAYLOAD_TYPE PayloadType() { PAYLOAD_TYPE PayloadType() const {
ParseRtpData(); // ParseRtpData();
return PAYLOAD_TYPE(payload_type_); return PAYLOAD_TYPE(payload_type_);
} }
uint16_t SequenceNumber() { uint16_t SequenceNumber() const {
ParseRtpData(); // ParseRtpData();
return sequence_number_; return sequence_number_;
} }
uint64_t Timestamp() { uint64_t Timestamp() const {
ParseRtpData(); // ParseRtpData();
return timestamp_; return timestamp_;
} }
uint32_t Ssrc() { uint32_t Ssrc() const {
ParseRtpData(); // ParseRtpData();
return ssrc_; return ssrc_;
} }
std::vector<uint32_t> Csrcs() { std::vector<uint32_t> Csrcs() const {
ParseRtpData(); // ParseRtpData();
return csrcs_; return csrcs_;
}; };
uint16_t ExtensionProfile() { uint16_t ExtensionProfile() const {
ParseRtpData(); // ParseRtpData();
return extension_profile_; return extension_profile_;
} }
const uint8_t *ExtensionData() { const uint8_t *ExtensionData() {
ParseRtpData(); // ParseRtpData();
return extension_data_; return extension_data_;
} }
@@ -336,34 +336,42 @@ class RtpPacket {
// Payload // Payload
const uint8_t *Payload() { const uint8_t *Payload() {
ParseRtpData(); // ParseRtpData();
return payload_; return payload_;
}; };
size_t PayloadSize() { size_t PayloadSize() {
ParseRtpData(); // ParseRtpData();
return payload_size_; 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 // Entire RTP buffer
const uint8_t *Buffer() const { return buffer_; } const uint8_t *Buffer() const { return buffer_; }
size_t Size() const { return size_; } size_t Size() const { return size_; }
// NAL // NAL
NAL_UNIT_TYPE NalUnitType() { NAL_UNIT_TYPE NalUnitType() {
ParseRtpData(); // ParseRtpData();
return nal_unit_type_; return nal_unit_type_;
} }
bool FuAStart() { bool FuAStart() {
ParseRtpData(); // ParseRtpData();
return fu_header_.start; return fu_header_.start;
} }
bool FuAEnd() { bool FuAEnd() {
ParseRtpData(); // ParseRtpData();
return fu_header_.end; return fu_header_.end;
} }
bool Av1FrameStart() { bool Av1FrameStart() {
ParseRtpData(); // ParseRtpData();
int z, y, w, n; int z, y, w, n;
GetAv1AggrHeader(z, y, w, n); GetAv1AggrHeader(z, y, w, n);
// return !z && !y; // return !z && !y;
@@ -378,7 +386,7 @@ class RtpPacket {
} }
bool Av1FrameEnd() { bool Av1FrameEnd() {
ParseRtpData(); // ParseRtpData();
int z, y, w, n; int z, y, w, n;
GetAv1AggrHeader(z, y, w, n); GetAv1AggrHeader(z, y, w, n);
// return z && !y; // return z && !y;
@@ -421,6 +429,7 @@ class RtpPacket {
// Payload // Payload
uint8_t *payload_ = nullptr; uint8_t *payload_ = nullptr;
size_t payload_size_ = 0; size_t payload_size_ = 0;
size_t padding_size_ = 0;
// Entire RTP buffer // Entire RTP buffer
uint8_t *buffer_ = nullptr; uint8_t *buffer_ = nullptr;

View File

@@ -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 "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() = default;
RtpPacketReceived::RtpPacketReceived(
webrtc::Timestamp arrival_time /*= webrtc::Timestamp::MinusInfinity()*/)
: RtpPacket(), arrival_time_(arrival_time) {}
RtpPacketReceived::RtpPacketReceived(const RtpPacketReceived& packet) = default; RtpPacketReceived::RtpPacketReceived(const RtpPacketReceived& packet) = default;
RtpPacketReceived::RtpPacketReceived(RtpPacketReceived&& packet) = default; RtpPacketReceived::RtpPacketReceived(RtpPacketReceived&& packet) = default;
@@ -10,3 +32,20 @@ RtpPacketReceived& RtpPacketReceived::operator=(RtpPacketReceived&& packet) =
default; default;
RtpPacketReceived::~RtpPacketReceived() {} 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

View File

@@ -1,20 +1,36 @@
/* /*
* @Author: DI JUNKUN * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
* @Date: 2024-12-18 *
* Copyright (c) 2024 by DI JUNKUN, 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_ #include <stdint.h>
#define _RTP_PACKET_RECEIVED_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" #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 { class RtpPacketReceived : public RtpPacket {
public: public:
RtpPacketReceived(); RtpPacketReceived();
explicit RtpPacketReceived(
webrtc::Timestamp arrival_time = webrtc::Timestamp::MinusInfinity());
RtpPacketReceived(const RtpPacketReceived& packet); RtpPacketReceived(const RtpPacketReceived& packet);
RtpPacketReceived(RtpPacketReceived&& packet); RtpPacketReceived(RtpPacketReceived&& packet);
@@ -23,14 +39,19 @@ class RtpPacketReceived : public RtpPacket {
~RtpPacketReceived(); ~RtpPacketReceived();
public: // TODO(bugs.webrtc.org/15054): Remove this function when all code is updated
int64_t arrival_time() const { return arrival_time_; } // to use RtpPacket directly.
void set_arrival_time(int64_t time) { arrival_time_ = time; } 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. // Explicit Congestion Notification (ECN), RFC-3168, Section 5.
// Used by L4S: https://www.rfc-editor.org/rfc/rfc9331.html // Used by L4S: https://www.rfc-editor.org/rfc/rfc9331.html
EcnMarking ecn() const { return ecn_; } rtc::EcnMarking ecn() const { return ecn_; }
void set_ecn(EcnMarking ecn) { ecn_ = ecn; } void set_ecn(rtc::EcnMarking ecn) { ecn_ = ecn; }
// Flag if packet was recovered via RTX or FEC. // Flag if packet was recovered via RTX or FEC.
bool recovered() const { return recovered_; } bool recovered() const { return recovered_; }
@@ -41,11 +62,22 @@ class RtpPacketReceived : public RtpPacket {
payload_type_frequency_ = value; 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: private:
int64_t arrival_time_ = std::numeric_limits<int64_t>::min(); webrtc::Timestamp arrival_time_ = Timestamp::MinusInfinity();
EcnMarking ecn_ = EcnMarking::kNotEct; rtc::EcnMarking ecn_ = rtc::EcnMarking::kNotEct;
int payload_type_frequency_ = 0; int payload_type_frequency_ = 0;
bool recovered_ = false; bool recovered_ = false;
rtc::scoped_refptr<rtc::RefCountedBase> additional_data_;
}; };
#endif } // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_

View File

@@ -41,7 +41,9 @@ target("log")
target("common") target("common")
set_kind("object") 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}) add_includedirs("src/common", {public = true})
target("inih") target("inih")
@@ -100,7 +102,7 @@ target("ws")
target("rtp") target("rtp")
set_kind("object") 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", add_files("src/rtp/*.cpp",
"src/rtp/rtp_packet/*.cpp") "src/rtp/rtp_packet/*.cpp")
add_includedirs("src/rtp", add_includedirs("src/rtp",
@@ -120,7 +122,8 @@ target("rtcp")
target("qos") target("qos")
set_kind("object") set_kind("object")
add_deps("log", "rtp", "rtcp") add_deps("log", "rtp", "rtcp")
add_files("src/qos/*.cpp") add_files("src/qos/*.cc",
"src/qos/*.cpp")
add_includedirs("src/qos", {public = true}) add_includedirs("src/qos", {public = true})
target("channel") target("channel")