WebRTC研究:audio 重传

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]);
  }
发布了112 篇原创文章 · 获赞 22 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/u010601662/article/details/105146131