前面文章介绍了抖动,今天我们来看下chrome://webrtc-internals/
页面中的一个重要统计参数:往返时延(RTT)。
什么是RTT
往返时延(round-trip time,RTT) 是网络请求从起点到目的地然后再回到起点所花费的时长(不包括接收端的处理时间)。RTT是分析网络性能的一个重要指标,我们通常使用RTT来诊断网络连接的速度,可靠性以及拥塞程度。
ping命令是最常见的一种估计往返时延的方法。下面是ping Google的示例,底部显示了rtt的不同表示:最小,平均,最大,平均偏差。
1 2 3 4 5 6 7 8 9 10 11 |
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
。
主要代码流程如下:
1 2 3 4 5 6 7 8 9 |
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报文格式:
1 2 3 4 5 6 7 8 9 |
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格式如下:
1 2 3 4 5 6 7 |
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)
由媒体流接收端发送。报文格式如下:
1 2 3 4 5 6 7 8 9 |
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
由媒体流发送端发送。报文格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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。
这里以视频为例,配置开启方式如下:
1 2 3 4 5 6 7 8 9 |
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"。例如:
1 2 3 4 5 6 7 |
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
。
主要代码流程如下:
1 2 3 4 5 6 7 8 9 |
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 丢包很严重 不知道是不是因为跨海就是有这么高的延迟