webrtc带宽预测---发送端预测

SR RR

通过RR包,将丢包率信息回传给发送端,根据丢包率做发送端带宽估计。接收RTCP包,以及对应处理流程基本相同,具体流程如下:

void UdpTransportImpl::IncomingRTCPCallback
void UdpTransportImpl::IncomingRTCPFunction
void VideoChannelTransport::IncomingRTCPPacket
int ViENetworkImpl::ReceivedRTCPPacket
int32_t ViEChannel::ReceivedRTCPPacket
int ViEReceiver::ReceivedRTCPPacket
int ViEReceiver::InsertRTCPPacket
int32_t ModuleRtpRtcpImpl::IncomingRtcpPacket
void RTCPReceiver::TriggerCallbacksFromRTCPPacket
void OnReceivedRtcpReceiverReport
void BitrateControllerImpl::OnReceivedRtcpReceiverReport
void SendSideBandwidthEstimation::UpdateReceiverBlock
void SendSideBandwidthEstimation::UpdateEstimate

bitrate_controller模块接口是bitrate_controller_impl.h文件,通过这个文件就可以建立整个模块。

int ViEBaseImpl::CreateChannel
int ViEBaseImpl::CreateChannel
rtc::scoped_ptr<ChannelGroup> group
ChannelGroup::ChannelGroup
BitrateController* BitrateController::CreateBitrateController

其他的对象也都在此BitrateControllerImpl类中,从而将整个模块联系起来。

有一个线程一直会轮询这个更新带宽评估的函数,因为接收到反馈包之后,会进行评估,这里具体什么用意,后面看。

int32_t BitrateControllerImpl::Process()
void SendSideBandwidthEstimation::UpdateEstimate(int64_t now_ms)

设置保留带宽,用于音频

int Conductor::VideoSetStream
int ViERTP_RTCPImpl::SetReservedTransmitBitrate
bool ViEChannelManager::SetReservedTransmitBitrate
void BitrateControllerImpl::SetReservedBitrate

获取用于编码器的起始码率

int Conductor::VideoSetSendCodec
int ViECodecImpl::SetSendCodec
int32_t ViEEncoder::SetEncoder
bool BitrateControllerImpl::AvailableBandwidth

根据上一步的起始码率以及设置的起始码率,取较大值设置到SendSideBandwidthEstimation

int32_t ViEEncoder::SetEncoder
void BitrateControllerImpl::SetStartBitrate
void SendSideBandwidthEstimation::SetSendBitrate

设置最小最大码率,这个码率怎么来的,应该是上面设置下来的,后面看。

int32_t ViEEncoder::SetEncoder
void BitrateControllerImpl::SetMinMaxBitrate
void SendSideBandwidthEstimation::SetMinMaxBitrate

设置模式,然后决定是否使用RembSuppressor,这个做什么用,后面看。

int32_t ViEEncoder::SetEncoder
void BitrateControllerImpl::SetCodecMode(webrtc::VideoCodecMode mode) 
void RembSuppressor::SetEnabled(bool enabled)

计算RTT

照抄下面这篇博客,验证基本正确。

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

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
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秒.
为什么在获取本地时间不直接获取,而要先得到NTP再转为毫秒,应该是要时间统一都用NTP时间,而不是本地时间。

NTP相关计算

通过_clock->CurrentNtp()方法来获得当前时刻的ntp,其中lastReceivedRRNTPsecs 为秒,lastReceivedRRNTPfrac 为小数点后面部分。

  // 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
  void CurrentNtp(uint32_t& seconds, uint32_t& fractions) const override {
    timeval tv = CurrentTimeVal();
    double microseconds_in_seconds;
    Adjust(tv, &seconds, &microseconds_in_seconds);
    fractions = static_cast<uint32_t>(
        microseconds_in_seconds * kMagicNtpFractionalUnit + 0.5);
  }

通过CurrentTimeVal()方法的复杂计算之后,最后得到秒和微妙。

  timeval CurrentTimeVal() const override {
    const uint64_t FILETIME_1970 = 0x019db1ded53e8000;

    FILETIME StartTime;
    uint64_t Time;
    struct timeval tv;

    // We can't use query performance counter since they can change depending on
    // speed stepping.
    GetTime(&StartTime);

    Time = (((uint64_t) StartTime.dwHighDateTime) << 32) +
           (uint64_t) StartTime.dwLowDateTime;

    // Convert the hecto-nano second time to tv format.
    Time -= FILETIME_1970;

    tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000);
    tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10);
    return tv;
  }

Adjust()对秒和微秒做微调。

  static void Adjust(const timeval& tv, uint32_t* adjusted_s,
                     double* adjusted_us_in_s) {
    *adjusted_s = tv.tv_sec + kNtpJan1970;
    *adjusted_us_in_s = tv.tv_usec / 1e6;

    if (*adjusted_us_in_s >= 1) {
      *adjusted_us_in_s -= 1;
      ++*adjusted_s;
    } else if (*adjusted_us_in_s < -1) {
      *adjusted_us_in_s += 1;
      --*adjusted_s;
    }
  }
};

通过NtpToMs()将NTP时间转为毫秒

  int64_t receiveTimeMS = Clock::NtpToMs(lastReceivedRRNTPsecs,
                                         lastReceivedRRNTPfrac);
int64_t Clock::NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) {
  const double ntp_frac_ms = static_cast<double>(ntp_frac) / kNtpFracPerMs;
  return 1000 * static_cast<int64_t>(ntp_secs) +
      static_cast<int64_t>(ntp_frac_ms + 0.5);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • RTSP SDP RTP/RTCP 介绍应用层 RTSP、SDP; 传输层 RTP、TCP、UDP; 网络层 IP...
    Atom_Woo阅读 3,801评论 0 7
  • 音视频同步事关多媒体产品的最直观用户体验,是音视频媒体数据传输和渲染播放的最基本质量保证。音视频如果不同步,有可能...
    weizhenwei阅读 12,859评论 7 18
  • 音视频同步事关多媒体产品的最直观用户体验,是音视频媒体数据传输和渲染播放的最基本质量保证。音视频如果不同步,有可能...
    Monktan阅读 2,202评论 1 8
  • 一 前言 RF3550定义实时传输协议RTP和它的控制协议RTCP。RTP协议是Internet上针对流媒体传输的...
    weizhenwei阅读 15,798评论 5 31
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139