WebRTC服务质量(11)- Pacer机制(03) IntervalBudget

news2025/3/13 22:17:52

WebRTC服务质量(01)- Qos概述
WebRTC服务质量(02)- RTP协议
WebRTC服务质量(03)- RTCP协议
WebRTC服务质量(04)- 重传机制(01) RTX NACK概述
WebRTC服务质量(05)- 重传机制(02) NACK判断丢包
WebRTC服务质量(06)- 重传机制(03) NACK找到真正的丢包
WebRTC服务质量(07)- 重传机制(04) 接收NACK消息
WebRTC服务质量(08)- 重传机制(05) RTX机制
WebRTC服务质量(09)- Pacer机制(01) 流程概述
WebRTC服务质量(10)- Pacer机制(02) RoundRobinPacketQueue
WebRTC服务质量(11)- Pacer机制(03) IntervalBudget
WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

一、前言:

先想想,我们其实已经有了目标码率了,为什么还要控制码率?

因为,我们获取的目标码率是以秒为单位的,比如每秒钟发送300k,这是我们通过webrtc的网络拥塞控制可以获得的。 但是,具体到每个时间分片,我们前面看了pacer是5ms作为一个时间分片,IntervalBudget就是控制每个时间分片内应该发送的数据大小。

二、概念:

IntervalBudget 是 WebRTC 中的一个数据流速率控制工具,目的是在固定时间间隔内,根据目标码率动态管理和分配可以发送的字节数(预算字节)。它类似一个预算系统,平衡上一时间间隔的欠载(underuse)和过载(overuse)情况,确保整体码率维持在目标值,同时允许一定程度的积累和补偿。

详细分主要作用是:

  1. 维持目标码率(target_rate_kbps 根据目标速率(如 300 kbps),动态调整每段时间的字节发送目标。
  2. 处理过载和欠载 若某次时间片中没有充分利用发送预算,是否允许将未用的预算累积到后续时间片(由 can_build_up_underuse 决定)。
  3. 预算限制 限制过载和欠载的值,最高不能超过特定的预算范围(max_bytes_in_budget_)。
  4. 字节消耗管理 随着数据发送,会减少预算,维持运行中实时的发送和控制。

三、类定义:

class IntervalBudget {
 public:
  explicit IntervalBudget(int initial_target_rate_kbps);
  IntervalBudget(int initial_target_rate_kbps, bool can_build_up_underuse);
  // 用于设置目标码率
  void set_target_rate_kbps(int target_rate_kbps);

  // TODO(tschumim): Unify IncreaseBudget and UseBudget to one function.
  // 用于计算我们这个时间分片,有多少数据可以发送
  void IncreaseBudget(int64_t delta_time_ms);
  void UseBudget(size_t bytes);

  size_t bytes_remaining() const;
  double budget_ratio() const;
  int target_rate_kbps() const;

 private:
  // 通过带宽评估算法评估出的目标码率(也就是1s内发送多少数据)
  int target_rate_kbps_;
  // 在budget中最大可以存放多少字节
  int64_t max_bytes_in_budget_;
  // 在一个时间分片内,还有多少数据可以发送
  int64_t bytes_remaining_;
  bool can_build_up_underuse_;
};

四、Pacer中两个重要的IntervalBudget:

  IntervalBudget media_budget_; // 用于计算可以发送媒体的数据量
  IntervalBudget padding_budget_; // 用于计算可以发送padding的数据量

五、设置目标码率:

要使用IntervalBudget,就需要先设置目标码率。两种设置目标码率的途径:

  • 周期执行RtpTransportControllerSend::UpdateControllerWithTimeInterval ;
  • 通过收到对方发送来的transport-cc,然后使用OnTransportPacketsFeedback函数来分析,并更新目标码率;

5.1、调用UpdateControllerWithTimeInterval过程:

在这里插入图片描述

红框中的都属于RepeatingTask,进行任务的反复执行。

// 周期执行当前任务
bool RepeatingTaskBase::Run() {
  // 执行当前任务
  TimeDelta delay = RunClosure();

  // ...
  // 然后再把this打包成任务,放入线程的任务队列当中,之后这个线程又会执行Run,反复如此
  // delay.ms()是每隔多长时间执行这个任务
  task_queue_->PostDelayedTask(absl::WrapUnique(this), delay.ms());

  return false;
}

// 在其父类 RepeatingTaskBase 当中会通过Run来执行任务
template <class Closure>
class RepeatingTaskImpl final : public RepeatingTaskBase {
 public:
  RepeatingTaskImpl(TaskQueueBase* task_queue,
                    TimeDelta first_delay,
                    Closure&& closure,
                    Clock* clock)
      : RepeatingTaskBase(task_queue, first_delay, clock),
        closure_(std::forward<Closure>(closure)) {
    static_assert(std::is_same<TimeDelta, typename std::result_of<decltype (&Closure::operator())(Closure)>::type>::value, "");
  }

 private:
  // 执行当前任务
  TimeDelta RunClosure() override { return closure_(); }
  typename std::remove_const<typename std::remove_reference<Closure>::type>::type closure_;
};

5.2、根据Transport-cc执行:

当我们收到transport-cc反馈后,会执行下面函数:

// 每当我们收到transport-cc之后,就会调用下面的方法
void RtpTransportControllerSend::OnTransportFeedback(
    const rtcp::TransportFeedback& feedback) {
  feedback_demuxer_.OnTransportFeedback(feedback);
  auto feedback_time = Timestamp::Millis(clock_->TimeInMilliseconds());
  // 生成一个匿名函数任务,这个任务当中通过 OnTransportPacketsFeedback 来解析transport-cc中的内容
  // 解析完这个内容之后,就会计算我们当前的带宽是多少,拿到带宽就可以为budget设置目标码率了
  // 将返回值(也就是目标码率)传给PostUpdates,这里面会设置给budget目标码率
  task_queue_.PostTask([this, feedback, feedback_time]() {
    RTC_DCHECK_RUN_ON(&task_queue_);
    absl::optional<TransportPacketsFeedback> feedback_msg =
        transport_feedback_adapter_.ProcessTransportFeedback(feedback, feedback_time);
    if (feedback_msg && controller_) {
      PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg));
    }
    pacer()->UpdateOutstandingData(
        transport_feedback_adapter_.GetOutstandingData());
  });
}

就是解析出人家反馈给我们的内容,根据这个计算我们自己的目标码率,并设置给budget;

六、使用media_budget:

看下ProcessPackets如何使用media_budget的:

// 周期处理包的发送
void PacingController::ProcessPackets() {
    if (mode_ == ProcessMode::kPeriodic) {
      // 将前面设置给pacer的目标码率设置给media_budget
      media_budget_.set_target_rate_kbps(target_rate.kbps());
      UpdateBudgetWithElapsedTime(elapsed_time);
    } else {
      media_rate_ = target_rate;
    }
}

其中,elapsed_time就是逝去的时间,在UpdateBudgetWithElapsedTime当中会使用:

/**
 * 使用逝去的时间更新media_budget_
 */
void PacingController::UpdateBudgetWithElapsedTime(TimeDelta delta) {
  if (mode_ == ProcessMode::kPeriodic) {
    // 获取较小者(kMaxProcessingInterval是30ms)
    delta = std::min(kMaxProcessingInterval, delta);
    // 将delta时间传给media_budget
    media_budget_.IncreaseBudget(delta.ms());
    padding_budget_.IncreaseBudget(delta.ms());
  } else {
    media_debt_ -= std::min(media_debt_, media_rate_ * delta);
    padding_debt_ -= std::min(padding_debt_, padding_rate_ * delta);
  }
}

再看看传进去IncreaseBudget怎么使用的:

void IntervalBudget::IncreaseBudget(int64_t delta_time_ms) {
  // target_rate_kbps_是我们之前传入的目标码率
  // 加入我们现在目标码率target_rate_kbps_是300,delta是10ms,那么bytes约等于300字节,
  // 也就是说我们接下来这段时间可以发送的数据是300字节
  int64_t bytes = target_rate_kbps_ * delta_time_ms / 8;
  if (bytes_remaining_ < 0 || can_build_up_underuse_) {
    // We overused last interval, compensate this interval.
    // 这种情况说明过载了(比如上次要求发100,最终发了150,多发了50)
    // 那么我们就用本次应该发送的数据量减去(因为bytes_remaining_为负数,下面看着是加)上次多出来的50,就是本次要发送的
    // 当然如果大于我们budget可以发送的最大值max_bytes_in_budget_,那么,只能允许发送max_bytes_in_budget_
    bytes_remaining_ = std::min(bytes_remaining_ + bytes, max_bytes_in_budget_);
  } else {
    // If we underused last interval we can't use it this interval.
    // 如果没有过载,使用当前计算的结果
    bytes_remaining_ = std::min(bytes, max_bytes_in_budget_);
  }
}

我尝试再好好解释下:

IncreaseBudget 增加新的字节预算,用于下一时间片发送。

核心逻辑:
  1. 计算新增预算字节:

    bytes = target_rate_kbps_ * delta_time_ms / 8
    

    此预算决定时间间隔 delta_time_ms 内允许发送的字节数。

  2. 判断是否存在欠载积累(bytes_remaining_ 为负值),以及是否允许保留欠载(can_build_up_underuse_)。两种情况:

    • 允许欠载/欠载存在:补偿上一次过载,同时加入当前新增预算,更新剩余字节。
    • 不允许欠载/无欠载:仅允许当前时间段的预算。
  3. 使用 std::min() 限制最终预算,确保预算总值不超过 max_bytes_in_budget_

示例:
  • 目标码率:300 kbps,时间段:10 ms

    bytes = 300 * 10 / 8 = 375 字节
    
  • 欠载情况:假设 bytes_remaining_ = -100can_build_up_underuse = true

    bytes_remaining_ = min(-100 + 375, max_bytes_in_budget_)
                    = min(275, 375)
                    = 275 字节
    
  • 正常情况:没有欠载,直接按当前时间段计算:

    bytes_remaining_ = min(375, 375) = 375 字节
    

七、总结:

本文主要介绍了Pacer模块中怎么根据目标码率,然后通过IntervalBudget来完成动态调整每段时间的字节预算,使整体码率稳定在设计的目标值附近,同时平衡欠载和过载的情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2267179.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码 大家都知道使用CubMAX可以很快的将&#xff0c;FATFS文件管理系统移植到单片机上&#xff0c;但是别的芯片没有这么好用的工具&#xff0c;就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题&#xff0c;然后再移植FATFS文件管理系统。 二、SD…

Ollama+OpenWebUI+llama3本地部署

引言 llama3在4月19日刚刚发布&#xff0c;官方的对比结果中在开源模型中堪称世界第一&#xff0c;整好周六日有时间&#xff0c;在魔搭社区上测试一下 2 安装Ollama 2.1 下载Ollama 登录Ollama官网下载Ollama安装包 GitHub&#xff1a;https://github.com/ollama/ollama?t…

新品:SA628F39大功率全双工音频传输模块

SA628F39是一款高集成度的8W大功率全双工无线数据语音一体通话模块&#xff0c;专为高效、稳定的远程通信设计。该模块内置高速微控制器、高性能射频芯片、功率放大器、ESD静电保护和硬件看门狗芯片&#xff0c;具备反接保护、过流过压保护和防死机保护等多重安全功能&#xff…

Unity EasyAR入门教程

文章目录 Easy不用下载盗版安装SDK注册应用ID最简单的识别注意点 Easy不用下载盗版 EasyAR可以免费试用&#xff0c;如果不在乎水印&#xff0c;直接去官网下载即可 EasyAR官网 安装SDK 先下载&#xff0c;官网找到下载页&#xff0c;如下选择 下载后不是普通的unityPackag…

ID卡网络读卡器Python小程序开发

UDP网络读卡器Python示例代码。 主页UI设计界面&#xff1a; UI代码&#xff1a; from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Frame(object):def setupUi(self, Frame):Frame.setObjectName("Frame")Frame.resize(1082, 685)font QtGui.QFont()font.set…

yarn list --pattern vuex-module-decorators

dgqdgqdeMac-mini spid-admin % yarn list --pattern vuex-module-decorators yarn list v1.22.22 └─ vuex-module-decorators0.16.1 ✨ Done in 0.24s.好的&#xff0c;这段代码是一个典型的 Vuex 模块定义&#xff0c;使用了 vuex-module-decorators 库。这个库为 Vuex 提…

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)Proteus中Unknown 1-byte opcode / Unknown 2-byte opcode错误

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …

【HENU】河南大学计院2024 计算机网络 期末复习知识点

和光同尘_我的个人主页 一直游到海水变蓝。 计网复习 第一章互联网组成类别交换方式分组交换的要点&#xff1a;分组交换的优点&#xff1a; 网络性能指标体系结构网络协议五层协议 第二章&#xff1a;物理层物理层的主要任务&#xff08;四大特性&#xff09;通信的三种方式…

chatwoot 开源客服系统搭建

1. 准备开源客服系统&#xff08;我是用的Chatwoot &#xff09; 可以选择以下开源客服系统作为基础&#xff1a; Chatwoot: 功能强大&#xff0c;支持多渠道客户对接&#xff0c;&#xff08;支持app&#xff0c;web&#xff09;。Zammad: 现代的开源工单系统。FreeScout: 免…

python爬虫----爬取视频实战

python爬虫-爬取视频 本次爬取&#xff0c;还是运用的是requests方法 首先进入此网站中&#xff0c;选取你想要爬取的视频&#xff0c;进入视频页面&#xff0c;按F12&#xff0c;将网络中的名称栏向上拉找到第一个并点击&#xff0c;可以在标头中&#xff0c;找到后续我们想要…

【PPTist】表格功能

前言&#xff1a;这篇文章来探讨一下表格功能是怎么实现的吧&#xff01; 一、插入表格 我们可以看到&#xff0c;鼠标移动到菜单项上出现的提示语是“插入表格” 那么就全局搜索一下&#xff0c;就发现这个菜单在 src/views/Editor/CanvasTool/index.vue 文件中 <Popov…

Web安全攻防入门教程——hvv行动详解

Web安全攻防入门教程 Web安全攻防是指在Web应用程序的开发、部署和运行过程中&#xff0c;保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现&#xff0c;还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。 本教程将带你入门Web安全攻防…

游戏开发线性空间下PS工作流程

前言 使用基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui 在游戏引擎中的渲染结果与 PS 中的不一致&#xff1a; PS&#xff08;颜色空间默认是sRGB伽马空间&#xff09;&#xff1a…

Segment Routing Overview

大家觉得有意义和帮助记得及时关注和点赞!!! Segment Routing (SR) 是近年来网络领域的一项新技术&#xff0c;“segment” 在这里 指代网络隔离技术&#xff0c;例如 MPLS。如果快速回顾网络设计在过去几十年的 发展&#xff0c;我们会发现 SR 也许是正在形成的第三代网络设计…

【连续学习之随机初始化算法 】2024Nature期刊论文Loss of plasticity in deep continual learning

1 介绍 年份&#xff1a;2024 期刊&#xff1a;Nature Dohare S, Hernandez-Garcia J F, Lan Q, et al. Loss of plasticity in deep continual learning[J]. Nature, 2024, 632(8026): 768-774. 本文提出的算法是“持续反向传播”&#xff08;continual backpropagation&a…

【NODE】01-fs和path常用知识点

前言 最近在使用express-generator知识进行搭建前后端通信&#xff0c;其中有些知识点涉及到nodejs的fs和path核心模块&#xff0c;因此另写一篇文章进行介绍和代码案例练习。 fs&#xff08;文件系统&#xff09;和 path 是 Node.js 的核心模块&#xff0c;用于文件操作和路径…

两分钟解决:vscode卡在设置SSH主机,VS Code-正在本地初始化VSCode服务器

问题原因 remote-ssh还是有一些bug的&#xff0c;在跟新之后可能会一直加载初始化SSH主机解决方案 1.打开终端2.登录链接vscode的账号&#xff0c;到家目录下3.找到 .vscode-server文件,删掉这个文件4.重启 vscode 就没问题了

干货ScottPlot4向ScottPlot5迁移

干货ScottPlot4向ScottPlot5迁移 干货满满1.背景2.需求的引出3.先说结论1.好消息2.坏消息 4.迁移的部分笔记ColorScottPlot.PlottableScottPlot.Plottables中的对象如何定义添加 ScottPlot.Plottable.ScatterPlot 对象ScatterPolygonMarker也类似 Scatter的marker formsPlot1Re…

Github优质项目推荐(第九期)

文章目录 Github优质项目推荐&#xff08;第九期&#xff09;一、【tldraw】&#xff0c;37.1k stars - 在 React 中创建无限画布体验的库二、【zapret】&#xff0c;9.1k stars - 独立&#xff08;无需第三方服务器&#xff09;DPI 规避工具三、【uBlock】&#xff0c;48.3k s…

学习threejs,THREE.PlaneGeometry 二维平面几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️HREE.PlaneGeometry 二维平…