WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

article/2025/10/6 16:29:06

WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

  • WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传
  • 前言
    • 一、NACK与RTX的作用
      • 1、NACK/RTX的工作机制的流程图
      • 2、NACK/RTX涉及到的几个问题
    • 二、判断包位置的关键算法
      • 1、一个关键的函数:AheadOf
    • 三、WebRTC中NACK的处理流程图
      • 1、NACK调用栈
    • 四、WebRTC如何判断是否丢包的逻辑
      • 1、NackModule::OnReceivedPacket 函数
      • 2、 NackModule::AddPacketsToNack初步判断有哪些包丢包了
      • 3、 GetNackBatch (真真判定丢包的函数)
      • 4、周期性执行的函数NackModule::Process
      • 5、Process函数和OnReceivedPacket函数都有有发送nack函数目的
    • 五、WebRTC中VP8关键帧的判断
      • 1、VP8 RTP结构图
      • 2、 VP8 Payload 结构图
      • 3、 VP8 Payload描述符两种结构
        • 3.1、VP8 Payload 区别和字段含义
        • 3.2、 描述符中的扩展字节
      • 4、VP8 Payload Header
        • 4.1、VP8 Payload header 字段含义
      • 5、KeyFrame Header 乘下的7个字节 含义
  • 总结:


WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

1、WebRTC中线程模型和常见线程模型介绍

2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒体协商

三、 WebRTC 音频数据采集

四、 WebRTC 音频引擎(编解码和3A算法)

五、 WebRTC 视频数据采集

六、 WebRTC 视频引擎( 编解码)

七、 WebRTC 网络传输

1、WebRTC的ICE之STUN协议

2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解

八、 WebRTC服务质量(Qos)

1、WebRTC中RTCP协议详解

2、WebRTC中RTP协议详解

3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

九、 NetEQ

十、 Simulcast与SVC

前言

NACK 是判断网络是否丢包重传, 和网络情况

一、NACK与RTX的作用

1、NACK用于通知丢失了哪些包
2、RTX用于重传丢失的包

在发送offer和answer中是否正常Nack和RTX的协议

···
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
···

1、NACK/RTX的工作机制的流程图

在这里插入图片描述

2、NACK/RTX涉及到的几个问题

1、如何判定算法发送了丢包
Sequence Number 不连续的时候是丢包了哈
2、NACK什么时候发送
3、NACK的格式是怎样的,当发送端收到NACK时如何处理?
4、RTX格式是怎样的,RTX与NACK如何配合的?

二、判断包位置的关键算法

1、一个关键的函数:AheadOf

在这里插入图片描述

1、 AheadOf(a,b), 比较a与b的顺序关系
2、a与b必须是无符号整数
3、如果吧排在a前面,则返回真,否则返回false
4、需要注意的是,他们不是简单的数值大小的比较


三、WebRTC中NACK的处理流程图

在这里插入图片描述

1、NackMoudle的创建时机

在RtpVideoStreamReceiver中构造函数中创建NackMoudle类哈

RtpVideoStreamReceiver是在VideoReceiveStream类的构造函数创建哈

1、NACK调用栈

在这里插入图片描述

四、WebRTC如何判断是否丢包的逻辑

在这里插入图片描述

1、NackModule::OnReceivedPacket 函数

moudle/video_codeing/nack_module.h


int NackModule::OnReceivedPacket(uint16_t seq_num,bool is_keyframe,bool is_recovered) {rtc::CritScope lock(&crit_);// TODO(philipel): When the packet includes information whether it is//                 retransmitted or not, use that value instead. For//                 now set it to true, which will cause the reordering//                 statistics to never be updated.bool is_retransmitted = true;// 1. 判断是否第一次, 初始化  完成就退出if (!initialized_) {newest_seq_num_ = seq_num;if (is_keyframe)// 这个包是否关键帧===》》 为什么要识别关键帧???keyframe_list_.insert(seq_num);initialized_ = true;return 0;}// Since the |newest_seq_num_| is a packet we have actually received we know// that packet has never been Nacked.// 2. 如果这次来的seq与上次一样,是重复包, 退出if (seq_num == newest_seq_num_)return 0;// 即不是第一个包和重复包就判断包顺序哈 seq_num在newest_seq_num之前就要删除了哈  // 3. 如果是上次处理前面的包, 这个包已经失效了, 如果还在nack列表中, 需要删除的// 说明这个包晚到达了 if (AheadOf(newest_seq_num_, seq_num)) {// An out of order packet has been received.auto nack_list_it = nack_list_.find(seq_num);int nacks_sent_for_packet = 0;if (nack_list_it != nack_list_.end()) {nacks_sent_for_packet = nack_list_it->second.retries;nack_list_.erase(nack_list_it);}if (!is_retransmitted)UpdateReorderingStatistics(seq_num);return nacks_sent_for_packet;}// Keep track of new keyframes.// 4. 如果判断是否是key帧??? 哈if (is_keyframe)keyframe_list_.insert(seq_num); // 如果该报属于key帧, 保持起来// And remove old ones so we don't accumulate keyframes.// 5. 找到最小边界点,   超出10000个就要删除之前的数据 , 这个是实时系统auto it = keyframe_list_.lower_bound(seq_num - kMaxPacketAge);if (it != keyframe_list_.begin())keyframe_list_.erase(keyframe_list_.begin(), it);// 6. 如何判断是否找回来的包???  恢复包if (is_recovered) {recovered_list_.insert(seq_num); // 如果该包是属于key帧,保持起来// Remove old ones so we don't accumulate recovered packets.//  是否超出项 超出项也删除了  , 最大项也是10000哈auto it = recovered_list_.lower_bound(seq_num - kMaxPacketAge);if (it != recovered_list_.begin())recovered_list_.erase(recovered_list_.begin(), it);// Do not send nack for packets recovered by FEC or RTX.return 0;}// 7. 什么情况会走到这边呢 //     1、不是第一个包//     2. 不是一个重复的包//     3、 不是在new_seq_num之前的包//     4、 不是一个恢复包//   有两种情况会走到这边//     1、  上一次处理的包的后面的一个包哈   有序的包//     2、  上一次处理的包 后面隔好几个包   AddPacketsToNack(newest_seq_num_ + 1, seq_num);newest_seq_num_ = seq_num;// Are there any nacks that are waiting for this seq_num.// 8. 哪些包是真真丢包的  就告诉对方从新发送包哈std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly);if (!nack_batch.empty()) nack_sender_->SendNack(nack_batch);  //  需要重传哈   放到缓冲区了 ??????return 0;
}

2、 NackModule::AddPacketsToNack初步判断有哪些包丢包了

void NackModule::AddPacketsToNack(uint16_t seq_num_start,uint16_t seq_num_end) {// Remove old packets.auto it = nack_list_.lower_bound(seq_num_end - kMaxPacketAge);nack_list_.erase(nack_list_.begin(), it);// If the nack list is too large, remove packets from the nack list until// the latest first packet of a keyframe. If the list is still too large,// clear it and request a keyframe.// 1. 开始到结束之间有多大距离 uint16_t num_new_nacks = ForwardDiff(seq_num_start, seq_num_end);if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {while (RemovePacketsUntilKeyFrame() &&nack_list_.size() + num_new_nacks > kMaxNackPackets) {}// 1.1、 极端情况  没有删除, 就要清除nack, 然后发送请求关键帧给对方  让解码器从新工作哈if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {nack_list_.clear();RTC_LOG(LS_WARNING) << "NACK list full, clearing NACK"" list and requesting keyframe.";keyframe_request_sender_->RequestKeyFrame();return;}}// 2、 遍历seq_num_start 到seq_num_end 之间 是否有丢包 有的话 就放到nack_list_中哈for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) {// Do not send nack for packets that are already recovered by FEC or RTX// 2.1 是否已经通过FEC或者RTX恢复了 该包 恢复了 就不需要放到nack_list_列表中去哈if (recovered_list_.find(seq_num) != recovered_list_.end())continue;NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5),clock_->TimeInMilliseconds());RTC_DCHECK(nack_list_.find(seq_num) == nack_list_.end());nack_list_[seq_num] = nack_info;}
}

3、 GetNackBatch (真真判定丢包的函数)

// 遍历所有可疑包 如果包符合条件 就插入nack_batch中
std::vector<uint16_t> NackModule::GetNackBatch(NackFilterOptions options) {// 1. 标识以seq_num为判断条件bool consider_seq_num = options != kTimeOnly;// 2. 标识以timestamp为判断条件 bool consider_timestamp = options != kSeqNumOnly;int64_t now_ms = clock_->TimeInMilliseconds();std::vector<uint16_t> nack_batch;auto it = nack_list_.begin();while (it != nack_list_.end()) {// 1. send_nack_delay_ms_ 默认为0 , 可修改bool delay_timed_out = now_ms - it->second.created_at_time >= send_nack_delay_ms_;// 2. 从一次发送开始到现在, 是否超过了一个RTT的回路的时长 时间  // 需要得到一个RTT防止重复传送的情况 bool nack_on_rtt_passed = now_ms - it->second.sent_at_time >= rtt_ms_;// 3、 第一次发送和最后处理包之前的bool nack_on_seq_num_passed = it->second.sent_at_time /*如果是第一次发送*/== -1 &&AheadOrAt(newest_seq_num_, it->second.send_at_seq_num)/*该包在最后处理的包之前*/;// 符合条件if (delay_timed_out && ((consider_seq_num && nack_on_seq_num_passed) ||(consider_timestamp && nack_on_rtt_passed))) {nack_batch.emplace_back(it->second.seq_num);++it->second.retries;it->second.sent_at_time = now_ms;// 尝试10次 在nack_list列表中没有发现 就要删除了if (it->second.retries >= kMaxNackRetries/*kMaxNackRetries= 10*/) {RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num<< " removed from NACK list due to max retries.";it = nack_list_.erase(it);} else {++it;}continue;}++it;}return nack_batch;
}

其中要的判断条件

在这里插入图片描述

4、周期性执行的函数NackModule::Process


void NackModule::Process() {if (nack_sender_) {std::vector<uint16_t> nack_batch;{rtc::CritScope lock(&crit_);nack_batch = GetNackBatch(kTimeOnly);}if (!nack_batch.empty())nack_sender_->SendNack(nack_batch);}// Update the next_process_time_ms_ in intervals to achieve// the targeted frequency over time. Also add multiple intervals// in case of a skip in time as to not make uneccessary// calls to Process in order to catch up.int64_t now_ms = clock_->TimeInMilliseconds();if (next_process_time_ms_ == -1) {next_process_time_ms_ = now_ms + kProcessIntervalMs;} else {next_process_time_ms_ = next_process_time_ms_ + kProcessIntervalMs +(now_ms - next_process_time_ms_) /kProcessIntervalMs * kProcessIntervalMs;}
}

5、Process函数和OnReceivedPacket函数都有有发送nack函数目的

  1. 一个是时间查找丢失的包
  2. 一个seq_num的顺序查找丢失包

这个WebRTC在m74版本中设计怎么样哈 , 给你会怎么设计这个丢包重传哈

五、WebRTC中VP8关键帧的判断

1、VP8 RTP结构图

在这里插入图片描述

2、 VP8 Payload 结构图

在这里插入图片描述

3、 VP8 Payload描述符两种结构

在这里插入图片描述

3.1、VP8 Payload 区别和字段含义

1、 两者的区别

pictureID: 第一个7位第二种15位

2、 字段含义

X:代表是否下面一行有扩展 [I|L|T|K|RSV]
R:预留的字段
N:  1:代表是非参考帧,0:是参考帧
S:是否属于视频帧分片  1:视频帧的第一个分片 0:其他分片
R:预留位
PID:表示分片的序号 , 最大不超过8

3.2、 描述符中的扩展字节

1、I,该位置1, 表示存在PictureID,且其紧跟在扩展字节之后
2、L, 该位置1, 表示存在TLOPICIDX,它跟在PictureID之后,且T位必须置1
3、T,该位置1或者(T=0其K=1),TID/Y/KEYIDX字节存在,否则不存在
4、K,置1,TID/Y/KEYIDX字节存在;T=1且K=0,KEYIDX域被忽略;否则TID/Y/KEYIDX字节不存在
5、SRV,保留,必须设置为0

需要注意的点
1、PictureID域由M和PictureID组成。
M=1 :PictureID占15位;
M = 0 : 占7位
2、 TID= 0 , TLOPICIDX表示的是时间基础层帧的运行索引
视频的分层
3、TID> 0 : TLOPICIDX表示的是当前图像所依赖的基础层帧
4、TID/Y/KEYIDX域中,TID占两位,Y占1位,KEYIDX占5位
TID:代表时间层,基础层为0,层级越高,值越大
Y: 层同步位,置1,表示当前帧仅依赖基础层帧(TIDO);置0表示当前帧不依赖基础帧
KEYIDX:key帧索引值

4、VP8 Payload Header

在这里插入图片描述

4.1、VP8 Payload header 字段含义

一、 Header
1、对于内部帧,该域占3字节;对于key帧,该域占10字节
2、其中前3个字节的结构是通用的
3、该Header只有在上面的S位置位且PID=0时才存在
二、字段含义
1、P,1位,表示帧类型: 0-key, 1-infterframe 关键帧
2、VER,3位,1-3定义了4种不同的解码复杂度的profile
3、SIze, 19位,第一个分片字节的大小

5、KeyFrame Header 乘下的7个字节 含义

1、 3字节起始码: 固定值: 0x9D,0x01,0x2A
2、接下来的16位:(2bit Horizontal Scale << 14) | Width (14bits)
3、最后16位: (2bits Vertical Scale << 14)| Height (14bits)

总结:

WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc


http://chatgpt.dhexx.cn/article/5dxTPUyM.shtml

相关文章

WebRTC 的音频弱网对抗之 NACK

本文梳理 WebRTC 的音频弱网对抗中的 NACK 机制的实现。音频的 NACK 机制在 WebRTC 中默认是关闭的&#xff0c;本文会介绍开启 NACK 机制的方法。 在网络数据传输中&#xff0c;NACK (NAK&#xff0c;negative acknowledgment&#xff0c;not acknowledged) 是数据接收端主动…

流媒体弱网优化之路(NACK)——纯NACK方案的优化探索

流媒体弱网优化之路(NACK)——纯NACK方案的优化探索 —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全配置&#xff…

WebRTC源码分析 nack详解

1、Nack过程 1.1 nack是什么 丢包重传(NACK)是抵抗网络错误的重要手段。NACK在接收端检测到数据丢包后&#xff0c;发送NACK报文到发送端&#xff1b;发送端根据NACK报文中的序列号&#xff0c;在发送缓冲区找到对应的数据包&#xff0c;重新发送到接收端。NACK需要发送端&am…

谈谈网络通信中的 ACK、NACK 和 REX

目录 名词解释 问题 1&#xff1a; 接收方如何判断数据包是否丢失&#xff1f; 问题 2&#xff1a;发送方如何确认数据包已经丢失&#xff1f; 问题 3&#xff1a;重传超时的计算规则&#xff1f; 问题 4&#xff1a;发送方的数据包要缓存多久&#xff1f; 问题 5&#x…

费马定理_高数_1元微积分

定理 设 f(x) 在 x0 点处满足&#xff1a;1、可导 2、取得极值&#xff0c;则有 f ’ (x0)0 证明 不妨假设 f(x) 在点 x0 处取得极大值&#xff0c;则存在 x0 的邻域 U( x0 )&#xff0c;对任意的 x属于U( x0 )&#xff0c;都有 根据导数定义与极限的保号性有 又 f(x) 在点…

老猿Python博客文章目录索引

本目录提供老猿Python所有相关博文的一级目录汇总&#xff0c;带星号的为付费专栏&#xff1a; 一、专栏列表 本部分为老猿所有专栏的列表&#xff0c;每个专栏都有该专栏置顶的博文目录&#xff1a; Python基础教程目录PyQt入门学习* 使用PyQt开发图形界面Python应用PyQtmo…

(组合数+快速幂+lucas+费马小引理)acwing 887. 求组合数 III

887. 求组合数 III 题目链接https://www.acwing.com/problem/content/889/ 题目&#xff1a; 思路&#xff1a; #include<iostream> #include<cstdio> using namespace std; typedef long long LL; int n; int qmi(LL a,int k,int p){int res1;while(k){if(k&…

(组合数 +快速幂+逆元+费马小引理)acwing 886. 求组合数 II

886. 求组合数 II 题目链接https://www.acwing.com/problem/content/888/ 题目&#xff1a; 思路&#xff1a; #include<iostream> #include<cstdio> using namespace std; typedef long long LL; const int mod1e97;//mod为质数 int a,b,n; int fact[100010],…

Leetcode-数学费马平方和定理-633. 平方数之和

题目633. 平方数之和&#xff1a; 题解&#xff1a; 1&#xff0c;暴力枚举sqrt class Solution { public:bool judgeSquareSum(int c) {if(c < 2 ){return true;}for(long a 0; a*a < c; a){double b sqrt(c - a * a);if((int)b b){return true;}}return false;} …

费马定理、罗尔中值定理、零点存在定理、拉格朗日中值定理、

介值定理 介值定理如果AB,则开区间&#xff08;a,b&#xff09;内可能取不到端点值A,B 即介值定理如果C不等于端点值&#xff0c;那么 ξ ξ ξ可以是属于开区间&#xff0c;如果C等于端点值&#xff0c;那么 ξ ξ ξ必须属于闭区间。 零点存在定理 假设函数f(x)在闭区间[a,…

费马引理的证明

设 f(x)在 ξ处最大&#xff0c;故不论Δx是正或负&#xff0c;总有 设 &#xff0c; 则 。 故由极限的保号性有 &#xff08;1&#xff09; 而当 时&#xff0c; &#xff0c; 故 &#xff08;2&#xff09; 由(1)&#xff0c;(2)两式及 存在知&#xff0c;必有 设 f(x)在 ξ处…

费马引理

转载于:https://www.cnblogs.com/fuhang/p/7976329.html

高等数学——微分中值定理

本文始发于个人公众号&#xff1a;TechFlow&#xff0c;原创不易&#xff0c;求个关注 今天和大家回顾一下高数当中的微分中值定理&#xff0c;据说是很多高数公式的基础。由于本人才疏学浅&#xff0c;所以对于这点没有太深的认识。但是提出中值定理的几个数学家倒是如雷贯耳&…

(机器学习、人工智能数学基础:高等数学篇)第三章:微分中值定理:第一节:微分中值定理

文章目录 一&#xff1a;费马引理二&#xff1a;罗尔定理三&#xff1a;拉格朗日中值定理四&#xff1a;柯西中值定理五&#xff1a;泰勒公式&#xff08;1&#xff09;泰勒公式&#xff08;2&#xff09;常见泰勒展开式 一&#xff1a;费马引理 费马引理&#xff1a;如果函数…

高等数学(总结1-导数的几个定理)

1&#xff09;费马引理&#xff1a; 证明的关键&#xff1a;如果在x0处可导&#xff0c;则在x0处的左导数等于右导数等于导数。 意义&#xff1a;显然(x0&#xff0c;f(x0))是f(x)在x0邻域内的一个极值点&#xff0c;推广&#xff1a;f(x)的极值点&#xff08;驻点&#x…

人工智能数学基础:费马引理、罗尔定理、拉格朗日微分中值定理、柯西中值定理

一、费马&#xff08;Fermat&#xff09;引理 费马&#xff08;Fermat&#xff09;引理&#xff1a;设函数f(x)在点x0的某邻域U(x0)内有定义&#xff0c;并且在x0处可导&#xff0c;如果对任意的x∈U(x0)&#xff0c;有f(x)≤f(x0)(或f(x)≥f(xo))&#xff0c;那么f’(x0)0。 …

nmon结果分析工具_使用nmon analyzer 分析指标

监控结果分析 使用nmon analyser对nmon文件进行解析&#xff0c;生成带图表信息的xlsx格式文件。 下载地址&#xff1a;http://nmon.sourceforge.net/pmwiki.php?nSite.Nmon-Analyser nmon analyser转换数据 进入工具目录&#xff0c;双击nmon analyser vxx_x.xlsm格式文件&…

Linux数据存储监控工具 - nmon

nmon介绍 nmon 是 IBM 公司开发的 Linux 性能监控工具&#xff0c;可以实时展示系统性能情况&#xff0c;也可以将监控数据写入文件中&#xff0c;并使用 nmon 分析器做数据展示。 nmon 可监控的数据类型 CPU使用率内存使用情况磁盘适配器文件系统中的可用空间页面空间和页面速…