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