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

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来完成动态调整每段时间的字节预算,使整体码率稳定在设计的目标值附近,同时平衡欠载和过载的情况。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式协同 - 分布式事务_2PC 3PC解决方案

文章目录 导图Pre2PC&#xff08;Two-Phase Commit&#xff09;协议准备阶段提交阶段情况 1&#xff1a;只要有一个事务参与者反馈未就绪&#xff08;no ready&#xff09;&#xff0c;事务协调者就会回滚事务情况 2&#xff1a;当所有事务参与者均反馈就绪&#xff08;ready&a…

计算机图形学知识点汇总

一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中&#xff0c;“形”指形体或形状&#xff0c;存在于客观世界和虚拟世界&#xff0c;它的本质是“表示”&#xff1b;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面&#xff0c;用于表达…

Nginx区分PC端和移动端访问

在使用Nginx时&#xff0c;可以通过$http_user_agent变量来判断用户访问的客户端类型&#xff0c;从而提供不同的内容或服务。下面是一个基于$http_user_agent变量来判断是否为PC访问的Nginx配置示例。 1. 理解$http_user_agent变量的含义及其在Nginx中的用途 $http_user_agen…

方法。。。

1. 方法概述 1.1 方法的概念 ​** 方法&#xff08;method&#xff09;是程序中最小的执行单元** 注意&#xff1a; 方法必须先创建才可以使用&#xff0c;该过程成为方法定义方法创建后并不是直接可以运行的&#xff0c;需要手动使用后&#xff0c;才执行&#xff0c;该过程…

jasypt原理

jasypt原理 一、背景知识二、原理分析1、(uml中蓝色)加载Encryptor、Detector和Resolver2、(uml中红色)加载EnableEncryptablePropertiesBeanFactoryPostProcessor3、(uml中绿色)解密过程 以jasypt 1.14为例 一、背景知识 需要了解spring的加载顺序&#xff1a; step1:主要是…

【UE5 C++课程系列笔记】13——GameInstanceSubsystem的简单使用

目录 概念 基本使用案例 效果 步骤 概念 UGameInstanceSubsystem 类继承自 USubsystem&#xff0c;它与 GameInstance 紧密关联&#xff0c;旨在为游戏提供一种模块化、可方便扩展和管理的功能单元机制。在整个游戏运行期间&#xff0c;一个 GameInstance 可以包含多个 UGa…

SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法

本篇博客将深入探讨 MyBatis-Plus 的三个核心功能&#xff1a;条件构造器、自定义 SQL 和 Service 接口的基本用法。通过对这些功能的学习和掌握&#xff0c;开发者能够更加高效地使用 MyBatis-Plus 进行业务开发。 目录 前言 条件构造器 自定义SQL Service接口基本用法 总结…

我的 2024 年终总结

2024 年&#xff0c;我离开了待了两年的互联网公司&#xff0c;来到了一家聚焦教育机器人和激光切割机的公司&#xff0c;没错&#xff0c;是一家硬件公司&#xff0c;从未接触过的领域&#xff0c;但这还不是我今年最重要的里程碑事件 5 月份的时候&#xff0c;正式提出了离职…

STM32-笔记11-手写带操作系统的延时函数

1、为什么带操作系统的延时函数&#xff0c;和笔记10上的延时函数不能使用同一种&#xff1f; 因为笔记10的延时函数在每次调用的时候&#xff0c;会一直开关定时器&#xff0c;而在FreeRTOS操作系统中&#xff0c;SysTick定时器当作时基使用。 时基是一个时间显示的基本单位。…

人工智能与物联网:从智慧家居到智能城市的未来蓝图

引言&#xff1a;未来已来&#xff0c;智能化的世界 想象一下&#xff0c;一个早晨&#xff0c;智能闹钟根据你的睡眠状态自动调整叫醒时间&#xff0c;咖啡机早已备好热腾腾的咖啡&#xff0c;窗帘缓缓拉开&#xff0c;迎接清晨的阳光。这不是科幻小说中的场景&#xff0c;而是…

流程控制

第一章 流程控制语句 在一个程序执行的过程中&#xff0c;各条语句的执行顺序对程序的结果是有直接影响的。所以&#xff0c;我们必须清楚每条语句的执行流程。而且&#xff0c;很多时候要通过控制语句的执行顺序来实现我们想要的功能。 1.1 流程控制语句分类 ​ 顺序结构 …

台球助教平台系统开发APP和小程序信息收藏功能需求解析(第十二章)

以下是开发台球助教系统客户端&#xff08;APP&#xff0c;小程序&#xff0c;H5&#xff09;几端的信息收藏功能的详细需求和功能说明&#xff0c;内容比较详细&#xff0c;可以说是一个教科书式的详细说明了&#xff0c;这套需求说明不仅仅用在我们的台球助教系统程序上&…

RISC-V 医疗芯片发展方向探究及展望

&#xff08;一&#xff09;研究背景与意义 近年来&#xff0c;RISC-V作为一种开源指令集架构在芯片领域迅速兴起。它起源于加州大学伯克利分校&#xff0c;于2011年首次公开发布&#xff0c;后凭借其独特优势吸引了全球众多企业、机构以及科研人员的关注与参与。RISC-V具有开…

三维动画的常用“视觉特效”有哪些?

在当今的视觉盛宴中&#xff0c;三维动画技术宛如一位神奇的魔法师&#xff0c;为视觉特效&#xff08;VFX&#xff09;领域施下了变革的咒语。从大荧幕上的震撼电影&#xff0c;到让人沉浸其中的视频游戏&#xff0c;再到夺人眼球的广告以及精细的模拟场景&#xff0c;三维动画…

【EtherCATBasics】- KRTS C++示例精讲(2)

EtherCATBasics示例讲解 目录 EtherCATBasics示例讲解结构说明代码讲解 项目打开请查看【BaseFunction精讲】。 结构说明 EtherCATBasics&#xff1a;应用层程序&#xff0c;主要用于人机交互、数据显示、内核层数据交互等&#xff1b; EtherCATBasics.h &#xff1a; 数据定义…

前端初学基础

一.Web开发 前端三件 HTML &#xff0c;页面展现 CSS&#xff0c;样式 JS(JavaScript),动起来 二&#xff0c;HTML 1.HTML概念 网页&#xff0c;网站中的一个页面&#xff0c;网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台。通俗的说&#xff0c;网站就…

C语言结构体位定义(位段)的实际作用深入分析

1、结构体位段格式 struct struct_name {type [member_name] : width; };一般定义结构体&#xff0c;成员都是int、char等类型&#xff0c;占用的空间大小是固定的在成员名称后用冒号来指定位宽&#xff0c;可以指定每个成员所占用空间&#xff0c;并且也不用受结构体成员起始…

机器学习之PCA降维

主成分分析&#xff08;PCA&#xff0c;Principal Component Analysis&#xff09; 主成分分析&#xff08;PCA&#xff09;是一种常见的无监督学习技术&#xff0c;广泛应用于数据降维、数据可视化以及特征提取等任务。PCA的目标是通过线性变换将数据从高维空间映射到低维空间…

x86_64 Ubuntu 编译安装英伟达GPU版本的OpenCV

手把手带你在Linux上安装带GPU加速的opencv库&#xff08;C版本&#xff09;_opencv linux-CSDN博客 cmake \-D CMAKE_BUILD_TYPERELEASE \-D OPENCV_GENERATE_PKGCONFIGON \-D CMAKE_INSTALL_PREFIX/usr/local \-D OPENCV_EXTRA_MODULES_PATH/home/hwj/opencv/opencv_contrib…

Bert各种变体——RoBERTA/ALBERT/DistillBert

RoBERTa 会重复一个语句10次&#xff0c;然后每次都mask不同的15%token。丢弃了NSP任务&#xff0c;论文指出NSP任务有时甚至会损害性能。使用了BPE ALBERT 1. 跨层参数共享 可以共享多头注意力层的参数&#xff0c;或者前馈网络层的参数&#xff0c;或者全部共享。 实验结果…