天天看點

[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() % ;
int32_t timeToNext = RTCP_INTERVAL_AUDIO_MS;

if(_audio)
{
timeToNext = (RTCP_INTERVAL_AUDIO_MS/) +
(RTCP_INTERVAL_AUDIO_MS*random/);
}else
{
uint32_t minIntervalMs = RTCP_INTERVAL_AUDIO_MS;
if(_sending)
{
// Calculate bandwidth for video;  / send bandwidth in kbit/s.
uint32_t send_bitrate_kbit = feedback_state.send_bitrate / ;
if (send_bitrate_kbit != )
minIntervalMs =  / send_bitrate_kbit;
}
if(minIntervalMs > RTCP_INTERVAL_VIDEO_MS)
{
minIntervalMs = RTCP_INTERVAL_VIDEO_MS;
}
timeToNext = (minIntervalMs/) + (minIntervalMs*random/);
}
_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 = ;
uint32_t lastReceivedRRNTPfrac = ;

_clock->CurrentNtp(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac);

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

// Estimate RTT
uint32_t d = (delaySinceLastSendReport & ) * ;
d /= ;
d += ((delaySinceLastSendReport & ) >> ) * ;

int32_t RTT = ;

if (sendTimeMS > ) {
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 ==  && _lastReceivedXRNTPfrac == ) {
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 = ;
uint32_t ntp_frac = ;
_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)

http://www.cnblogs.com/lingdhox/p/5746210.html

繼續閱讀