WebRTC研究:rrt 时间计算 之 接收 SR / RR 包

一、当 sender / receiver 收到一个 RTCP 包时,会根据包类型调用分别调用不同的处理函数。对于 SR 和 RR 包,都会交由 HandleSenderReceiverReport() 处理:

int32_t ModuleRtpRtcpImpl::IncomingRtcpPacket(const uint8_t* rtcp_packet, const size_t length) --------- rtp_rtcp_impl.cc

int32_t RTCPReceiver::IncomingRTCPPacket(RTCPPacketInformation& rtcpPacketInformation, RTCPUtility::RTCPParserV2* rtcpParser) --------- rtp_rtcp_impl.cc

int32_t
RTCPReceiver::IncomingRTCPPacket(RTCPPacketInformation& rtcpPacketInformation,
                                 RTCPUtility::RTCPParserV2* rtcpParser)
{
  ...
  ...
  ...
  while (pktType != RTCPPacketTypes::kInvalid) 
  {
        // Each "case" is responsible for iterate the parser to the
        // next top level packet.
        switch (pktType)
        {
        case RTCPPacketTypes::kSr:
        case RTCPPacketTypes::kRr:
            HandleSenderReceiverReport(*rtcpParser, rtcpPacketInformation);
            break;
        ...
        ...
        ...
        }
        pktType = rtcpParser->PacketType();
  }
  ...
  ...
  ...
}

二、对于 SR 包,receiver 会保留包内所携带的 NTP 时间戳,并记录当前 NTP 时间戳:

void
RTCPReceiver::HandleSenderReceiverReport(RTCPUtility::RTCPParserV2& rtcpParser,
                                         RTCPPacketInformation& rtcpPacketInformation)
{
    RTCPUtility::RTCPPacketTypes rtcpPacketType = rtcpParser.PacketType();
    const RTCPUtility::RTCPPacket& rtcpPacket   = rtcpParser.Packet();
    
    // 在 map:_receivedInfoMap 中创建一个 RTCPReceiveInformation 对象,键为 remoteSSRC
    RTCPReceiveInformation* ptrReceiveInfo = CreateReceiveInformation(remoteSSRC);
    if (!ptrReceiveInfo)
    {
        rtcpParser.Iterate();
        return;
    }
    
  	...
	...
	...
	
	/* 接收到 SR 包 */
    if (rtcpPacketType == RTCPPacketTypes::kSr) 
	{
        ...
	    ...
	    ...
	    
        if (_remoteSSRC == remoteSSRC) // have I received RTP packets from this party
        {
            /* 记录保存在 SR 包中的 NTP 时间戳 */
            _remoteSenderInfo.NTPseconds = rtcpPacket.SR.NTPMostSignificant;
            _remoteSenderInfo.NTPfraction = rtcpPacket.SR.NTPLeastSignificant;

			/* 记录接收到 SR 包的当前 NTP 时间戳 */
            _clock->CurrentNtp(_lastReceivedSRNTPsecs, _lastReceivedSRNTPfrac);
            
            ...
	        ...
	        ...
        }
        else
        {
            rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpRr;
        }
    } 
	else
    {
      /* 接收到 RR 包 */
      TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RR",
                           "remote_ssrc", remoteSSRC, "ssrc", main_ssrc_);

        rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpRr;
    }

    // 标记 RTCPReceiveInformation.lastTimeReceived 为当前时间,表示对端目前是活动的
    UpdateReceiveInformation(*ptrReceiveInfo);
    
    rtcpPacketType = rtcpParser.Iterate();

    // 取出 RR 包中 DelayLastSR / LastSR,计算 RRT 时间
    while (rtcpPacketType == RTCPPacketTypes::kReportBlockItem) 
	{
        HandleReportBlock(rtcpPacket, rtcpPacketInformation, remoteSSRC);
        rtcpPacketType = rtcpParser.Iterate();
    }

三、更新最近接收到 SR/RR 包时间:

void RTCPReceiver::UpdateReceiveInformation(RTCPReceiveInformation& receiveInformation) 
{
  // Update that this remote is alive
  receiveInformation.lastTimeReceived = _clock->TimeInMilliseconds();
}

四、对于 RR 包,sender 会取出包体所携带的 DelayLastSR / LastSR,并计算 RRT 时间:

RRT = receive_time - delay - send_time,即:sender接收到RR包的当前时间戳 - receiver从接到SR包到发送RR包之间的延时 - sender发送SR包时的当前时间戳

void RTCPReceiver::HandleReportBlock(
    const RTCPUtility::RTCPPacket& rtcpPacket,
    RTCPPacketInformation& rtcpPacketInformation,
    uint32_t remoteSSRC)
    EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPReceiver) 
{
  ...
  ...
  ...
  
  /*
  在 std::map<uint32_t, std::map<uint32_t, RTCPHelp::RTCPReportBlockInformation*> > _receivedReportBlockMap 中创建一个键值对
  外层 key:rtcpPacket.ReportBlockItem.SSRC
  内层 key:remoteSSRC
  */
  RTCPReportBlockInformation* reportBlock = CreateOrGetReportBlockInformation(remoteSSRC, rtcpPacket.ReportBlockItem.SSRC);
  if (reportBlock == NULL) 
  {
    LOG(LS_WARNING) << "Failed to CreateReportBlockInformation("
                    << remoteSSRC << ")";
    return;
  }

  ...
  ...
  ...
  
  // 取出 RR 包中携带的 DelayLastSR,即为 receiver(对端)从接到SR包 到发送RR包 之间的延时
  reportBlock->remoteReceiveBlock.delaySinceLastSR = rb.DelayLastSR;

  // 取出 RR 包中携带的 LastSR,也是 sender(本端)发送 SR 包时的当前时间戳
  reportBlock->remoteReceiveBlock.lastSR = rb.LastSR;
  
  ...
  ...
  ...
  
  int64_t rtt = 0;

  // 取出 RR 包中携带的 LastSR,也是 sender(本端)发送 SR 包时的当前时间戳
  uint32_t send_time = rtcpPacket.ReportBlockItem.LastSR;
  if (!receiver_only_ && send_time != 0) 
  {
	// 取出 RR 包中携带的 DelayLastSR,即为 receiver(对端)从接到SR包 到发送RR包 之间的延时
    uint32_t delay = rtcpPacket.ReportBlockItem.DelayLastSR;

    // 获取当前时间戳
    uint32_t receive_time = CompactNtp(NtpTime(*_clock));

	// 计算 RTT,单位是:1/(2^16) = 1 / 65536 s
    uint32_t rtt_ntp = receive_time - delay - send_time;


    // 将 RTT 的单位转换成 ms
    rtt = CompactNtpRttToMs(rtt_ntp);

	// 记录最大 RRT 时间
    if (rtt > reportBlock->maxRTT) 
	{
      reportBlock->maxRTT = rtt;
    }

	// 记录最小 RTT 时间
    if (reportBlock->minRTT == 0) 
	{
      reportBlock->minRTT = rtt;
    } 
	else if (rtt < reportBlock->minRTT) 
	{
      reportBlock->minRTT = rtt;
    }

    // 记录最新 RTT 时间
    reportBlock->RTT = rtt;

    // 记录平均 RTT 时间
    if (reportBlock->numAverageCalcs != 0) 
	{
	  // 获取平均 RTT 时间计算次数
      float ac = static_cast<float>(reportBlock->numAverageCalcs);

	  /*计算最新平均 RTT 时间,为:
	  旧平均RTT时间 与 最新RTT 时间的加权和,权重分别为 ac / (ac + 1)、(1 / (ac + 1)
	  */
      float newAverage = ((ac / (ac + 1)) * reportBlock->avgRTT) + ((1 / (ac + 1)) * rtt);

	  // 四舍五入
      reportBlock->avgRTT = static_cast<int64_t>(newAverage + 0.5f);
    } 
	else 
	{
      // First RTT.
      reportBlock->avgRTT = rtt;
    }

	// 记录平均 RRT 时间计算次数
    reportBlock->numAverageCalcs++;
  }

  ...
  ...
  ...
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值