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