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

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

  1. WebRTC系列-Qos系列之发送NACK

    文章目录 1. NACK简述 2. 判断包位置的算法 2. WebRTC中NACK的处理流程 2.1 主要流程函数分析 2.2 是否丢包判断 2.3 乱序包的处理 在之前的文章 WebRTC系列-Qo ...

  2. 如何判断BUG是属于前端还是后端(抓包/日志定位分析)

    (我的公众号"墨石测试攻略",关注可免费获取整套接口测试实战项目!) 有的BUG看一眼就知道是前端还是后端的,而有些BUG则需要借助抓包工具(Fiddler.Charlers.浏览 ...

  3. java判断zip包的编码格式_java解压zip包出现乱码

    解决思路: 首先判断需要解压的文件是否存在或路径是否正确,接着判断路径是否存在,若不存在则创建路径,然后判断压缩文件是否合法,最后设置文件名称编码为"GBK"即可. 免费在线教学视 ...

  4. TCP数据包发送的过程及实际问题解决 判断socket是否断开 解决socket数据传输数据丢失的问题

    文章目录 一.头条之问题引出 二.tcp发送数据过程 三.我的问题解决(判断socket是否断开) 四.头条之问题回答 这是篇文章是在看了今日头条后,回想起来自己曾今遇到过的一个关于socket套接字 ...

  5. python输入生日判断星座_用java编一个程序能通过用户输入的生日判断用户的星座。请大神们给点提示或者思路吧。规定不能自定义方法...

    既然不允许用其他的方式,那就直接上代码吧.... public static void main(String[] args) { int point = -1; String[] str = {&q ...

  6. WebRTC Native M96 SDK接口封装--muteLocalVideoStream开关本地视频发送

    https://dabaojian.blog.csdn.net/article/details/123587207 已经介绍了如何开关本地预览,现在介绍一下mute/unmute本地音视频设备的接口. ...

  7. WebRTC Native M96 SDK接口封装--muteLocalAudioStream开关本地音频发送

    摊牌了,水了一篇. 因为之前介绍了muteLocalVideoStream接口的实现,那么实现muteLocalAudioStream就大同小异了. 接口定义(Agora) 接口名:    muteL ...

  8. 物理环路造成网络风暴,无法访问交换机故障处理过程

       问题处理过程: 一天下午,突然工位的pc192.168.205.201无法ping通机柜汇聚交换机192.168.205.15,ping的结果是无法访问目标主机. 已知,公司组网如下图: 查看a ...

  9. 10没有基于策略的qos_WebRTC QoS | NACK 格式与发送策略

    本文是 WebRTC QoS 第 1 篇 导读 10.20.100.1000.10000 策略 1,10 次 策略 2,20 毫秒 策略 3,100 毫秒 策略 4,1000 个(丢失包数量) 策略 ...

  10. WebRTC GCC拥塞控制算法详解

    1.WebRTC版本 m74 2.GCC的概念 GCC全称Google Congest Control,所谓拥塞控制,就是控制数据发送的速率避免网络的拥塞.可以对比TCP的拥塞控制算法,由于WebRT ...

最新文章

  1. websocket服务器响应头,从服务器发送响应握手后,websocket.onopen不会触发
  2. http://acm.hrbeu.edu.cn/index.php?act=problemid=1001cid=19 人工湖的公路
  3. python如何读取数据并输出为表格_Python实现将数据库一键导出为Excel表格的实例...
  4. 微信公众号使用Chrome插件:Markdown Nice优化微信公众号排版教程
  5. Python ImportError: No module named Image
  6. 浅谈JS中的原型对象和原型链
  7. 数据分析sql面试必会6题经典_经典SQL面试题及答案分析
  8. SocksProxy代理服务器下载,附IE使用socks代理的方法
  9. c语言程序输出三角,C语言小程序之输出“上三角”
  10. python google 搜索结果爬取_对于 Python 抓取 Google 搜索结果的一些了解
  11. html前端页面的字体大小,JQuery 改变页面字体大小的实现代码(实时改变网页字体大小)...
  12. Wow~70G上市公司定期报告数据集!
  13. 树莓派GPIO远程控制继电器
  14. Boot(重点SCSS☆☆☆☆☆)(day03)
  15. 汽车侧向动力学模型简介(动力学建模入门知识)
  16. [HTML]解决html5中设置的颜色和浏览器显示的颜色不一致的问题
  17. WWW相关概念的学习
  18. LabVIEW控制Arduino采集光敏电阻数值(基础篇—14)
  19. newifi3 web认证_newifi新路由3图文设置教程 | 192.168.1.1登陆页面
  20. UG NX机械手抓取实物的仿真,包涵PLC程序,触摸屏程序

热门文章

  1. 金蝶EAS系统,供应链,即时库存查询,库存查询SQL脚本
  2. 寻找因数——算法简化
  3. matlab程序特殊符号,MATLAB——matlab特殊符号表【转载】
  4. 【机器学习实战】利用朴素贝叶斯算法(naive_bayes)实现新闻分类
  5. 谷歌浏览器如何截取整个网页保存为图片截取整个网页长图
  6. 上海大学计算机专业就业薪资,人均月薪过万?985、211、普通大学毕业生薪资水平大起底!和你想的不一样……...
  7. 怎么缩小图片大小kb
  8. php 相似文章,php 比较两篇文章的相似度的方法
  9. 定制开发 app 的好处都有哪些?
  10. markdown编辑器Typora的使用方法(保姆级教程)