基础知识
音频的 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 数据包处理过程中,不同于一般采集、编码及发送流程的地方。