Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 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"
19 : #include "media/congestion_control.h"
20 :
21 : #include <cstdint>
22 : #include <cmath>
23 :
24 : namespace jami {
25 : static constexpr uint8_t packetVersion = 2;
26 : static constexpr uint8_t packetFMT = 15;
27 : static constexpr uint8_t packetType = 206;
28 : static constexpr uint32_t uniqueIdentifier = 0x52454D42; // 'R' 'E' 'M' 'B'.
29 :
30 : static constexpr float Q = 0.5f;
31 : static constexpr float beta = 0.95f;
32 :
33 : static constexpr float ku = 0.004f;
34 : static constexpr float kd = 0.002f;
35 :
36 : constexpr auto OVERUSE_THRESH = std::chrono::milliseconds(100);
37 :
38 : // Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
39 : //
40 : // 0 1 2 3
41 : // 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
42 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 : // |V=2|P| FMT=15 | PT=206 | length |
44 : // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
45 : // 0 | SSRC of packet sender |
46 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 : // 4 | Unused = 0 |
48 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 : // 8 | Unique identifier 'R' 'E' 'M' 'B' |
50 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 : // 12 | Num SSRC | BR Exp | BR Mantissa |
52 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 : // 16 | SSRC feedback |
54 : // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 : // : ... :
56 :
57 111 : CongestionControl::CongestionControl() {}
58 :
59 111 : CongestionControl::~CongestionControl() {}
60 :
61 : static void
62 0 : insert2Byte(std::vector<uint8_t>& v, uint16_t val)
63 : {
64 0 : v.insert(v.end(), val >> 8);
65 0 : v.insert(v.end(), val & 0xff);
66 0 : }
67 :
68 : static void
69 0 : insert4Byte(std::vector<uint8_t>& v, uint32_t val)
70 : {
71 0 : v.insert(v.end(), val >> 24);
72 0 : v.insert(v.end(), (val >> 16) & 0xff);
73 0 : v.insert(v.end(), (val >> 8) & 0xff);
74 0 : v.insert(v.end(), val & 0xff);
75 0 : }
76 :
77 : uint64_t
78 0 : CongestionControl::parseREMB(const rtcpREMBHeader& packet)
79 : {
80 0 : if (packet.fmt != 15 || packet.pt != 206) {
81 0 : JAMI_ERR("Unable to parse REMB packet.");
82 0 : return 0;
83 : }
84 0 : uint64_t bitrate_bps = (packet.br_mantis << packet.br_exp);
85 0 : bool shift_overflow = (bitrate_bps >> packet.br_exp) != packet.br_mantis;
86 0 : if (shift_overflow) {
87 0 : JAMI_ERR("Invalid remb bitrate value : %u*2^%u", packet.br_mantis, packet.br_exp);
88 0 : return 0;
89 : }
90 0 : return bitrate_bps;
91 : }
92 :
93 : std::vector<uint8_t>
94 0 : CongestionControl::createREMB(uint64_t bitrate_bps)
95 : {
96 0 : std::vector<uint8_t> remb;
97 0 : remb.reserve(24);
98 :
99 0 : remb.insert(remb.end(), packetVersion << 6 | packetFMT);
100 0 : remb.insert(remb.end(), packetType);
101 0 : insert2Byte(remb, 5); // (sizeof(rtcpREMBHeader)/4)-1 -> not safe
102 0 : insert4Byte(remb, 0x12345678); // ssrc
103 0 : insert4Byte(remb, 0x0); // ssrc source
104 0 : insert4Byte(remb, uniqueIdentifier); // uid
105 0 : remb.insert(remb.end(), 1); // n_ssrc
106 :
107 0 : const uint32_t maxMantissa = 0x3ffff; // 18 bits.
108 0 : uint64_t mantissa = bitrate_bps;
109 0 : uint8_t exponenta = 0;
110 0 : while (mantissa > maxMantissa) {
111 0 : mantissa >>= 1;
112 0 : ++exponenta;
113 : }
114 :
115 0 : remb.insert(remb.end(), (exponenta << 2) | (mantissa >> 16));
116 0 : insert2Byte(remb, mantissa & 0xffff);
117 0 : insert4Byte(remb, 0x2345678b);
118 :
119 0 : return remb;
120 0 : }
121 :
122 : float
123 3 : CongestionControl::kalmanFilter(int gradiant_delay)
124 : {
125 3 : float var_n = get_var_n(gradiant_delay);
126 3 : float k = get_gain_k(Q, var_n);
127 3 : float m = get_estimate_m(k, gradiant_delay);
128 3 : last_var_p_ = get_sys_var_p(k, Q);
129 3 : last_estimate_m_ = m;
130 3 : last_var_n_ = var_n;
131 :
132 3 : return m;
133 : }
134 :
135 : float
136 3 : CongestionControl::get_estimate_m(float k, int d_m)
137 : {
138 : // JAMI_WARN("[get_estimate_m]k:%f, last_estimate_m_:%f, d_m:%f", k, last_estimate_m_, d_m);
139 : // JAMI_WARN("m: %f", ((1-k) * last_estimate_m_) + (k * d_m));
140 3 : return ((1 - k) * last_estimate_m_) + (k * static_cast<float>(d_m));
141 : }
142 :
143 : float
144 3 : CongestionControl::get_gain_k(float q, float dev_n)
145 : {
146 : // JAMI_WARN("k: %f", (last_var_p_ + q) / (last_var_p_ + q + dev_n));
147 3 : return (last_var_p_ + q) / (last_var_p_ + q + dev_n);
148 : }
149 :
150 : float
151 3 : CongestionControl::get_sys_var_p(float k, float q)
152 : {
153 : // JAMI_WARN("var_p: %f", ((1-k) * (last_var_p_ + q)));
154 3 : return ((1 - k) * (last_var_p_ + q));
155 : }
156 :
157 : float
158 3 : CongestionControl::get_var_n(int d_m)
159 : {
160 3 : float z = get_residual_z(d_m);
161 : // JAMI_WARN("var_n: %f", (beta * last_var_n_) + ((1.0f - beta) * z * z));
162 3 : return (beta * last_var_n_) + ((1.0f - beta) * z * z);
163 : }
164 :
165 : float
166 3 : CongestionControl::get_residual_z(int d_m)
167 : {
168 : // JAMI_WARN("z: %f", d_m - last_estimate_m_);
169 3 : return (static_cast<float>(d_m) - last_estimate_m_);
170 : }
171 :
172 : float
173 3 : CongestionControl::update_thresh(float m, int deltaT)
174 : {
175 3 : float ky = 0.0f;
176 3 : if (std::fabs(m) < last_thresh_y_)
177 3 : ky = kd;
178 : else
179 0 : ky = ku;
180 3 : float res = last_thresh_y_ + ((static_cast<float>(deltaT) * ky) * (std::fabs(m) - last_thresh_y_));
181 3 : last_thresh_y_ = res;
182 3 : return res;
183 : }
184 :
185 : float
186 3 : CongestionControl::get_thresh()
187 : {
188 3 : return last_thresh_y_;
189 : }
190 :
191 : BandwidthUsage
192 3 : CongestionControl::get_bw_state(float estimation, float thresh)
193 : {
194 3 : if (estimation > thresh) {
195 : // JAMI_WARN("Enter overuse state");
196 0 : if (not overuse_counter_) {
197 0 : t0_overuse = clock::now();
198 0 : overuse_counter_++;
199 0 : return bwNormal;
200 : }
201 0 : overuse_counter_++;
202 0 : time_point now = clock::now();
203 0 : auto overuse_timer = now - t0_overuse;
204 0 : if ((overuse_timer >= OVERUSE_THRESH) and (overuse_counter_ > 1)) {
205 0 : overuse_counter_ = 0;
206 0 : last_state_ = bwOverusing;
207 : }
208 3 : } else if (estimation < -thresh) {
209 : // JAMI_WARN("Enter underuse state");
210 0 : overuse_counter_ = 0;
211 0 : last_state_ = bwUnderusing;
212 : } else {
213 3 : overuse_counter_ = 0;
214 3 : last_state_ = bwNormal;
215 : }
216 3 : return last_state_;
217 : }
218 :
219 : } // namespace jami
|