zoukankan      html  css  js  c++  java
  • [webrtc] rtcp模块中rtt时间计算

    RTT指 round-trip time,即计算AB两端的往返时延

    这里可以分成两个问题:

    如何在A端估算A和B之间的RTT时间?

    如何在B端估算A和B之间的RTT时间?

    本文参考资料:
    rfc 3550
    rfc 3611
    webrtc issue https://code.google.com/p/webrtc/issues/detail?id=1613
    以及解决版本
    https://code.google.com/p/webrtc/source/detail?r=4898
    https://code.google.com/p/webrtc/source/detail?r=5063

    一、假设A -> B 发送视频. 那么如何在A端估算A->B之间的RTT时间?

    RFC 3550 http://tools.ietf.org/html/rfc3550#section-6.4.1
    6.4.1 SR: Sender Report RTCP Packet
    中描述了如何在发送端计算RTT时间.
    大概过程如下:
    A 发送 SR 包, 并记录SR包的发送时间. 记为send_time
    B 接收到 A的SR包后, 记录下最后一次接受到SR包的时间. 记为last_recv_time
    ... (B等待发送rtcp包)
    B 发送 RR包, 计算从[last_recv_time] 到 当前时间的延时. 记录为delay_since_last_SR. 附加到RR包中.
    A 收到 B的RR包后, 计算RTT
    RTT = send_time - delay_since_last_SR - last_recv_time

    对应到webrtc中的实现.

    何时发送rtcp ?
    ModuleRtpRtcpImpl::Process
    if (rtcp_sender_.TimeToSendRTCPReport()) {
    rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport);
    }
    在RTCPSender::TimeToSendRTCPReport 详细说明了RTCP的发送频率.
    每次发送RTCP时都会计算出下一次发送rtcp的时间, 即_nextTimeToSendRTCP.
    对于 RR和SR包. 计算如下.
    RTCPSender::PrepareRTCP
    ....
    if( rtcpPacketTypeFlags & kRtcpRr ||
    rtcpPacketTypeFlags & kRtcpSr)
    {
    // generate next time to send a RTCP report
    // seeded from RTP constructor
    int32_t random = rand() % 1000;
    int32_t timeToNext = RTCP_INTERVAL_AUDIO_MS;

    if(_audio)
    {
    timeToNext = (RTCP_INTERVAL_AUDIO_MS/2) +
    (RTCP_INTERVAL_AUDIO_MS*random/1000);
    }else
    {
    uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS;
    if(_sending)
    {
    // Calculate bandwidth for video; 360 / send bandwidth in kbit/s.
    uint32_t send_bitrate_kbit = feedback_state.send_bitrate / 1000;
    if (send_bitrate_kbit != 0)
    minIntervalMs = 360000 / send_bitrate_kbit;
    }
    if(minIntervalMs > RTCP_INTERVAL_VIDEO_MS)
    {
    minIntervalMs = RTCP_INTERVAL_VIDEO_MS;
    }
    timeToNext = (minIntervalMs/2) + (minIntervalMs*random/1000);
    }
    _nextTimeToSendRTCP = _clock->TimeInMilliseconds() + timeToNext;
    }
    依赖于随机值, 而且音视频的时间也不同.


    A -> 发送SR包.
    ModuleRtpRtcpImpl::Process
    RTCPSender::SendRTCP
    RTCPSender::PrepareRTCP
    RTCPSender::BuildSR
    PrepareRTCP 中通过_sending(是否是发送端) 状态判定发送SR或RR. SR包中含有发送时的NTP时间戳.
    BuildSR中
    _lastSendReport 记录NTP时间的中间32位. 可以标识SR包, 也就是B回应RR包中report block的LSR字段(last SR timestamp ), 通过LSR可以查找_lastRTCPTime.
    _lastRTCPTime记录RTCP_NUMBER_OF_SR个数的SR发送时间.
    这两个数组是一一对应的.
    _lastRTCPTime[0] = Clock::NtpToMs(NTPsec, NTPfrac);
    _lastSendReport[0] = (NTPsec << 16) + (NTPfrac >> 16);

    最后SendToNetwork.


    B -> 接收到SR包.
    ModuleRtpRtcpImpl::IncomingRtcpPacket
    RTCPReceiver::IncomingRTCPPacket
    RTCPReceiver::HandleSenderReceiverReport
    在HandleSenderReceiverReport 中保存 SR包中的NTP时间戳
    _remoteSenderInfo.NTPseconds = rtcpPacket.SR.NTPMostSignificant;
    _remoteSenderInfo.NTPfraction = rtcpPacket.SR.NTPLeastSignificant;
    并记录SR包接到时的NTP时间戳
    _clock->CurrentNtp(_lastReceivedSRNTPsecs, _lastReceivedSRNTPfrac);


    B -> 发送RR包
    获取回馈状态, 并发送给A
    ModuleRtpRtcpImpl::Process()
    if (rtcp_sender_.TimeToSendRTCPReport()) {
    rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport);
    }

    ModuleRtpRtcpImpl::GetFeedbackState()
    ModuleRtpRtcpImpl::LastReceivedNTP

    state.last_rr_ntp_secs 和state.last_rr_ntp_frac
    即为上一次接收到SR包时, 记录的_clock->CurrentNtp(_lastReceivedSRNTPsecs, _lastReceivedSRNTPfrac); 时间戳.
    state.remote_sr 通过_remoteSenderInfo.NTPseconds 和 _remoteSenderInfo.NTPfraction, 取中间32位算出.

    RTCPSender::PrepareReport
    在这里计算延时, 填充到report block中.

    // get our NTP as late as possible to avoid a race
    _clock->CurrentNtp(*ntp_secs, *ntp_frac);

    // Delay since last received report
    uint32_t delaySinceLastReceivedSR = 0;
    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 higest 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;

    delaySinceLastReceivedSR = now-receiveTime;
    }
    report_block->delaySinceLastSR = delaySinceLastReceivedSR;
    report_block->lastSR = feedback_state.remote_sr;

    report_block->delaySinceLastSR 即为 从接到SR包到发送RR包之间的延时.
    report_block->lastSR 即SR包中NTP时间戳的中间32位. (在A端_lastSendReport数组中记录).

    A 收到 B的RR包
    ModuleRtpRtcpImpl::IncomingRtcpPacket
    RTCPReceiver::IncomingRTCPPacket
    RTCPReceiver::HandleSenderReceiverReport
    RTCPReceiver::HandleReportBlock
    通过 lastSR 到sender模块中取出SR包的发送时间.
    uint32_t sendTimeMS =
    _rtpRtcp.SendTimeOfSendReport(rtcpPacket.ReportBlockItem.LastSR);

    计算RTT .

    uint32_t delaySinceLastSendReport =
    rtcpPacket.ReportBlockItem.DelayLastSR;

    // local NTP time when we received this
    uint32_t lastReceivedRRNTPsecs = 0;
    uint32_t lastReceivedRRNTPfrac = 0;

    _clock->CurrentNtp(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac);

    // time when we received this in MS
    uint32_t receiveTimeMS = Clock::NtpToMs(lastReceivedRRNTPsecs,
    lastReceivedRRNTPfrac);

    // Estimate RTT
    uint32_t d = (delaySinceLastSendReport & 0x0000ffff) * 1000;
    d /= 65536;
    d += ((delaySinceLastSendReport & 0xffff0000) >> 16) * 1000;

    int32_t RTT = 0;

    if (sendTimeMS > 0) {
    RTT = receiveTimeMS - d - sendTimeMS;
    ....
    }

    注意delay since last SR (DLSR) 的单位是1/65536秒.


    二、另外一个问题, 那么如何在B端估算A和B之间的RTT时间?

    如果是互相视频聊天的话, A和B都是发送端, 自然都可以计算出RTT.
    但是B如果仅仅是接收者的话, 仅仅依靠RFC3550协议是无法计算RTT时间的.
    需要参考rfc 3611协议, 实现section4.5 的 DLRR Report Block 即可. http://tools.ietf.org/html/rfc3611#section-4.5

    webrtc 在bug 1613 https://code.google.com/p/webrtc/issues/detail?id=1613
    中讨论该问题. 并在版本 https://code.google.com/p/webrtc/source/detail?r=4898
    和https://code.google.com/p/webrtc/source/detail?r=5063 中修复.

    具体实现和SR非常类似.
    1. 开启XR协议 ModuleRtpRtcpImpl::SetRtcpXrRrtrStatus(true)

    webrtc的示例loopback 程序中可以这样启动
    receive_config.rtp.rtcp_xr.receiver_reference_time_report = true;

    接受者(B)发送RTCP时, 附加kRtcpXrReceiverReferenceTime
    发送者(A)发送RTCP时, 附加kRtcpXrDlrrReportBlock

    RTCPSender::PrepareRTCP
    if (xrSendReceiverReferenceTimeEnabled_ && !_sending)
    {
    rtcpPacketTypeFlags |= kRtcpXrReceiverReferenceTime;
    }
    if (feedback_state.has_last_xr_rr)
    {
    rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock;
    }

    B在发送kRtcpXrReceiverReferenceTime, 在last_xr_rr_ map中记录 NTP时间戳中间32位(key) 和 发送时间(value).

    A 收到XR_RR包后
    在处理kRtcpXrReceiverReferenceTimeCode
    RTCPReceiver::HandleXrReceiveReferenceTime

    _remoteXRReceiveTimeInfo.lastRR = RTCPUtility::MidNtp(
    packet.XRReceiverReferenceTimeItem.NTPMostSignificant,
    packet.XRReceiverReferenceTimeItem.NTPLeastSignificant);
    _clock->CurrentNtp(_lastReceivedXRNTPsecs, _lastReceivedXRNTPfrac);

    记录lastRR和收到XR_RR包的时间.

    A 发送RTCP时, 会检查是否收到过xr_rr包.
    ModuleRtpRtcpImpl::GetFeedbackState()
    state.has_last_xr_rr = LastReceivedXrReferenceTimeInfo(&state.last_xr_rr);

    bool RTCPReceiver::LastReceivedXrReferenceTimeInfo(
    RtcpReceiveTimeInfo* info) const {
    assert(info);
    CriticalSectionScoped lock(_criticalSectionRTCPReceiver);
    if (_lastReceivedXRNTPsecs == 0 && _lastReceivedXRNTPfrac == 0) {
    return false;
    }

    info->sourceSSRC = _remoteXRReceiveTimeInfo.sourceSSRC;
    info->lastRR = _remoteXRReceiveTimeInfo.lastRR;

    // Get the delay since last received report (RFC 3611).
    uint32_t receive_time = RTCPUtility::MidNtp(_lastReceivedXRNTPsecs,
    _lastReceivedXRNTPfrac);

    uint32_t ntp_sec = 0;
    uint32_t ntp_frac = 0;
    _clock->CurrentNtp(ntp_sec, ntp_frac);
    uint32_t now = RTCPUtility::MidNtp(ntp_sec, ntp_frac);

    info->delaySinceLastRR = now - receive_time;
    return true;
    }
    计算出 从接到last_xr_rr 到当前的延时.
    然后发送 kRtcpXrDlrrReportBlock 出去.

    B 收到XR_SR后
    RTCPReceiver::HandleXrDlrrReportBlock
    计算出RTT时间. 保存在xr_rr_rtt_ms_


    rtp_rtcp_impl_unittest.cc 测试程序.
    TEST_F(RtpRtcpImplTest, RttForReceiverOnly)

  • 相关阅读:
    一天进步一点点
    Flask
    Sqlalchemy 设置表编码及引擎
    threading.local
    xshell配置密码公钥登录
    linux 系统优化+定时任务
    linux命令
    xshell连接及优化
    linux前奏
    Vue Devtools--vue调式工具
  • 原文地址:https://www.cnblogs.com/lingdhox/p/5746210.html
Copyright © 2011-2022 走看看