webRTC 每收到一个包时,都会调用 UpdateLastReceivedPacket() 进行丢包判断,同时也会调用 SendNACK() 发送重传请求。重传原理:
-
从集合 nack_list_ 中筛选出所有满足重传条件的序列号集合 nack_list,重传条件:已被明确为丢包(即:is_missing = true)并且 包的播放时间 > 当前需要时间;
-
判断本次是否需要请求重传整个集合 nack_list:当前时间 - 上次请求重传所有的时间 > 5 + RTT * 1.5。如果是的,跳转到第四步;
-
若本次不需要请求重传整个集合,则只请求集合 nack_list 中新的 seq,之前已经请求过的,不再请求;
-
每次最多请求 seq 个数:kRtcpMaxNackFields = 253(超过限制的,下次再请求),同时记录本次请求的最新 seq;
-
调用 SendRTCP(),一次性请求多个包。
一、获取需要重传的包序列号:
std::vector<uint16_t> NackTracker::GetNackList(int64_t round_trip_time_ms) const
{
RTC_DCHECK_GE(round_trip_time_ms, 0);
std::vector<uint16_t> sequence_numbers;
for (NackList::const_iterator it = nack_list_.begin(); it != nack_list_.end(); ++it)
{
/* 包播放的时间 大于 当前需要的时间 */
if (it->second.is_missing && it->second.time_to_play_ms > round_trip_time_ms)
sequence_numbers.push_back(it->first);
}
return sequence_numbers;
}
二、请求重传:
/*
nack_list:数组指针,待重传序列号集合
size:数组长度
*/
int32_t ModuleRtpRtcpImpl::SendNACK(const uint16_t* nack_list, const uint16_t size)
{
for (int i = 0; i < size; ++i)
{
receive_loss_stats_.AddLostPacket(nack_list[i]);
}
uint16_t nack_length = size;
uint16_t start_id = 0;
int64_t now = clock_->TimeInMilliseconds();
/* 判断是否到了发送整个重传列表的时间 */
if (TimeToSendFullNackList(now))
{
/* 记录本次重传整个列表的时间 */
nack_last_time_sent_full_ = now;
nack_last_time_sent_full_prev_ = now;
}
else
{
/*
若待重传集合 nack_list 中的包都已经请求重传,即:
最新重传的序列号 == 待重传集合 nack_list 的最后一个节点
则直接返回,不需要再次请求重传
*/
if (nack_last_seq_number_sent_ == nack_list[size - 1])
{
return 0;
}
// 找到上一次重传的位置,从下一个节点开始请求重传
for (int i = 0; i < size; ++i)
{
if (nack_last_seq_number_sent_ == nack_list[i])
{
/* 本次开始重传的索引号 */
start_id = i + 1;
break;
}
}
/* 本次重传的长度 */
nack_length = size - start_id;
}
// Our RTCP NACK implementation is limited to kRtcpMaxNackFields sequence
// numbers per RTCP packet.
/* 一次最多请求重传个数:kRtcpMaxNackFields = 253 */
if (nack_length > kRtcpMaxNackFields)
{
nack_length = kRtcpMaxNackFields;
}
/* 记录 本次重传的最新序列号 */
nack_last_seq_number_sent_ = nack_list[start_id + nack_length - 1];
return rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpNack, nack_length, &nack_list[start_id]);
}
三、判断本次是否需要请求重传整个集合:
bool ModuleRtpRtcpImpl::TimeToSendFullNackList(int64_t now) const
{
// Use RTT from RtcpRttStats class if provided.
int64_t rtt = rtt_ms();
if (rtt == 0)
{
rtcp_receiver_.RTT(rtcp_receiver_.RemoteSSRC(), NULL, &rtt, NULL, NULL);
}
const int64_t kStartUpRttMs = 100;
int64_t wait_time = 5 + ((rtt * 3) >> 1); // 5 + RTT * 1.5.
if (rtt == 0)
{
wait_time = kStartUpRttMs;
}
// Send a full NACK list once within every |wait_time|.
if (rtt_stats_)
{
return now - nack_last_time_sent_full_ > wait_time;
}
return now - nack_last_time_sent_full_prev_ > wait_time;
}
四、调用 SendRTCP(),一次性请求多个包:
// Report stats.
NACKStringBuilder stringBuilder;
for (int idx = 0; idx < ctx.nack_size_; ++idx)
{
stringBuilder.PushNACK(ctx.nack_list_[idx]);
nack_stats_.ReportRequest(ctx.nack_list_[idx]);
}