[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)

?

轉載于:https://www.cnblogs.com/lingdhox/p/5746210.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247157.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247157.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247157.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Deep learning Reading List

本文轉自&#xff1a; http://jmozah.github.io/links/ http://www.datakit.cn/blog/2014/12/31/Deep_learning_Reading_List.html 文章來自J Mohamed Zahoor的深度學習閱讀書單。 Following is a growing list of some of the materials i found on the web for Deep Learning…

商戶網站使用第三方支付的大致原理和實現

如果把商戶網站使用第三方支付平臺(比如支付寶)的原理搞清楚&#xff0c;那編程就變得簡單多了。 整個過程大致這樣&#xff1a; 1、商戶與支付寶簽約。2、在商戶網站購買商品&#xff0c;填寫數量&#xff0c;確定購買后跳轉到結賬頁面。結賬頁面可能包括訂單號、定單狀態(已支…

Awesome Deep Vision

本文轉自&#xff1a;https://github.com/kjw0612/awesome-deep-vision http://jiwonkim.org/awesome-deep-vision/ A curated list of deep learning resources for computer vision, inspired by awesome-php and awesome-computer-vision. Maintainers - Jiwon Kim, Heesoo …

GitHub 新出的 Actions 是什么? 用他做自動測試?

體驗分享 本文一個嘗鮮的體驗分享, 并沒有太復雜的技巧, 做了一個最少代碼的例子展示, 讓每個人都可以把action用起來, 如果路過的大牛有高級技巧請留言分享, 我會補充. 下面正文開始. 是什么? 是一個免費的操作系統容器(Linux/Windows/macOS), 我們可以讓他預裝開發環境(node…

caffe框架翻譯-理解(轉載)

本文轉自&#xff1a; http://dirlt.com/caffe.html http://blog.csdn.net/songyu0120/article/details/468170851 caffe http://caffe.berkeleyvision.org/ 1.1 setup 安裝需要下面這些組件。這些組件都可以通過apt-get獲得。 libgoogle-glog-dev # gloglibgflags-dev # gfla…

賈揚清分享_深度學習框架caffe

本文轉自&#xff1a; http://www.datakit.cn/blog/2015/06/12/online_meet_up_with_yangqing_jia.html http://www.ifight.me/187/ Caffe是一個清晰而高效的深度學習框架&#xff0c;其作者是博士畢業于UC Berkeley的 賈揚清&#xff0c;目前在Google工作。本文是根據機器學習…

iOS多線程理解

在iOS開發中&#xff0c;線程的創建與管理已經被Apple進行了很好的封裝&#xff0c;但是在開發者實際開發中會濫用GCD,導致整個代碼混亂不堪&#xff0c;因此在這里需要對iOS開發中的多線程開發進行整理。 1. 主線程完成耗時操作&#xff0c;會導致UI卡頓&#xff0c;因此耗時…

Java生鮮電商平臺-SpringCloud微服務架構中分布式事務解決方案

Java生鮮電商平臺-SpringCloud微服務架構中分布式事務解決方案 說明&#xff1a;Java生鮮電商平臺中由于采用了微服務架構進行業務的處理&#xff0c;買家&#xff0c;賣家&#xff0c;配送&#xff0c;銷售&#xff0c;供應商等進行服務化&#xff0c;但是不可避免存在分布式事…

批量提取 caffe 特征 (python, C++, Matlab)(待續)

本文參考如下&#xff1a; Instant Recognition with Caffe Extracting Features Caffe Python特征提取 caffe 練習4 —-利用python批量抽取caffe計算得到的特征——by 香蕉麥樂迪 caffe 練習3 用caffe提供的C函數批量抽取圖像特征——by 香蕉麥樂迪 caffe python批量抽…

iOS單例初步理解

iOS單例初步理解 在iOS開發中&#xff0c;系統自帶的框架中使用了很多單例&#xff0c;非常方便用戶&#xff08;開發者&#xff0c;使用比如[NSApplication sharedApplication] 等&#xff09;&#xff0c;在實際的開發中&#xff0c;有時候也需要設計單例對象&#xff0c;為…

python面向對象之類的成員

面向對象之類的成員 細分類的組成成員 類大致分為兩塊區域&#xff1a; 第一部分&#xff1a;靜態字段 第二部分&#xff1a;動態方法 class Animal:type_name "動物類" # 靜態變量&#xff08;靜態字段&#xff09;__feature "活的" # 私有靜態變量…

python元類、反射及雙線方法

元類、反射及雙線方法 元類 print(type(abc)) print(type(True)) print(type(100)) print(type([1, 2, 3])) print(type({name: 太白金星})) print(type((1,2,3))) print(type(object))class A:passprint(isinstance(object,type)) print(isinstance(A, type)) type元類是獲取該…

iOS中的多線程一般使用場景

在IOS開發中為提高程序的運行效率會將比較耗時的操作放在子線程中執行&#xff0c;iOS系統進程默認啟動一個主線程&#xff0c;用來響應用戶的手勢操作以及UI刷新&#xff0c;因此主線程又叫做UI線程。 前面的Blog說明了NSThread以及GCD處理并發線程以及線程安全&#xff08;線…

iOS中如何優化Cell中圖片的下載性能

在iOS開發中使用最為常見的是UITableView&#xff0c;其中UITabelViewCell中下載圖片&#xff0c;會影響用戶下拉刷新UI,導致卡頓&#xff0c;用戶體驗不好&#xff0c;在這篇blog中&#xff0c;我將以一個例子來說明如何優化UITableView下載圖片 1.使用懶加載方式&#xff0c…

【Yoshua Bengio 親自解答】機器學習 81 個問題及答案(最全收錄)

本文轉自&#xff1a;http://mp.weixin.qq.com/s?__bizMzI3MTA0MTk1MA&mid401958262&idx1&sn707f228cf5779a31f0933af903516ba6&scene1&srcid0121zzdeFPtgoRoEviZ3LZDG#rd 譯者&#xff1a;張巨巖 王婉婷 李宏菲 戴秋池 這是 Quora 的最新節目&#xf…

Java生鮮電商平臺-SpringCloud微服務架構中網絡請求性能優化與源碼解析

Java生鮮電商平臺-SpringCloud微服務架構中網絡請求性能優化與源碼解析 說明&#xff1a;Java生鮮電商平臺中&#xff0c;由于服務進行了拆分&#xff0c;很多的業務服務導致了請求的網絡延遲與性能消耗&#xff0c;對應的這些問題&#xff0c;我們應該如何進行網絡請求的優化與…

XCode7 創建framework

1.新建一個靜態庫工程. file→ new→ project, 彈出框中選擇iOS→ framework & library中的cocoa touch static library.點擊Next,輸入product name: TestFramework, 點擊Next→ 點擊Create. 2.刪除向導所生成工程中的Target. 點擊工程名→ 點擊TARGETS → 右鍵Delete. …

基礎js逆向練習-登錄密碼破解(js逆向)

練習平臺&#xff1a;逆向賬號密碼 https://login1.scrape.center/ 直接打開平臺&#xff0c;輸入密碼賬號&#xff0c;抓包找到加密的參數攜帶的位置&#xff0c;這邊我們找到的是一個叫token的加密參數&#xff0c;這個參數的攜帶是一個密文 我們首先考慮一下搜索這個加密的…

python之socket

socket套接字 什么叫socket socket是處于應用層與傳輸層之間的抽象層,他是一組操作起來非常簡單的接口(接受數據)此接口接受數據之后,交由操作系統.socket在python中就是一個模塊. socket兩個分類 基于文件類型的套接字家族 套接字家族的名字&#xff1a;AF_UNIX unix一切皆文件…

iOS----JSON解析

在iOS開發中與服務器進行數據交互操作&#xff0c;操作過程中使用最為常見的格式為JSON與XML,其中JSON較為清量,因此本篇blog就講解一下如何在iOS中進行JSON解析。 1.建立HTTP請求 &#xff08;1&#xff09;創建URL NSString *URLStr [NSString stringWithFormat:”http:/…