LCOV - code coverage report
Current view: top level - foo/src/media - congestion_control.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 77 90 85.6 %
Date: 2026-04-01 09:29:43 Functions: 15 15 100.0 %

          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

Generated by: LCOV version 1.14