Ring Daemon 16.0.0
Loading...
Searching...
No Matches
congestion_control.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "logger.h"
20
21#include <cstdint>
22#include <utility>
23#include <cmath>
24
25namespace jami {
26static constexpr uint8_t packetVersion = 2;
27static constexpr uint8_t packetFMT = 15;
28static constexpr uint8_t packetType = 206;
29static constexpr uint32_t uniqueIdentifier = 0x52454D42; // 'R' 'E' 'M' 'B'.
30
31static constexpr float Q = 0.5f;
32static constexpr float beta = 0.95f;
33
34static constexpr float ku = 0.004f;
35static constexpr float kd = 0.002f;
36
37constexpr auto OVERUSE_THRESH = std::chrono::milliseconds(100);
38
39// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
40//
41// 0 1 2 3
42// 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
43// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44// |V=2|P| FMT=15 | PT=206 | length |
45// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
46// 0 | SSRC of packet sender |
47// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48// 4 | Unused = 0 |
49// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50// 8 | Unique identifier 'R' 'E' 'M' 'B' |
51// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52// 12 | Num SSRC | BR Exp | BR Mantissa |
53// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54// 16 | SSRC feedback |
55// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56// : ... :
57
59
61
62static void
63insert2Byte(std::vector<uint8_t>& v, uint16_t val)
64{
65 v.insert(v.end(), val >> 8);
66 v.insert(v.end(), val & 0xff);
67}
68
69static void
70insert4Byte(std::vector<uint8_t>& v, uint32_t val)
71{
72 v.insert(v.end(), val >> 24);
73 v.insert(v.end(), (val >> 16) & 0xff);
74 v.insert(v.end(), (val >> 8) & 0xff);
75 v.insert(v.end(), val & 0xff);
76}
77
80{
81 if (packet.fmt != 15 || packet.pt != 206) {
82 JAMI_ERR("Unable to parse REMB packet.");
83 return 0;
84 }
85 uint64_t bitrate_bps = (packet.br_mantis << packet.br_exp);
86 bool shift_overflow = (bitrate_bps >> packet.br_exp) != packet.br_mantis;
87 if (shift_overflow) {
88 JAMI_ERR("Invalid remb bitrate value : %u*2^%u", packet.br_mantis, packet.br_exp);
89 return false;
90 }
91 return bitrate_bps;
92}
93
94std::vector<uint8_t>
96{
97 std::vector<uint8_t> remb;
98 remb.reserve(24);
99
100 remb.insert(remb.end(), packetVersion << 6 | packetFMT);
101 remb.insert(remb.end(), packetType);
102 insert2Byte(remb, 5); // (sizeof(rtcpREMBHeader)/4)-1 -> not safe
103 insert4Byte(remb, 0x12345678); // ssrc
104 insert4Byte(remb, 0x0); // ssrc source
106 remb.insert(remb.end(), 1); // n_ssrc
107
108 const uint32_t maxMantissa = 0x3ffff; // 18 bits.
110 uint8_t exponenta = 0;
111 while (mantissa > maxMantissa) {
112 mantissa >>= 1;
113 ++exponenta;
114 }
115
116 remb.insert(remb.end(), (exponenta << 2) | (mantissa >> 16));
117 insert2Byte(remb, mantissa & 0xffff);
118 insert4Byte(remb, 0x2345678b);
119
120 return remb;
121}
122
123float
125{
126 float var_n = get_var_n(gradiant_delay);
127 float k = get_gain_k(Q, var_n);
128 float m = get_estimate_m(k, gradiant_delay);
129 last_var_p_ = get_sys_var_p(k, Q);
130 last_estimate_m_ = m;
131 last_var_n_ = var_n;
132
133 return m;
134}
135
136float
137CongestionControl::get_estimate_m(float k, int d_m)
138{
139 // JAMI_WARN("[get_estimate_m]k:%f, last_estimate_m_:%f, d_m:%f", k, last_estimate_m_, d_m);
140 // JAMI_WARN("m: %f", ((1-k) * last_estimate_m_) + (k * d_m));
141 return ((1 - k) * last_estimate_m_) + (k * d_m);
142}
143
144float
145CongestionControl::get_gain_k(float q, float dev_n)
146{
147 // JAMI_WARN("k: %f", (last_var_p_ + q) / (last_var_p_ + q + dev_n));
148 return (last_var_p_ + q) / (last_var_p_ + q + dev_n);
149}
150
151float
152CongestionControl::get_sys_var_p(float k, float q)
153{
154 // JAMI_WARN("var_p: %f", ((1-k) * (last_var_p_ + q)));
155 return ((1 - k) * (last_var_p_ + q));
156}
157
158float
159CongestionControl::get_var_n(int d_m)
160{
161 float z = get_residual_z(d_m);
162 // JAMI_WARN("var_n: %f", (beta * last_var_n_) + ((1.0f - beta) * z * z));
163 return (beta * last_var_n_) + ((1.0f - beta) * z * z);
164}
165
166float
167CongestionControl::get_residual_z(float d_m)
168{
169 // JAMI_WARN("z: %f", d_m - last_estimate_m_);
170 return (d_m - last_estimate_m_);
171}
172
173float
175{
176 float ky = 0.0f;
177 if (std::fabs(m) < last_thresh_y_)
178 ky = kd;
179 else
180 ky = ku;
181 float res = last_thresh_y_ + ((deltaT * ky) * (std::fabs(m) - last_thresh_y_));
182 last_thresh_y_ = res;
183 return res;
184}
185
186float
188{
189 return last_thresh_y_;
190}
191
194{
195 if (estimation > thresh) {
196 // JAMI_WARN("Enter overuse state");
197 if (not overuse_counter_) {
198 t0_overuse = clock::now();
199 overuse_counter_++;
200 return bwNormal;
201 }
202 overuse_counter_++;
203 time_point now = clock::now();
204 auto overuse_timer = now - t0_overuse;
205 if ((overuse_timer >= OVERUSE_THRESH) and (overuse_counter_ > 1)) {
206 overuse_counter_ = 0;
207 last_state_ = bwOverusing;
208 }
209 } else if (estimation < -thresh) {
210 // JAMI_WARN("Enter underuse state");
211 overuse_counter_ = 0;
212 last_state_ = bwUnderusing;
213 } else {
214 overuse_counter_ = 0;
215 last_state_ = bwNormal;
216 }
217 return last_state_;
218}
219
220} // namespace jami
float kalmanFilter(uint64_t gradiant_delay)
std::vector< uint8_t > createREMB(uint64_t bitrate_bps)
uint64_t parseREMB(const rtcpREMBHeader &packet)
BandwidthUsage get_bw_state(float estimation, float thresh)
float update_thresh(float m, int deltaT)
#define JAMI_ERR(...)
Definition logger.h:218
static void insert2Byte(std::vector< uint8_t > &v, uint16_t val)
static constexpr float Q
void emitSignal(Args... args)
Definition ring_signal.h:64
static constexpr float beta
static constexpr uint32_t uniqueIdentifier
static constexpr uint8_t packetType
static void insert4Byte(std::vector< uint8_t > &v, uint32_t val)
static constexpr uint8_t packetFMT
static constexpr float kd
static constexpr uint8_t packetVersion
static constexpr float ku
constexpr auto OVERUSE_THRESH