WebRTC研究:audio 如何發送需要重傳的數據

當 WebRTC 收到一個 RTP 包重傳請求(一個請求中可能包含多個要重傳的 seq)時:

  • 首先,判斷是否有足夠的帶寬來發送需要重傳的包,否的話,則直接拒絕本次重傳。判斷依據:最近 1s 內且最近 60次 (即默認以最近 1s 爲參考對象,若最近 1s 內有 60 或以上次數的重傳,則以最近 60 次爲參考對象)發送的總位數 是否小於 目標比特率(target_bitrate)* 時間長度;

  • 遍歷需要重傳的 seq,並根據 seq 檢索對應的數據包在緩存 stored_packets_ 中的存儲位置;

  • 判斷包能否被重傳,判斷依據:若對應數據包是第一次被請求重傳,則始終同意,否則只有 當前時間 距離 上一次重傳該數據包的時間 超過 min_elapsed_time_ms = avg_rtt(平均 rrt)+ 5 ms,才允許被再次重傳;

  • 一個重傳請求中可能包含多個要重傳的 seq,而每重傳完一個 seq 對應的數據包,會累加針對本次重傳請求所發送的字節總數 bytes_re_sent 並判斷是否超過本次請求最多可重傳的字節數,如果是的話,則直接忽視本次請求中剩下的待重傳 seq,判斷依據:bytes_re_sent > (static_cast<size_t>(target_bitrate / 1000) * avg_rtt) >> 3,即以 target_bitrate (每秒傳送的 bit 數)爲標準在 avg_rtt 時間內最多能重傳的字節數。

  • 當前重傳請求處理完畢後,會 將本次重傳的總字節數當前時間 分別存入集合 nack_byte_count_ 和 nack_byte_count_times_,供下一次響應重傳請求在第一步中使用。

一、判斷是否有足夠的帶寬來發送需要重傳的包:

bool RTPSender::ProcessNACKBitRate(uint32_t now) 
{
  uint32_t num = 0;
  size_t byte_count = 0;
  const uint32_t kAvgIntervalMs = 1000;

  /*
  獲取目標比特率,即每秒傳送的比特(bit)數
  */
  uint32_t target_bitrate = GetTargetBitrate();

  rtc::CritScope lock(&send_critsect_);

  if (target_bitrate == 0) 
  {
    return true; //沒有比特率的限制
  }

  /*
  計算最近 1s 內重傳的總字節數

  NACK_BYTECOUNT_SIZE = 60

  nack_byte_count_times_:存儲最近 NACK_BYTECOUNT_SIZE 次重傳的時間,時間越近的排在越前面
  nack_byte_count_:存儲最近 NACK_BYTECOUNT_SIZE 次重傳的字節數,時間越近的排在越前面,
  若某次重傳N個包,則這裏的字節數指的是N個包的總字節數
  */
  for (num = 0; num < NACK_BYTECOUNT_SIZE; ++num) 
  {
    if ((now - nack_byte_count_times_[num]) > kAvgIntervalMs) 
	{
      // Don't use data older than 1sec.
      break;
    } 
	else 
	{
      byte_count += nack_byte_count_[num];
    }
  }

  // 時間間隔默認爲 1000ms
  uint32_t time_interval = kAvgIntervalMs;

  /*
  若最近 1000ms 內,收到了 NACK_BYTECOUNT_SIZE 及以上次重傳請求
  則時間間隔以 當前時間 到 最近倒數第 NACK_BYTECOUNT_SIZE 次重傳的時間爲準
  */
  if (num == NACK_BYTECOUNT_SIZE) 
  {
    if (nack_byte_count_times_[num - 1] <= now) 
	{
      time_interval = now - nack_byte_count_times_[num - 1];
    }
  }

  /*
  計算最近一段時間內,重傳的位數是否超過限制
  */
  return (byte_count * 8) < (target_bitrate / 1000 * time_interval);
}

二、根據 seq 檢索對應的數據包在緩存 stored_packets_ 中的存儲位置

bool RTPPacketHistory::FindSeqNum(uint16_t sequence_number, int32_t* index) const 
{
  /*
  首先根據 prev_index_ 來連續獲取 seq
  這裏根據 prev_index > 0,處理了 stored_packets_ 擴容問題

  prev_index_ 表示下一個 rtp 包存入 stored_packets_ 的索引
  stored_packets_ 默認長度爲 600,當往其中存入 rtp 包時,若發現 prev_index_ 位置上有包並且包還沒有發送,
  則以當前容量的1.5倍進行擴容
  stored_packets_ 最大可擴容到 9600
  */

  /* 找到上一次存入 stored_packets_ 的節點 temp_sequence_number */
  uint16_t temp_sequence_number = 0;
  if (prev_index_ > 0) 
  {
    *index = prev_index_ - 1;
    temp_sequence_number = stored_packets_[*index].sequence_number;
  } 
  else 
  {
    *index = stored_packets_.size() - 1;
    temp_sequence_number = stored_packets_[*index].sequence_number;  // wrap
  }

  /*
  首先按連續方式處理
  prev_index_ - 1:上一個節點存入 stored_packets_ 的索引位置
  */
  int32_t idx = (prev_index_ - 1) - (temp_sequence_number - sequence_number);
  if (idx >= 0 && idx < static_cast<int>(stored_packets_.size())) 
  {
    *index = idx;
    temp_sequence_number = stored_packets_[*index].sequence_number;
  }

  if (temp_sequence_number != sequence_number) 
  {
	  /* stored_packets_ 擴容可能導致節點的不連續,此處一次遍歷所有節點 */
    for (uint16_t m = 0; m < stored_packets_.size(); m++) 
	{
      if (stored_packets_[m].sequence_number == sequence_number) 
	  {
        *index = m;
        temp_sequence_number = stored_packets_[*index].sequence_number;
        break;
      }
    }
  }

  if (temp_sequence_number == sequence_number) 
  {
    // We found a match.
    return true;
  }
  return false;
}

三、ReSendPacket() 中會調用 GetPacketAndSetSendTime() 檢索數據包,並判斷包能否被重傳:

bool RTPPacketHistory::GetPacketAndSetSendTime(uint16_t sequence_number,
                                               int64_t min_elapsed_time_ms,
                                               bool retransmit,
                                               uint8_t* packet,
                                               size_t* packet_length,
                                               int64_t* stored_time_ms) 
{
  rtc::CritScope cs(&critsect_);
  RTC_CHECK_GE(*packet_length, static_cast<size_t>(IP_PACKET_SIZE));
  if (!store_)
    return false;

  /* 從緩存 stored_packets_ 中找到包的索引位置 */
  int32_t index = 0;
  bool found = FindSeqNum(sequence_number, &index);
  if (!found) 
  {
    LOG(LS_WARNING) << "No match for getting seqNum " << sequence_number;
    return false;
  }

  size_t length = stored_packets_[index].length;
  assert(length <= IP_PACKET_SIZE);
  if (length == 0) 
  {
    LOG(LS_WARNING) << "No match for getting seqNum " << sequence_number
                    << ", len " << length;
    return false;
  }

  /*
  計算距離該包上一次重傳的時間
  min_elapsed_time_ms:最小再次重傳時間,= avg_rtt(平均 rrt)+ 5 ms
  如果是第一次請求重傳該包,則會始終重傳,後續只有超過時間間隔後,纔會再次重傳
  */
  int64_t now = clock_->TimeInMilliseconds();
  if (min_elapsed_time_ms > 0 && retransmit &&
      stored_packets_[index].has_been_retransmitted &&
      ((now - stored_packets_[index].send_time) < min_elapsed_time_ms)) 
  {
    return false;
  }

  if (retransmit) 
  {
	/* 包不能被重傳 */
    if (stored_packets_[index].storage_type == kDontRetransmit) 
	{
      return false;
    }

	/* 將包標記爲已重傳 */
    stored_packets_[index].has_been_retransmitted = true;
  }

  /* 記錄重傳包本次發送的時間 */
  stored_packets_[index].send_time = clock_->TimeInMilliseconds();

  /* 拷貝包內容、長度、時間 */
  GetPacket(index, packet, packet_length, stored_time_ms);
  return true;
}

四、每重傳完一個數據包後,會累加針對本次重傳請求發送的字節總數並判斷是否超過本次請求最多可重傳的字節數:

void RTPSender::OnReceivedNACK(const std::list<uint16_t>& nack_sequence_numbers, int64_t avg_rtt) 
{
  TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
               "RTPSender::OnReceivedNACK", "num_seqnum",
               nack_sequence_numbers.size(), "avg_rtt", avg_rtt);
  const int64_t now = clock_->TimeInMilliseconds();
  uint32_t bytes_re_sent = 0;
  uint32_t target_bitrate = GetTargetBitrate();

  // 判斷是否有足夠的帶寬來發送 NACK
  if (!ProcessNACKBitRate(now)) 
  {
    LOG(LS_INFO) << "NACK bitrate reached. Skip sending NACK response. Target "
                 << target_bitrate;
    return;
  }

  for (std::list<uint16_t>::const_iterator it = nack_sequence_numbers.begin();
      it != nack_sequence_numbers.end(); ++it) 
  {
    const int32_t bytes_sent = ReSendPacket(*it, 5 + avg_rtt);
   
	if (bytes_sent > 0) 
	{
      bytes_re_sent += bytes_sent; //累加包的長度
    } 
	else if (bytes_sent == 0) 
	{
      // The packet has previously been resent.
      // Try resending next packet in the list.
      continue;
    } 
	else 
	{
	  /* 若發送出錯,則剩下的 seq 不重傳了 */
      LOG(LS_WARNING) << "Failed resending RTP packet " << *it
                      << ", Discard rest of packets";
      break;
    }

	// 每發送一個包之後,估算後續帶寬:Delay bandwidth
    if (target_bitrate != 0 && avg_rtt) 
	{
      // kbits/s * ms = bits => bits/8 = bytes
	  // 計算每次最多能重傳的字節數:rrt 時間內可發送的字節數
      size_t target_bytes = (static_cast<size_t>(target_bitrate / 1000) * avg_rtt) >> 3;

      if (bytes_re_sent > target_bytes) 
	  {
        break;  // 終止重傳,直接忽視剩下的 seq
      }
    }
  }

  // 更新本次發送的字節數
  if (bytes_re_sent > 0) 
  {
    UpdateNACKBitRate(bytes_re_sent, now);
  }
}

五、更新本次重傳的字節數和重傳時間:

void RTPSender::UpdateNACKBitRate(uint32_t bytes, int64_t now) 
{
  rtc::CritScope lock(&send_critsect_);
  if (bytes == 0)
    return;

  /* 累計發送的字節數與次數 */
  nack_bitrate_.Update(bytes);
 
  /* 將 0 ~ NACK_BYTECOUNT_SIZE - 2 區間的節點,往後挪一個位置 */
  for (int i = NACK_BYTECOUNT_SIZE - 2; i >= 0; i--) 
  {
    nack_byte_count_[i + 1] = nack_byte_count_[i];
    nack_byte_count_times_[i + 1] = nack_byte_count_times_[i];
  }

  /* 將本次重傳時間與字節總數 放到數組第一個位置 */
  nack_byte_count_[0] = bytes;
  nack_byte_count_times_[0] = now;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章