[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)
[webrtc] rtcp模块中rtt时间计算的更多相关文章
- python requests模块中返回时间elapsed解析
一.问题: Python 中requests库在发送http请求时相当方便好用,但在使用时一直受一个问题困扰,怎么才能查看请求时长呢? 自己写时间函数再相减?NO,这个方法肯定不行. 二.解决: 好吧 ...
- 关于js中的时间——计算时间差等
获取当前(系统)时间: var NowDate= new Date(); // 获取当前日期时间 // 输出为: Wed May 03 2017 14:52:08 GMT+0800 (中国标准时间) ...
- informix中的时间计算
今天看SUN服务器是的mail(vi /var/mail/xxxuser),发现定时任务上的一些存储过程执行有错误,其中有一个错误是long transaction,长事务错误,到数据库一查,天哪 ...
- c++时间计算模块
c++时间计算模块 可用于计算代码运行耗时.计算代码运行时间线(比如处理与运行时间相关函数). 该模块从实际项目中产生,使用方式仁者见仁智者见智,设计思想可供参考. 源码: //author: cai ...
- [ Python入门教程 ] Python中日期时间datetime模块使用实例
Python中datetime模块提供强大易用的日期处理功能,用于记录程序操作或修改时间.时间计算.日志时间显示等功能.datatime模块重新封装了time模块,提供的类包括date.time.da ...
- TCP中RTT的测量和RTO的计算
https://blog.csdn.net/zhangskd/article/details/7196707 tcp传输往返时间是指:发送方发送tcp断开时, 到发送方接收到改段立即响应的所耗费的时间 ...
- Delphi中获取Unix时间戳及注意事项(c语言中time()是按格林威治时间计算的,比北京时间多了8小时)
uses DateUtils;DateTimeToUnix(Now) 可以转换到unix时间,但是注意的是,它得到的时间比c语言中time()得到的时间大了8*60*60这是因为Now是当前时区的时间 ...
- NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用
NET MVC全局异常处理(一) 目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...
- mysql中时间计算函数SQL DATE_SUB()用法
本文为博主原创,未经允许不得转载: 在写sql的时候,经常要在sql中传值时间,对时间进行计算并过滤.之前都是将时间在后台计算好,直接传值给sql, 今天发现,有一个更方便的sql函数,可以简化很多代 ...
随机推荐
- C#打印日志的小技巧
public static void Log(params System.Object[] message) { string str = ""; if (message == n ...
- C#和MatLab的混合编程(充分利用二者的优势)
C#和MatLab的混合编程,充分利用了winform的直观显示和matlab的强大计算能力.在此以一个小例子的形式给大家讲述一下二者混合编程的实现. 一.软件的配置说明 C#版本:VS2010:Ma ...
- UVaLive 3353 Optimal Bus Route Design (最小费用流)
题意:给定一个 n 个点的有向带权图,让你找若干个圈,使得每个结点恰好属于一个圈,并且总长度尽量小. 析:一开始想的是先缩点,先用DP,来求... 题解给的是最小费用流或者是最佳完全匹配,其实都是一样 ...
- WriteableBitmap(三) 扩展
backbuffer使用您在创建WriteableBitmap时指定的像素格式,还有一个BackBufferStride属性,您可以使用它来创建一个合适的存储映射函数. 添加一些方法来设置和获取特定情 ...
- POI解析Excel文件
@Test public void test1() throws FileNotFoundException, IOException{ HSSFWorkbook workbook = new HSS ...
- 构建Maven项目自动下载jar包
使用Maven 自动下载jar包 右键单击项目,将项目 转换成Maven 项目 然后进去Maven官网 http://mvnrepository.com/ 这里有大量的jar包供我们使用,比如我现在要 ...
- 测试与发布(Alpha版本)——小谷围驻广东某工业719电竞大队
测试与发布(Alpha版本)--小谷围驻广东某工业719电竞大队 一.引言 1.需求规格说明书: https://www.cnblogs.com/TaoTaoLV1/p/9819913.html 2. ...
- EBS R12 探索之路【EBS 经典SQL分享】
http://bbs.erp100.com/thread-251217-1-1.html 1. 查询EBS 系统在线人数 SELECT U.USER_NAME ,APP.APPLICATION_SHO ...
- 查看Linux服务器被映射的公网ip
查看Linux服务器被映射的公网ip 现在云服务器非常流行,不仅企业甚至是个人都可能拥有自己的云服务器,但是目前的云服务器厂商提供的公网IP大都是映射而来,所以在Linux服务器上执行ifconf ...
- 我要总结基本书 .net稍微有些深度的书籍看看
1. 你必须知道的.NET 2. C# in depth 3.C#并发编程经典实例 4.ASP.NET MVC 4框架揭秘 5.NET最佳实践 6..NET探秘 .NET安全编程 .NET企业服务框架 ...