当前位置: 首页 > news >正文

WebRTC研究:audio 丢包判断

当收到一个包时,丢包判断原理:

将 当前收包序列号:sequence_number 从丢包集合 nack_list_ 中剔除;

将 sequence_number 与 最近收到的新包:sequence_num_last_received_rtp_ 对比,判断当前包是否为重传或乱序包。如若是的话,直接终止判断,否则继续;

对比 当前包 与 最新收到的包 的序列号与时间戳,更新每个包的样本数:samples_per_packet_;

更新 nack_list_ 中部分包的丢包状态:is_missing = true,这些包我们之前认为后续再考虑,现在需要把它们当做丢包。这些包满足:序列号 在 sequence_number - nack_threshold_packets_ 之前(nack_threshold_packets_ = 2);

如果 最新收到的包 与 当前包 不连续,则区间内的包都要被放到 nack_list_,其中那些太旧的包(判断标准同上一步)会被直接认为丢包,剩下的包当后续包达到时,会在上一步中再明确是否为丢包;

将 最近收到的新包 的序列号: sequence_num_last_received_rtp_ 与时间戳更新为 当前包 的序列号与时间戳;

丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制(最大容量 max_nack_list_size_ = 500,即对于单位长度为 20ms 的帧,保留 10s 长度)。不能重传的包满足序列号在 最新收到的包序列号 - static_cast<uint16_t>(max_nack_list_size_) - 1 之前。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

一、判断当前收包是否为重传或乱序包:

/*
sequence_number:当前收到的包的序列号
timestamp:当前收到的包的时间戳
*/
void NackTracker::UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp)
{
  /*
  如果这是第一个包
  any_rtp_received_ = true,表示收到了任意包
  */
  if (!any_rtp_received_) 
  {
	/* 第一个包仅仅记录序列号与时间戳 */
    sequence_num_last_received_rtp_ = sequence_number;
    timestamp_last_received_rtp_ = timestamp;
    any_rtp_received_ = true;

    // If no packet is decoded, to have a reasonable estimate of time-to-play
    // use the given values.
    if (!any_rtp_decoded_) 
	{
      sequence_num_last_decoded_rtp_ = sequence_number;
      timestamp_last_decoded_rtp_ = timestamp;
    }

    return;
  }

  /* 如果是上一个包, */
  if (sequence_number == sequence_num_last_received_rtp_)
    return;

  /* nack_list_:丢包集合 */
  nack_list_.erase(sequence_number);

  /*
  将当前包 与 最近收到的新包 对比,判断当前包是否为重传或乱序包
  是的话,直接返回
  */
  if (IsNewerSequenceNumber(sequence_num_last_received_rtp_, sequence_number))
    return;

  /* 对比 当前包 与 最新收到的包 的序列号与时间戳,更新每个包的样本数:samples_per_packet_ */
  UpdateSamplesPerPacket(sequence_number, timestamp);

  /* 更新丢包集合:nack_list_ */
  UpdateList(sequence_number);

  /* 更新最新收包的序列号与时间戳 */
  sequence_num_last_received_rtp_ = sequence_number;
  timestamp_last_received_rtp_ = timestamp;

  /* 丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制 */
  LimitNackListSize();
}

二、更新丢包集合:nack_list_: 

void NackTracker::UpdateList(uint16_t sequence_number_current_received_rtp) 
{
  /*
  更新 nack_list_ 中部分包的丢包状态:is_missing = true,
  这些包我们之前认为后续再考虑,现在需要把它们当做丢包
  */
  ChangeFromLateToMissing(sequence_number_current_received_rtp);

  /*
  如果当前收到的包 比 sequence_num_last_received_rtp_ + 1 新,
  即:最近收到的新包 与 当前收到的包 不连续
  */
  if (IsNewerSequenceNumber(sequence_number_current_received_rtp,
                            sequence_num_last_received_rtp_ + 1))
    AddToList(sequence_number_current_received_rtp);
}

三、更新 nack_list_ 中部分包的丢包状态:is_missing = true,这些包我们之前认为后续再考虑,现在需要把它们当做丢包: 

void NackTracker::ChangeFromLateToMissing(uint16_t sequence_number_current_received_rtp) 
{
  /*
  nack_threshold_packets_ = 2,表示:
  当序列号为 N 的包到达时,序列号在 ( , N - |nack_threshold_packets_| ) 区间、所有未到达的包,都会被认为丢失
  而对于序列号在 [ N - |nack_threshold_packets_| , N-1 ] 区间、所有未达到的包,后面再考虑
  */

  /* 查找 大于等于 N - |nack_threshold_packets_| 的第一个元素 */
  NackList::const_iterator lower_bound =
      nack_list_.lower_bound(static_cast<uint16_t>(
          sequence_number_current_received_rtp - nack_threshold_packets_));

  for (NackList::iterator it = nack_list_.begin(); it != lower_bound; ++it)
    it->second.is_missing = true;
}

四、最新收到的包 与 当前包 之间的所有包都要被添加到 nack_list_,其中那些太旧的包会被直接认为丢包,剩下的包当后续包达到时,会在上一步中再明确是否为丢包: 

void NackTracker::AddToList(uint16_t sequence_number_current_received_rtp) 
{
  assert(!any_rtp_decoded_ ||
         IsNewerSequenceNumber(sequence_number_current_received_rtp,
                               sequence_num_last_decoded_rtp_));

  /*
  序列号在 upper_bound_missing 之前的包(不包括 upper_bound_missing),都认为是丢包,添加到 nack_list_
  upper_bound_missing 以及之后的包,一并添加到 nack_list_,但后面再考虑
  */
  uint16_t upper_bound_missing =
      sequence_number_current_received_rtp - nack_threshold_packets_;

  /*
  最近收到的新包 到 当前收到的包 区间内的包,都可能是丢包,都会被添加到 nack_list_
  但只有在 upper_bound_missing 之前的包,才直接认为丢包
  */
  for (uint16_t n = sequence_num_last_received_rtp_ + 1;
       IsNewerSequenceNumber(sequence_number_current_received_rtp, n); ++n) 
  {
    bool is_missing = IsNewerSequenceNumber(upper_bound_missing, n);
    uint32_t timestamp = EstimateTimestamp(n);
    NackElement nack_element(TimeToPlay(timestamp), timestamp, is_missing);
    nack_list_.insert(nack_list_.end(), std::make_pair(n, nack_element));
  }
}

五、丢弃掉集合 nack_list_ 中不能重传的包信息,同时保证集合容量不超过限制:

void NackTracker::LimitNackListSize() 
{
  /* max_nack_list_size_ = 500 */
  uint16_t limit = sequence_num_last_received_rtp_ - static_cast<uint16_t>(max_nack_list_size_) - 1;

  /* 删除 [ start, end ) 区间内的节点 */
  nack_list_.erase(nack_list_.begin(), nack_list_.upper_bound(limit));
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

相关文章:

  • Nginx-HTTPS 配置
  • 2022-09-07 mysql/stonedb-多线程遍历元组问题分析
  • 单调栈题目:找出最具竞争力的子序列
  • Python运算符,数字,字符串
  • JSP教学评估管理系统myeclipse开发mysql数据库bs框架java编程web网页结构
  • 【vue3】04. 跟着官网学习vue3
  • xv6源码阅读——xv6的启动,进程初识
  • 金仓数据库KingbaseES客户端应用参考手册--13. sys_isready
  • 前端工程师面试题总结
  • 从“1L 小钢炮”到 “PC界变形金刚”——Tiny助力企业数智转型的十年进化之路
  • 【数据结构:1.绪论】
  • 计算机组成原理第二章----数据信息的表示 详解版
  • 网络安全-防火墙安全加固
  • 中秋节祝福程序源代码分享:土地分类数据阈值筛选和重投影分类
  • Java新手小白入门篇 API - 多线程
  • 【笔记】你不知道的JS读书笔记——Promise
  • Angular6错误 Service: No provider for Renderer2
  • GitUp, 你不可错过的秀外慧中的git工具
  • idea + plantuml 画流程图
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • Laravel核心解读--Facades
  • MySQL-事务管理(基础)
  • Object.assign方法不能实现深复制
  • Vue 2.3、2.4 知识点小结
  • 创建一种深思熟虑的文化
  • 构造函数(constructor)与原型链(prototype)关系
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 七牛云假注销小指南
  • 前端js -- this指向总结。
  • 如何在GitHub上创建个人博客
  • 深度学习入门:10门免费线上课程推荐
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 阿里云ACE认证之理解CDN技术
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #HarmonyOS:Web组件的使用
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (zhuan) 一些RL的文献(及笔记)
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (多级缓存)缓存同步
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (四)库存超卖案例实战——优化redis分布式锁
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一一四)第九章编程练习
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)四层和七层负载均衡的区别
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes