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