WebRTC 的音频弱网对抗之 NACK
基础知识
音频的 NACK 机制在 WebRTC 中默认是关闭的.
rtcp feedbacknack开启就可以了
WebRTC 的音频数据传输中,尽管对低延时有着很高的要求,但也实现了 NACK,以用于一些音质比延迟更重要的场景。
重传数据包的记录功能, 记录那些需要重传
nack_tracker.cc
在 WebRTC 里,NetEQ 的 webrtc::NackTracker 用来跟踪和记录可能需要请求重传的数据包。
typedef std::map<uint16_t, NackElement, NackListCompare> NackList;
//NackList记录了丢包的序列号和每个包的播放时间和timestamp
struct NackElement {
NackElement()
int64_t time_to_play_ms; //此数据包解码的估计剩余时间(毫秒)
uint32_t estimated_timestamp;//关于丢失数据包的时间戳的猜测
bool is_missing; //判定是丢包还是,延迟了
};
小于最新收到的, 才可能是丢包
更新最后接收到的包NackTracker::UpdateLastReceivedPacket
- 从 NACK 列表中移除对应数据包序列号的记录
- 如果新收到的数据包的序列号比收到的最近的数据包的序列号小, 收到了丢包; 否则更新NACK 列表
- 计算收到的最近的数据包到这次收到的数据包之间还没有收到的数据包的个数,并据此计算丢包率;
- 更新NACK 列表
- 更新记录收到的最近的数据包的序列号和 timestamp;
- 执行 NACK 列表大小限制。
更新最近一次解码成功, NackTracker::UpdateLastDecodedPacket
接收端寻找时机发送 NACK 消息
WebRTC 在每次收到音频数据包,并把它送进 NetEQ 之后,就会立即去获取 NACK 列表
ChannelReceive::OnReceivedPayloadData
webrtc::voe::ChannelReceive::OnReceivedPayloadData()
NackTracker::GetNackList
ModuleRtpRtcpImpl::SendNack
//5 + RTT * 1.5. 来区别NACK下次时间
接收端开启音频 NACK
webrtc::internal::AudioReceiveStream 对象创建时,有个配置项 config.rtp.nack.rtp_history_ms 用于控制是否开启 NACK。
config.rtp.nack.rtp_history_ms 的值大于 0 时,开启 NACK,否则不开启。
config.rtp.nack.rtp_history_ms 的值根据 WebRtcVoiceEngine 的 recv_nack_enabled_ 配置计算得到,而这个配置则来自于 codec spec 的 nack_enabled,codec spec 的配置来自于接收和发送的两方协调的 codec 配置的 SDP 消息
FeedbackParam(kRtcpFbParamNack, kParamValueEmpty));
发送端缓存
webrtc::RtpPacketHistory //发送的时候存储在该对象
发送端接收并处理 RTCP NACK 反馈包
webrtc::RTCPReceiver::IncomingPacket
webrtc::ModuleRtpRtcpImpl2::IncomingRtcpPacket
webrtc::voe::ChannelSend::ReceivedRTCPPacket(unsigned char const*, unsigned long)
webrtc::internal::AudioSendStream::DeliverRtcp(unsigned char const*, unsigned long)
webrtc::internal::Call::DeliverRtcp(webrtc::MediaType, rtc::CopyOnWriteBuffer):
蓝色箭头和红色方框中的这些逻辑是 NACK 数据包处理过程中,不同于一般采集、编码及发送流程的地方。
最新文章
- MySql binlog恢复数据
- java中static 和 final 的一些使用规则
- delphi 获取图片某一像素的颜色值
- swift学习笔记之-错误处理
- BZOJ3498 : PA2009 Cakes
- Java Concurrency - Callable &; Future
- ubuntu下的Samba配置:使每个用户可以用自己的用户名和密码登录自己的home目录
- JVM菜鸟进阶高手之路五
- jQuery使用serialize(),serializeArray()方法取得表单数据+字符串和对象类型两种表单提交的方法
- ubuntu下ftp服务
- 关于H5的一些杂思细想(一)
- PHP零基础入门
- less 基础+ flex
- python爬虫scrapy的LinkExtractor
- CUDA开发
- BZOJ4556 [Tjoi2016&;Heoi2016]字符串 SA ST表 二分答案 主席树
- php防止刷流量攻击
- swift - 快速代码块 - 创建 tableview等一些控件 基本属性
- 用户态处理arp、ndisc neighbour solication 报文
- offsetTop 实现滚动条内内容定位
热门文章
- CF884E - Binary Matrix
- vue 组件通信方式 ,父子、隔代、兄弟 三类通信,六种方法
- 深入解读.NET MAUI音乐播放器项目(三):界面交互
- Avalonia 实现平滑拖动指定控件
- JUC学习笔记
- android 投屏
- I2C接口(续一)
- golang 中使用mysql报错:“ scannable dest type slice with >;1 columns (4) in result”
- go项目,出现too many open files
- 题解[CF674D]Bearish_Fanpages