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 301 : CongestionControl::CongestionControl() {}
58 :
59 301 : CongestionControl::~CongestionControl() {}
60 :
61 : static void
62 352 : insert2Byte(std::vector<uint8_t>& v, uint16_t val)
63 : {
64 352 : v.insert(v.end(), val >> 8);
65 352 : v.insert(v.end(), val & 0xff);
66 352 : }
67 :
68 : static void
69 704 : insert4Byte(std::vector<uint8_t>& v, uint32_t val)
70 : {
71 704 : v.insert(v.end(), val >> 24);
72 704 : v.insert(v.end(), (val >> 16) & 0xff);
73 704 : v.insert(v.end(), (val >> 8) & 0xff);
74 704 : v.insert(v.end(), val & 0xff);
75 704 : }
76 :
77 : uint64_t
78 176 : CongestionControl::parseREMB(const rtcpREMBHeader& packet)
79 : {
80 176 : if (packet.fmt != 15 || packet.pt != 206) {
81 0 : JAMI_ERROR("Unable to parse REMB packet.");
82 0 : return 0;
83 : }
84 176 : uint64_t bitrate_bps = (packet.br_mantis << packet.br_exp);
85 176 : bool shift_overflow = (bitrate_bps >> packet.br_exp) != packet.br_mantis;
86 176 : if (shift_overflow) {
87 0 : JAMI_ERROR("Invalid remb bitrate value : {}*2^{}", packet.br_mantis, packet.br_exp);
88 0 : return 0;
89 : }
90 176 : return bitrate_bps;
91 : }
92 :
93 : std::vector<uint8_t>
94 176 : CongestionControl::createREMB(uint64_t bitrate_bps)
95 : {
96 176 : std::vector<uint8_t> remb;
97 176 : remb.reserve(24);
98 :
99 176 : remb.insert(remb.end(), packetVersion << 6 | packetFMT);
100 176 : remb.insert(remb.end(), packetType);
101 176 : insert2Byte(remb, 5); // (sizeof(rtcpREMBHeader)/4)-1 -> not safe
102 176 : insert4Byte(remb, 0x12345678); // ssrc
103 176 : insert4Byte(remb, 0x0); // ssrc source
104 176 : insert4Byte(remb, uniqueIdentifier); // uid
105 176 : remb.insert(remb.end(), 1); // n_ssrc
106 :
107 176 : const uint32_t maxMantissa = 0x3ffff; // 18 bits.
108 176 : uint64_t mantissa = bitrate_bps;
109 176 : uint8_t exponenta = 0;
110 176 : while (mantissa > maxMantissa) {
111 0 : mantissa >>= 1;
112 0 : ++exponenta;
113 : }
114 :
115 176 : remb.insert(remb.end(), (exponenta << 2) | (mantissa >> 16));
116 176 : insert2Byte(remb, mantissa & 0xffff);
117 176 : insert4Byte(remb, 0x2345678b);
118 :
119 176 : return remb;
120 0 : }
121 :
122 : float
123 5579 : CongestionControl::kalmanFilter(int gradiant_delay)
124 : {
125 5579 : float var_n = get_var_n(gradiant_delay);
126 5579 : float k = get_gain_k(Q, var_n);
127 5579 : float m = get_estimate_m(k, gradiant_delay);
128 5579 : last_var_p_ = get_sys_var_p(k, Q);
129 5579 : last_estimate_m_ = m;
130 5579 : last_var_n_ = var_n;
131 :
132 5579 : return m;
133 : }
134 :
135 : float
136 5579 : 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 5579 : return ((1 - k) * last_estimate_m_) + (k * static_cast<float>(d_m));
141 : }
142 :
143 : float
144 5579 : 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 5579 : return (last_var_p_ + q) / (last_var_p_ + q + dev_n);
148 : }
149 :
150 : float
151 5579 : CongestionControl::get_sys_var_p(float k, float q)
152 : {
153 : // JAMI_WARN("var_p: %f", ((1-k) * (last_var_p_ + q)));
154 5579 : return ((1 - k) * (last_var_p_ + q));
155 : }
156 :
157 : float
158 5579 : CongestionControl::get_var_n(int d_m)
159 : {
160 5579 : float z = get_residual_z(d_m);
161 : // JAMI_WARN("var_n: %f", (beta * last_var_n_) + ((1.0f - beta) * z * z));
162 5579 : return (beta * last_var_n_) + ((1.0f - beta) * z * z);
163 : }
164 :
165 : float
166 5579 : CongestionControl::get_residual_z(int d_m)
167 : {
168 : // JAMI_WARN("z: %f", d_m - last_estimate_m_);
169 5579 : return (static_cast<float>(d_m) - last_estimate_m_);
170 : }
171 :
172 : float
173 5579 : CongestionControl::update_thresh(float m, int deltaT)
174 : {
175 5579 : float ky = 0.0f;
176 5579 : if (std::fabs(m) < last_thresh_y_)
177 4006 : ky = kd;
178 : else
179 1573 : ky = ku;
180 5579 : float res = last_thresh_y_ + ((static_cast<float>(deltaT) * ky) * (std::fabs(m) - last_thresh_y_));
181 5579 : last_thresh_y_ = res;
182 5579 : return res;
183 : }
184 :
185 : float
186 5579 : CongestionControl::get_thresh()
187 : {
188 5579 : return last_thresh_y_;
189 : }
190 :
191 : BandwidthUsage
192 5579 : CongestionControl::get_bw_state(float estimation, float thresh)
193 : {
194 5579 : if (estimation > thresh) {
195 : // JAMI_WARN("Enter overuse state");
196 309 : if (not overuse_counter_) {
197 307 : t0_overuse = clock::now();
198 307 : overuse_counter_++;
199 307 : return bwNormal;
200 : }
201 2 : overuse_counter_++;
202 2 : time_point now = clock::now();
203 2 : auto overuse_timer = now - t0_overuse;
204 2 : if ((overuse_timer >= OVERUSE_THRESH) and (overuse_counter_ > 1)) {
205 0 : overuse_counter_ = 0;
206 0 : last_state_ = bwOverusing;
207 : }
208 5270 : } else if (estimation < -thresh) {
209 : // JAMI_WARN("Enter underuse state");
210 1264 : overuse_counter_ = 0;
211 1264 : last_state_ = bwUnderusing;
212 : } else {
213 4006 : overuse_counter_ = 0;
214 4006 : last_state_ = bwNormal;
215 : }
216 5272 : return last_state_;
217 : }
218 :
219 : } // namespace jami
|