WebRTC研究:rrt 时间计算 之 发送 SR / RR 包

一、ModuleRtpRtcpImpl::Process() 会定时处理 SR/RR 包发送、bitrate、rrt 等事宜,处理时间间隔为 5 ms:

int64_t ModuleRtpRtcpImpl::TimeUntilNextProcess() 
{
   /*
   last_process_time_:上次调用 Process() 的开始时间
   
   理论上是,在本次调用 Process() 之后,需等待 5ms,然后再次执行 Process() 
   但是由于调用 Process() 本身存在耗时,因此实际等待时间需要减掉这部分耗时
  */
  const int64_t now = clock_->TimeInMilliseconds();
  const int64_t kRtpRtcpMaxIdleTimeProcessMs = 5;

  return kRtpRtcpMaxIdleTimeProcessMs - (now - last_process_time_);
}

二、发送 SR / RR 包

对于 audio,发送 SR / RR 包的时间间隔为 [1/2 * 5000, 3/2 * 5000] ms 之间的一个随机数:

void ModuleRtpRtcpImpl::Process() 
{
  ...
  ...
  ...
  
  /*
  判断发送 RTP 包的时间是否到达
  */
  if (rtcp_sender_.TimeToSendRTCPReport()) 
  {
	/*
	获取 最近收到的 SR 包中所携带的 NTP 时间戳、收到 SR 包时本地时间戳等信息
	*/
    RTCPSender::FeedbackState state = GetFeedbackState();
    
    // Prevent sending streams to send SR before any media has been sent.
    if (!rtcp_sender_.Sending() || state.packets_sent > 0)
      rtcp_sender_.SendRTCP(state, kRtcpReport); /* 发送RTP包 并计算下一次发送 RTP包的时间 */
  }
  
  ...
  ...
  ...
}

三、获取最近收到的 SR 包中所携带的 NTP 时间戳、收到 SR 包时本地时间戳等信息:

RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() 
{
  ...
  ...
  ...
  
   /*
  remote_sr:最近收到的 SR 包中所携带的 NTP 时间戳的中间32位
  last_rr_ntp_secs / last_rr_ntp_frac:最近收到 SR 包时的当前时间戳
  */
  LastReceivedNTP(&state.last_rr_ntp_secs,
                  &state.last_rr_ntp_frac,
                  &state.remote_sr);
  ...
  ...
  ...
}
bool ModuleRtpRtcpImpl::LastReceivedNTP(
    uint32_t* rtcp_arrival_time_secs,  // When we got the last report.
    uint32_t* rtcp_arrival_time_frac,
    uint32_t* remote_sr) const 
{
  // Remote SR: NTP inside the last received (mid 16 bits from sec and frac).
  uint32_t ntp_secs = 0;
  uint32_t ntp_frac = 0;

  if (!rtcp_receiver_.NTP(&ntp_secs,
                          &ntp_frac,
                          rtcp_arrival_time_secs,
                          rtcp_arrival_time_frac,
                          NULL)) 
  {
    return false;
  }
  
  *remote_sr = ((ntp_secs & 0x0000ffff) << 16) + ((ntp_frac & 0xffff0000) >> 16);
  
  return true;
}

_remoteSenderInfo.NTPseconds 与 _remoteSenderInfo.NTPfraction:最近收到的 SR 包中所携带的 NTP 时间戳。

_lastReceivedSRNTPfrac 与 _lastReceivedSRNTPsecs:最近收到 SR 包时的本地时间戳。

具体来源,参考前一篇文章:WebRTC研究:rrt 时间计算 之 接收 SR 包

// TODO(pbos): Make this fail when we haven't received NTP.
bool RTCPReceiver::NTP(uint32_t* ReceivedNTPsecs,
                       uint32_t* ReceivedNTPfrac,
                       uint32_t* RTCPArrivalTimeSecs,
                       uint32_t* RTCPArrivalTimeFrac,
                       uint32_t* rtcp_timestamp) const
{
    rtc::CritScope lock(&_criticalSectionRTCPReceiver);
    
    if(ReceivedNTPsecs)
    {
        *ReceivedNTPsecs = _remoteSenderInfo.NTPseconds; // SR 包所携带 NTP 时间戳
    }
    if(ReceivedNTPfrac)
    {
        *ReceivedNTPfrac = _remoteSenderInfo.NTPfraction;
    }
    if(RTCPArrivalTimeFrac)
    {
        *RTCPArrivalTimeFrac = _lastReceivedSRNTPfrac; // 接收到 SR 包时的当前 NTP 时间戳
    }
    
    if(RTCPArrivalTimeSecs)
    {
        *RTCPArrivalTimeSecs = _lastReceivedSRNTPsecs;
    }
    
    if (rtcp_timestamp) 
    {
      *rtcp_timestamp = _remoteSenderInfo.RTPtimeStamp;
    }
    return true;
}

四、PrepareReport() 会根据字段 sending_(是否发送端)的值,判定发送 SR 还是 RR 包:

void RTCPSender::PrepareReport(const std::set<RTCPPacketType>& packetTypes,
                               const FeedbackState& feedback_state) 
{
    ...
    ...
    ...
    
    /*
	根据 sending_(是否发送端)判定发送 SR 或 RR 包
    SetFlag():将 flag:kRtcpSr 或 kRtcpRr 存入 集合 report_flags_,节点内部字段:is_volatile 的值设置为 true。
	*/
    if (generate_report)
      SetFlag(sending_ ? kRtcpSr : kRtcpRr, true);

    ...
    ...
    ...
    
	/*
	计算下次发送 RTCP 的时间,每次时间间隔不一,
	对于 audio,时间间隔是 [1/2 * 5000, 3/2 * 5000] ms之间的一个随机数
	*/
    uint32_t timeToNext = random_.Rand(minIntervalMs * 1 / 2, minIntervalMs * 3 / 2);
    next_time_to_send_rtcp_ = clock_->TimeInMilliseconds() + timeToNext;

    /*
    AddReportBlock()会设置两个关键值:
    last_sr_:SR 包中 NTP 时间戳的中间32位
    delay_since_last_sr_:从收到 SR包 到发送 RR包 之间的延时
    */
    if (receive_statistics_) 
	{
      StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians();

      RTC_DCHECK(report_blocks_.empty());
      for (auto& it : statisticians) 
	  {
        AddReportBlock(feedback_state, it.first, it.second);
      }
    }
    
    ...
    ...
    ...
}

五、设置 last_sr_(SR 包中 NTP 时间戳的中间32位)与 delay_since_last_sr_(从 接到SR包 到 发送RR包 之间的延时):

bool RTCPSender::AddReportBlock(const FeedbackState& feedback_state,
                                uint32_t ssrc,
                                StreamStatistician* statistician) 
{
  ...
  ...
  ...
  rtcp::ReportBlock* block = &report_blocks_[ssrc];
  
  /* 设置 last_sr_,即为 SR 包中 NTP 时间戳的中间32位 */
  block->WithLastSr(feedback_state.remote_sr);

  // Delay since last received report.
  if ((feedback_state.last_rr_ntp_secs != 0) || (feedback_state.last_rr_ntp_frac != 0)) 
  {
    // Get the 16 lowest bits of seconds and the 16 highest bits of fractions.
    uint32_t now = ntp_secs & 0x0000FFFF;
    now <<= 16;
    now += (ntp_frac & 0xffff0000) >> 16;

    uint32_t receiveTime = feedback_state.last_rr_ntp_secs & 0x0000FFFF;
    receiveTime <<= 16;
    receiveTime += (feedback_state.last_rr_ntp_frac & 0xffff0000) >> 16;

	/* 设置 delay_since_last_sr_,即为 从接到SR包 到发送RR包 之间的延时 */
    block->WithDelayLastSr(now - receiveTime);
  }
  return true;
  
  ...
  ...
  ...  
}

六、根据发送 SR 包还是RR 包,调用不同的包封装函数:

int32_t RTCPSender::SendCompoundRTCP(
    const FeedbackState& feedback_state,
    const std::set<RTCPPacketType>& packet_types,
    int32_t nack_size,
    const uint16_t* nack_list,
    bool repeat,
    uint64_t pictureID) 
{
  ...
  ...
  ...
  
  // 获取当前 NTP 时间戳
  uint32_t ntp_sec;
  uint32_t ntp_frac;
  clock_->CurrentNtp(ntp_sec, ntp_frac);
  
  RtcpContext context(feedback_state, nack_size, nack_list, repeat, pictureID,
                        ntp_sec, ntp_frac);
                        
  // 判断是发送 SR 包还是RR 包,并设置 last_sr_ 与 delay_since_last_sr_
  PrepareReport(packet_types, feedback_state);
 
  std::unique_ptr<rtcp::RtcpPacket> packet_bye;

  // 封装包
  auto it = report_flags_.begin();
  while (it != report_flags_.end()) 
  {
    auto builder_it = builders_.find(it->type);	
    RTC_DCHECK(builder_it != builders_.end());
    if (it->is_volatile) 
	{
      report_flags_.erase(it++);
    } 
	else 
	{
      ++it;
    }

	/*
	根据 flag 调用对应的包封装函数:
	kRtcpSr:RTCPSender::BuildSR
	kRtcpRr:RTCPSender::BuildRR
	*/
	  
    BuilderFunc func = builder_it->second;
    std::unique_ptr<rtcp::RtcpPacket> packet = (this->*func)(context);
    ...
    ...
    ...
  }

  // 发送包
  size_t bytes_sent = container.SendPackets(max_payload_length_);
  return bytes_sent == 0 ? -1 : 0;
}

七、封装 SR 包:

std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildSR(const RtcpContext& ctx) 
{
  ...
  ...
  ...
  
  /* 封装当前 NTP 时间戳 */
  report->WithNtp(NtpTime(ctx.ntp_sec_, ctx.ntp_frac_));
  report->WithRtpTimestamp(rtp_timestamp);

  ...
  ...
  ...
}

八、封装 RR 包:

直接使用 map 对象 report_blocks_ 构造包:

std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildRR(const RtcpContext& ctx) 
{
  rtcp::ReceiverReport* report = new rtcp::ReceiverReport();
  report->From(ssrc_);
  for (auto it : report_blocks_)
    report->WithReportBlock(it.second);

  report_blocks_.clear();
  return std::unique_ptr<rtcp::RtcpPacket>(report);
}
发布了112 篇原创文章 · 获赞 22 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/u010601662/article/details/105256409
rr
sr