WebRTC研究:丢包与抖动

1、累计收包、重传包等信息,并计算抖动:

void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,
                                            size_t packet_length,
                                            bool retransmitted) 
{
  rtc::CritScope cs(&stream_lock_);

  /*
  判断包是否有序
  判断标准:当前收包seq位于区间:[received_seq_max_ - max_reordering_threshold, received_seq_max_] 之外
  max_reordering_threshold,默认为50
  */
  bool in_order = InOrderPacketInternal(header.sequenceNumber);
  ssrc_ = header.ssrc;

  // 累计所收到的包数、字节数
  incoming_bitrate_.Update(packet_length);

  // 累计所收到的包数、header/padding/payload字节数(packet_length是包总长度)
  receive_counters_.transmitted.AddPacket(packet_length, header);

  // 累计所收到的无序重传包数、字节数
  if (!in_order && retransmitted) 
  {
    receive_counters_.retransmitted.AddPacket(packet_length, header);
  }

  // 记录所收到的第一个包的seq、当前时间
  if (receive_counters_.transmitted.packets == 1) 
  {
    received_seq_first_ = header.sequenceNumber;
    receive_counters_.first_packet_time_ms = clock_->TimeInMilliseconds();
  }

  // 收到有序包
  // Count only the new packets received. That is, if packets 1, 2, 3, 5, 4, 6
  // are received, 4 will be ignored.
  if (in_order) 
  {
    // Current time in samples.
    NtpTime receive_time(*clock_);

    // Wrong if we use RetransmitOfOldPacket.

	// 回绕
    if (receive_counters_.transmitted.packets > 1 && received_seq_max_ > header.sequenceNumber) 
	{
      // Wrap around detected.
      received_seq_wraps_++;
    }

    // 记录最新 seq
    received_seq_max_ = header.sequenceNumber;

    // 计算最新抖动
    if (header.timestamp != last_received_timestamp_ &&
        (receive_counters_.transmitted.packets - receive_counters_.retransmitted.packets) > 1) 
	{
      UpdateJitter(header, receive_time);
    }

	// 记录最近 所收到的最新包头携带的时间戳、收到最新包时的本地NTP时间与当前时间
    last_received_timestamp_ = header.timestamp;
    last_receive_time_ntp_ = receive_time;
    last_receive_time_ms_ = clock_->TimeInMilliseconds();
  }

  size_t packet_oh = header.headerLength + header.paddingLength;

  // Our measured overhead. Filter from RFC 5104 4.2.1.2:
  // avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH,
  received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
}

判断包是否有序:

bool StreamStatisticianImpl::InOrderPacketInternal(uint16_t sequence_number) const 
{
  // First packet is always in order.
  if (last_receive_time_ms_ == 0)
    return true;

  if (IsNewerSequenceNumber(sequence_number, received_seq_max_)) 
  {
    return true;
  } 
  else 
  {
    // If we have a restart of the remote side this packet is still in order.
    return !IsNewerSequenceNumber(sequence_number, received_seq_max_ - max_reordering_threshold_);
  }
}

计算抖动:

void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header, NtpTime receive_time) 
{
  // 将NTP时间戳:receive_time(本地当前NTP时间)转变成RTP时间戳
  uint32_t receive_time_rtp = NtpToRtp(receive_time, header.payload_type_frequency);
  // 将NTP时间戳:last_receive_time_ntp_(最近收到最新包时的本地NTP时间)转变成RTP时间戳
  uint32_t last_receive_time_rtp = NtpToRtp(last_receive_time_ntp_, header.payload_type_frequency);

  // 第一种计算抖动的方法:jitter_q4_
  /*
  计算(最近两次收到最新包时的本地NTP时间差)与(最近两次收到最新包头携带的时间戳)之差
  即:计算抖动值,理想情况下,抖动值为0
  */
  int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
      (header.timestamp - last_received_timestamp_);

  time_diff_samples = std::abs(time_diff_samples);

  // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
  // If this happens, don't update jitter value. Use 5 secs video frequency
  // as the threshold.

  // 更新 jitter_q4_
  if (time_diff_samples < 450000) 
  {
    // Note we calculate in Q4 to avoid using float.
    int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
    jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
  }

  // 第二种计算抖动的方法:jitter_q4_transmission_time_offset_
  // transmission_time_offset:一段时间间隔,代表属于同一帧的RTP的实际发送时间距离帧的capture time的偏移量
  // 即计算(最近两次收到最新包时的本地NTP时间差)与(最近两次收到最新包实际发送到网络的时间戳)之差
  int32_t time_diff_samples_ext = (receive_time_rtp - last_receive_time_rtp) -
    ((header.timestamp + header.extension.transmissionTimeOffset) -
     (last_received_timestamp_ + last_received_transmission_time_offset_));

  time_diff_samples_ext = std::abs(time_diff_samples_ext);

  // 计算 jitter_q4_transmission_time_offset_
  if (time_diff_samples_ext < 450000) 
  {
    int32_t jitter_diffQ4TransmissionTimeOffset = (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
    jitter_q4_transmission_time_offset_ += ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
  }
}

二、丢包统计

每发送一次RTP包,就会进行一次丢包统计(即丢包统计的周期:5000ms)。

1、调用流程

void ModuleRtpRtcpImpl::Process()

int32_t RTCPSender::SendRTCP(…)

int32_t RTCPSender::SendCompoundRTCP(…)

void RTCPSender::PrepareReport(…)

void RTCPSender::PrepareReport(const std::set<RTCPPacketType>& packetTypes,
                               const FeedbackState& feedback_state) 
{
  ...
  ...
  ...
   
   if (receive_statistics_) 
   {
      StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians();

      RTC_DCHECK(report_blocks_.empty());
      for (auto& it : statisticians) 
	  {
        AddReportBlock(feedback_state, it.first, it.second);
      }
  }
}
bool RTCPSender::AddReportBlock(const FeedbackState& feedback_state,
                                uint32_t ssrc,
                                StreamStatistician* statistician) 
{
  RtcpStatistics stats;
  if (!statistician->GetStatistics(&stats, true))
    return false;
    
  ...
  ...
  ...
}
bool StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics, bool reset) 
{
  {
    rtc::CritScope cs(&stream_lock_);
	/*
	received_seq_first_:所收到的第一个包的seq
	payload_bytes:所收到的包中payload部分字节总数
	*/
    if (received_seq_first_ == 0 && receive_counters_.transmitted.payload_bytes == 0) 
	{
      // We have not received anything.
      return false;
    }

	/* 不重新进行丢包统计 */
    if (!reset) 
	{
	  /* 收到的包总数 与 重传报数 之差 */
      if (last_report_inorder_packets_ == 0) 
	  {
        // No report.
        return false;
      }

      // 直接使用上一次的report
      *statistics = last_reported_statistics_;
      return true;
    }

	// 丢包统计
    *statistics = CalculateRtcpStatistics();
  }

  NotifyRtcpCallback();

  return true;
}
2、丢包统计

丢包率 = 255 * 丢包数 / 预期收到的包总数

  • 丢包数:期望收到的包总数 - 实际收到的包总数

  • 期望收到的包总数 = 当前收到的最新包seq - 截止到上一次report时收到的最新包seq

  • 实际收到的包总数 = 正常包总数 + 重传包总数

  • 正常包总数(不包括重传包)= 截止到目前收到的包总数与重传包总数之差 - 截止到上一次report时收到的包总数与重传包总数之差

  • 重传包数 = 截止到目前收到的重传包总数 - 截止到上一次report时收到的重传包总数

RtcpStatistics StreamStatisticianImpl::CalculateRtcpStatistics() 
{
  RtcpStatistics stats;

  // 第一次进行统计
  if (last_report_inorder_packets_ == 0) 
  {
	// 设置截止到上一次report时,收到的最新包
    // First time we send a report.
    last_report_seq_max_ = received_seq_first_ - 1;
  }

  /*
  计算从上一次report到当前这段时间内,预期收到的包总数
  期望收到的包总数 = 当前收到的最新包seq - 截止到上一次report时收到的最新包seq
  */
  uint16_t exp_since_last = (received_seq_max_ - last_report_seq_max_);

  // 遇到seq回绕
  if (last_report_seq_max_ > received_seq_max_) 
  {
    exp_since_last = 0;
  }

  /*
  计算从上一次report到当前这段时间内,所收到的包数(不包括重传包)
  包数(不包括重传包)= 截止到目前收到的包总数与重传包总数之差 - 截止到上一次report时收到的包总数与重传包总数之差
  */
  uint32_t rec_since_last =
      (receive_counters_.transmitted.packets -
       receive_counters_.retransmitted.packets) - last_report_inorder_packets_;

  // With NACK we don't know the expected retransmissions during the last
  // second. We know how many "old" packets we have received. We just count
  // the number of old received to estimate the loss, but it still does not
  // guarantee an exact number since we run this based on time triggered by
  // sending of an RTP packet. This should have a minimum effect.

  // With NACK we don't count old packets as received since they are
  // re-transmitted. We use RTT to decide if a packet is re-ordered or
  // re-transmitted.

  /*
  计算从上一次report到当前这段时间内,收到的重传包总数
  重传包数 = 截止到目前收到的重传包总数 - 截止到上一次report时收到的重传包总数
  */
  uint32_t retransmitted_packets = receive_counters_.retransmitted.packets - last_report_old_packets_;

  /*
  计算从上一次report到当前这段时间内,实际收到的包总数
  实际收到的包总数 = 正常包总数 + 重传包总数
  */
  rec_since_last += retransmitted_packets;

  // 计算丢包数:期望收到的包总数 - 实际收到的包总数
  int32_t missing = 0;
  if (exp_since_last > rec_since_last) 
  {
    missing = (exp_since_last - rec_since_last);
  }

  /*
  计算丢包率
  丢包率 = 255 * 丢包数 / 预期收到的包总数
  255表示100%丢包
  */
  uint8_t local_fraction_lost = 0;
  if (exp_since_last) 
  {
    local_fraction_lost = static_cast<uint8_t>(255 * missing / exp_since_last);
  }
  stats.fraction_lost = local_fraction_lost;

  // 累计到目前为止的丢包总数
  cumulative_loss_ += missing;
  stats.cumulative_lost = cumulative_loss_;

  /*
  扩展最近收到的最新包seq
  received_seq_wraps_:发生seq回绕的次数
  */
  stats.extended_max_sequence_number = (received_seq_wraps_ << 16) + received_seq_max_;

  // 抖动
  // Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
  stats.jitter = jitter_q4_ >> 4;

  // Store this report.
  last_reported_statistics_ = stats;

  // 截止到目前为止,收到的包总数与重传包总数之差
  last_report_inorder_packets_ = receive_counters_.transmitted.packets - receive_counters_.retransmitted.packets;

  // 记录本次report时的重传包总数、最新包seq
  last_report_old_packets_ = receive_counters_.retransmitted.packets;
  last_report_seq_max_ = received_seq_max_;

  return stats;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值