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