前面文章介绍了抖动,今天我们来看下chrome://webrtc-internals/
页面中的一个重要统计参数:往返时延 (RTT)。
什么是RTT
往返时延 (round-trip time,RTT) 是网络请求从起点到目的地然后再回到起点所花费的时长(不包括接收端的处理时间)。RTT是分析网络性能的一个重要指标,我们通常使用RTT来诊断网络连接的速度,可靠性以及拥塞程度。
ping命令是最常见的一种估计往返时延的方法。下面是ping Google的示例,底部显示了rtt的不同表示:最小,平均,最大,平均偏差。
root @ jeff : ~ # ping google.com
PING google . com ( 172.253.118.138 ) 56 ( 84 ) bytes of data .
64 bytes from 172.253.118.138 : icmp_seq = 1 ttl = 105 time = 1.81 ms
64 bytes from 172.253.118.138 : icmp_seq = 2 ttl = 105 time = 1.77 ms
64 bytes from 172.253.118.138 : icmp_seq = 3 ttl = 105 time = 1.76 ms
64 bytes from 172.253.118.138 : icmp_seq = 4 ttl = 105 time = 1.77 ms
64 bytes from 172.253.118.138 : icmp_seq = 5 ttl = 105 time = 1.94 ms
64 bytes from 172.253.118.138 : icmp_seq = 6 ttl = 105 time = 1.86 ms
-- - google . com ping statistics -- -
6 packets transmitted , 6 received , 0 % packet loss , time 5554ms
rtt min / avg / max / mdev = 1.764 / 1.821 / 1.944 / 0.069 ms
RTT计算方式
WebRTC中目前有两种方式计算RTT:
基于媒体流发送端的计算(默认开启)。通过Sender Report(SR)与Receiver Report(RR)携带的信息。
基于媒体流接收端的计算。通过RTCP Extended ReportsRTCP(XR)携带的信息。
这两种方式计算RTT的原理都一样。至于为什么需要接收端计算方式,这是因为在一些场景,媒体流传输是单向的,两个端点间一个只发媒体数据,一个只接收,例如客户端与SFU服务器间。假设客户端与SFU服务器上行推流场景,客户端推流,服务端收流,这种场景下作为接收端的服务端并不会发送SR,导致无法计算RTT。于是就有了通过RTCP XR在接收端计算这种方式。
媒体流发送端RTT计算
WebRTC中媒体流发送端RTT的计算是根据SR以及RR中携带的时间信息。这里我们回顾下Sender Report与Receiver Report RTCP两种报文。
Sender Report
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
header | V = 2 | P | RC | PT = SR = 200 | length |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| SSRC of sender |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
sender | NTP timestamp , most significant word |
info + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| NTP timestamp , least significant word |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| RTP timestamp |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| sender 's packet count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sender' s octet count |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
NTP timestamp:64bits。记录着发送该SR的NTP时间戳。
Receiver Report
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
header | V = 2 | P | RC | PT = RR = 201 | length |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| SSRC of packet sender |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
report | SSRC_1 ( SSRC of first source ) |
block + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
1 | fraction lost | cumulative number of packets lost |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| extended highest sequence number received |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| interarrival jitter |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| last SR ( LSR ) |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| delay since last SR ( DLSR ) |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
report | SSRC_2 ( SSRC of second source ) |
block + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
2 : . . . :
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
| profile - specific extensions |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
last SR timestamp (LSR) : 32 bits。64位NTP时间戳中间的32bit(NTP时间戳指绝对时间,相对1900年1月1日00:00:00经历的时间,单位为秒。完整NTP时间戳用64bits表示,左半32bits表示整数,右半32bits表示小数,一般为了紧凑,取中间32bits表示即可,这时整数与小数分别16bits表示)。记录着上次源SSRC_n发送SR的NTP时间,从收到的SR记录的NTP时间戳获取。如果没有收到SR,值为0。
delay since last SR (DLSR) : 32 bits。以1/65536(2^16)秒为单位。记录着上次接收到源SSRC_n发送的SR到当前发送RR的间隔时间。如果没有收到SR,值为0。
计算原理
前面简单介绍了RR中记录的时间信息,下面我们看下如何根据这些时间信息计算RTT。
上图中T_{0} 时刻媒体流发送端发送SR,SR中记录着send_time_ntp
。媒体流接收端t_{0} 时刻收到SR,t_{1} 时刻回复RR,RR中记录着:1)从收到的SR中获取的send_time_ntp
信息;2)接收端回复RR与收到SR的间隔时间delay_since_last_sr
。发送端在T_{1} 时刻收到回复的RR。可知:
delay\_since\_last\_sr = t_{1} - t_{0}\tag{1}
RTT = T_{1} - T_{0} - (t_{1} - t_{0})\tag{2}
RTT = receive\_time\_ntp - send\_time\_ntp - delay\_since\_last\_sr\tag{3}
代码导读
WebRTC中,发送端首先需要调用ModuleRtpRtcpImpl::OnSendingRtpFrame
,这样才能触发发送SR。
接着收到反馈的RR后才能进行RTT计算。WebRTC中发送端RTT计算代码位于RTCPReceiver::HandleReportBlock
。
主要代码流程如下:
ModuleRtpRtcpImpl :: IncomingRtcpPacket
↓
RTCPReceiver :: IncomingPacket
↓
RTCPReceiver :: ParseCompoundPacket
↓
RTCPReceiver :: HandleReceiverReport
↓
RTCPReceiver :: HandleReportBlock
RTCPReceiver::HandleReportBlock
中RTT计算代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int64_t rtt_ms = 0 ;
uint32_t send_time_ntp = report_block . last_sr ( ) ;
if ( send_time_ntp != 0 ) {
uint32_t delay_ntp = report_block . delay_since_last_sr ( ) ;
// Local NTP time.
uint32_t receive_time_ntp =
CompactNtp ( TimeMicrosToNtp ( last_received_rb_ . us ( ) ) ) ;
// RTT in 1/(2^16) seconds.
// 由于NTP时间戳中,整数以及小数部分分别用16bits表示,所以单位为1/(2^16)秒
uint32_t rtt_ntp = receive_time_ntp - delay_ntp - send_time_ntp ;
// Convert to 1/1000 seconds (milliseconds).
rtt_ms = CompactNtpRttToMs ( rtt_ntp ) ;
report_block_data -> AddRoundTripTimeSample ( rtt_ms ) ;
packet_information -> rtt_ms = rtt_ms ;
}
结合前面的公式,上面代码应该很好理解。最后得到一个ms表示的RTT,chrome://webrtc-internals/
中RTT的单位是秒。
媒体流接收端RTT计算
RTCP XR报文
WebRTC中媒体流接收端RTT的计算是根据XR中携带的时间信息。这里我们看下XR报文格式:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| V = 2 | P | reserved | PT = XR = 207 | length |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| SSRC |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
: report blocks :
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
时间信息存放在report blocks中。report blocks格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| BT | type - specific | block length |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
: type - specific block contents :
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
接收端RTT计算主要用到了Receiver Reference Time Report Block与DLRR Report Block这两种report blocks。
Receiver Reference Time Report Block(RRTR)
由媒体流接收端发送。报文格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| BT = 4 | reserved | block length = 2 |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| NTP timestamp , most significant word |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| NTP timestamp , least significant word |
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
NTP timestamp:64bits。记录着发送该Receiver Reference Time Report Block的NTP时间戳。
DLRR Report Block
由媒体流发送端发送。报文格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| BT = 5 | reserved | block length |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
| SSRC_1 ( SSRC of first receiver ) | sub -
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + block
| last RR ( LRR ) | 1
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
| delay since last RR ( DLRR ) |
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
| SSRC_2 ( SSRC of second receiver ) | sub -
+ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + block
: . . . : 2
+= += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += += +
last RR timestamp (LRR) : 32 bits。记录着上次源SSRC_n发送Receiver Reference Time Report Block的NTP时间,从收到的Receiver Reference Time Report Block记录的NTP时间戳获取。如果没有收到,值为0。
delay since last RR (DLRR) : 32 bits。以1/65536(2^16)秒为单位。记录着上次接收到源SSRC_n发送的Receiver Reference Time Report Block到当前发送DLRR Report Block的间隔时间。如果没有收到Receiver Reference Time Report Block,值为0。
计算原理
类似发送端RTT计算,只不过SR变为了Receiver Reference Time Report Block,由媒体流接收端发送。RR变为了DLRR Report Block,由媒体流发送端发送,最后在接收端计算。具体原理这里就不再啰嗦一次了。
代码导读
需要配置接收端ModuleRtpRtcpImpl
中RtpRtcpInterface::Configuration
的non_sender_rtt_measurement
开启XR。
这里以视频为例,配置开启方式如下:
WebRtcVideoReceiveChannel :: WebRtcVideoReceiveStream :: WebRtcVideoReceiveStream {
config_ . rtp . rtcp_xr . receiver_reference_time_report = HasRrtr ( codec . codec ) ;
}
const char kRtcpFbParamRrtr [ ] = "rrtr" ;
bool HasRrtr ( const Codec & codec ) {
return codec . HasFeedbackParam (
FeedbackParam ( kRtcpFbParamRrtr , kParamValueEmpty ) ) ;
}
也就是检查SDP中某编码器"a=rtcp-fb"是否含有"rrtr"。例如:
a = rtpmap : 96 VP8 / 90000
a = rtcp - fb : 96 goog - remb
a = rtcp - fb : 96 transport - cc
a = rtcp - fb : 96 ccm fir
a = rtcp - fb : 96 nack
a = rtcp - fb : 96 nack pli
a = rtcp - fb : 96 rrtr
最终这个receiver_reference_time_report
配置会传给RtpRtcpInterface::Configuration
中的non_sender_rtt_measurement
配置项,最后传到RTCPSender
中。
媒体流接收端首先发送RRTR,然后收到媒体流发送端反馈的DLRR Report Block进行RTT计算。WebRTC中接收端RTT计算代码位于RTCPReceiver::HandleXrDlrrReportBlock
。
主要代码流程如下:
ModuleRtpRtcpImpl :: IncomingRtcpPacket
↓
RTCPReceiver :: IncomingPacket
↓
RTCPReceiver :: ParseCompoundPacket
↓
RTCPReceiver :: HandleXr
↓
RTCPReceiver :: HandleXrDlrrReportBlock
RTCPReceiver::HandleXrDlrrReportBlock
中RTT计算代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// The send_time and delay_rr fields are in units of 1/2^16 sec.
uint32_t send_time_ntp = rti . last_rr ;
// RFC3611, section 4.5, LRR field discription states:
// If no such block has been received, the field is set to zero.
if ( send_time_ntp == 0 ) {
auto rtt_stats = non_sender_rtts_ . find ( sender_ssrc ) ;
if ( rtt_stats != non_sender_rtts_ . end ( ) ) {
rtt_stats -> second . Invalidate ( ) ;
}
return ;
}
uint32_t delay_ntp = rti . delay_since_last_rr ;
uint32_t now_ntp = CompactNtp ( clock_ -> CurrentNtpTime ( ) ) ;
uint32_t rtt_ntp = now_ntp - delay_ntp - send_time_ntp ;
TimeDelta rtt = CompactNtpRttToTimeDelta ( rtt_ntp ) ;
xr_rr_rtt_ = rtt ;
non_sender_rtts_ [ sender_ssrc ] . Update ( rtt ) ;
代码类似前面发送端RTT计算,就不分析了。
注意事项
WebRTC中RTCP XR总是附在SR或者RR后组成Compound RTCP包发送。
总结
本文简单介绍了RTT以及WebRTC中两种RTT的计算,RTT作为网络性能分析的一个重要指标,在WebRTC中扮演一个很重要的角色,至于具体用途,后面有空结合其它再分析。
参考
[1] RFC3550.https://tools.ietf.org/html/rfc3550.
[2] RFC3611.https://tools.ietf.org/html/rfc3611.
文章评论
为啥你ping google的RTT那么小
@6666 我是在我的国外服务器上操作的
谢谢大佬,解析的很透彻,受益匪浅。
我的理解,这套RTT算法设计得真的很妙:
1、对于双向传输,使用发送端RTT算法计算出一次round trip的时长作为RTT,满足双向传输的评估需求;
2、对于单向传输,使用接收端RTT算法计算出两次发送所需的时长作为RTT,既兼容了传统的RTT算法,又刚好满足单向传输的评估需求。
这种场景下作为接收端的服务端并不会发送SR,导致无法计算RTT——我看了下文中的图,为什么觉得应该时作为接收端不会发送RR,而不是SR。 是我理解错了还是笔误?
@chris 上行推流场景,我们服务端需要获取RTT值,也就是需要在媒体流接收端计算RTT,由于媒体是上行传输,服务端只能发送RR,计算是在发送SR端,当然无法计算RTT了
webrtc的统计信息中,有一个 currentRoundTripTime 信息。请问这个是 rrt吧? 服务器发送视频流,客户端只是接收,这个rrt 是否准确?
@Jerry 非常不准
@Unreal 请问为什么不准啊?
@Jerry 这个也是rtt,不过不是本文webrtc这种通过RTC/RTCP协议计算的,可以参考:https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime 属于某个IceCandidatePair的,通过ICE的stun检测计算得到
请教个问题 这个RTT时间很大 我怎么去debug是哪里出问题了 比如是是turn service的问题还是机器的问题
@fireyang 在问题机器先用普通Ping命令ping下turn服务器看下rtt多少,初步判断网络是否问题
用的是aws的 kinesis video stream 上海电信到us-west-2的 turn service ttl 大约0.5 开多个session 丢包很严重 不知道是不是因为跨海就是有这么高的延迟