拥塞控制技术的笔记三: TWCC 在 libwebrtc 中的实现
Posted on Sun 13 February 2022 in Journal
Abstract | 拥塞控制技术的笔记三: TWCC 在 libwebrtc 中的实现 |
---|---|
Authors | Walter Fan |
Category | learning note |
Status | WIP |
Updated | 2022-2-10 |
构建 libwebrtc
- 安装 Chromium 软件库工具.
参见 * WebRTC 开发依赖软件 webrtc-prerequisite-sw * `安装 WebRTC 开发工具 webrtc-depot-tools
- 下载 WebRTC 源码
$ mkdir webrtc-checkout
$ cd webrtc-checkout
$ fetch --nohooks webrtc
$ gclient sync --force
- 更新源码到你自己的分支
$ git checkout main
$ git pull origin main
$ gclient sync
$ git checkout my-branch
$ git merge main
- 构建
先要安装 ninja 构建工具 ninja-tool 这一构建工具, 通过它来生成构建脚本
在 Linux 系统上,比较简单的方法是运行 ./build/install-build-deps.sh
$ cd src
$ python build/util/lastchange.py build/util/LASTCHANGE
# generate project files using the defaults (Debug build)
$ gn gen out/Default
# clean all build artifacts in a directory but leave the current GN configuration untouched
$ gn clean out/Default
$ ninja -C out/Default
在 windows 系统上,建议安装 visual studio 和 windows 10 SDK
注意:
1)一定要在系统设置中选择 Windows SDK , 再选择修改,安装 debugging tool)
2)为了使用本地安装的 visual studio, 需要先设置一下环境变量 set DEPOT_TOOLS_WIN_TOOLCHAIN=0
gn gen --ide=vs out\Default
然后用 visual studio 打开 out\Default\all.sln
主要模块
拥塞控制的相关代码主要存在于 webrtc 代码的以下两个模块中
- modules/remote_bitrate_estimator 基于 REMB 的旧版本,基本已经废弃了,其中几个类在新版本中也有使用
- congestion_controller 基于 Transport Wide CC 的新版本,新旧版本中接收端的估算逻辑移到了发送端,其中的卡尔曼滤波算法也改成了线性回归算法
还有相关联的一些模块,例如:
- modules/pacing: 控制发送速率
- modules/rtp_rtcp: 支持 RTP 协议
- modules/video_coding: 控制编码速率
主要接口
NetworkControllerInterface 是拥塞控制的主要,其实现类是 GoogCcNetworkController
method | parameter | description |
---|---|---|
OnNetworkAvailability | NetworkAvailability | 当网络连接有效或无效时 |
OnNetworkRouteChange | NetworkRouteChange | 当网络地址更改时 |
OnProcessInterval | ProcessInterval | 定时回调,以检查网络 |
OnRemoteBitrateReport | RemoteBitrateReport | 当收到 REMB RTCP 消息时回调 |
OnRoundTripTimeUpdate | RoundTripTimeUpdate | 当 RTT 更改时回调(可通过 RTCP RR) |
OnSentPacket | SentPacket | 当发出一个 RTP 包时 |
OnReceivedPacket | ReceivedPacket | 当收到一个 RTP 包时 |
OnStreamsConfig | StreamsConfig | 当有媒体流相关的配置更新时 |
OnTargetRateConstraints | TargetRateConstraints | 当目标速率约束更改时 |
OnTransportLossReport | 当收到 TransportLossReport 时 | |
OnTransportPacketsFeedback | TransportPacketsFeedback | 当收到 TransportPacketsFeedback 时 |
OnNetworkStateEstimate | NetworkStateEstimate | 当网络状态估计更新时,还在开发中 |
主要流程
带宽探测 Bandwidth Probe
相关类有:
- class ProbeController 控制在什么时候如何进行带宽探测
- class ProbeBitrateEstimator 根据探测包的反馈结果进行带宽估算 主要方法有:
- HandleProbeAndEstimateBitrate
- FetchAndResetLastEstimatedBitrate
主要流程如下,详细分析另外找个时间再说
s=>start: start
e=>end: end
OnNetworkAvailability1=>operation: GoogCcNetworkController::OnNetworkAvailability
OnNetworkAvailability2=>operation: ProbeController::OnNetworkAvailability
is_available=>condition: is network available
InitiateExponentialProbing=>operation: ProbeController::InitiateExponentialProbing
InitiateProbing=>operation: ProbeController::InitiateProbing: 900k, 1.8m
s->OnNetworkAvailability1->OnNetworkAvailability2->is_available
is_available(yes)->InitiateExponentialProbing->InitiateProbing->e
is_available(no)->e
带宽估算 Bandwidth estimate
- GoogCcNetworkController 是核心类, ## 主要方法是 OnTransportPacketsFeedback 处理传输通道的 RTP 包的状态反馈报告
可以通过 webrtc library 自带的 peerconnection example 和一些单元测试来了解其流程.
在 WebRTC 的 test/scenario 模块中有一个称为 Scenario 的类, 它是一个拥有测试场景所有内容的类。 它创建并保存网络节点、呼叫客户端和媒体流。 它还提供了在运行时改变行为的方法。
由于它始终保持所创建组件的所有权,因此它通常返回的指针是不具有拥有仅的,只能由 Scenario 自己来保持其对象的生命,直到它被销毁。
对于接受配置结构体的方法,一般会提供一个修改函数接口。 这样测试者可以简单地覆盖部分默认的配置。
在发送端的流程大致有
1) RTCP message 接收后由 TransportFeedbackAdapter 解析
absl::optional<TransportPacketsFeedback> TransportFeedbackAdapter::ProcessTransportFeedback(
const rtcp::TransportFeedback& feedback,
Timestamp feedback_receive_time) {
if (feedback.GetPacketStatusCount() == 0) {
RTC_LOG(LS_INFO) << "Empty transport feedback packet received.";
return absl::nullopt;
}
TransportPacketsFeedback msg;
msg.feedback_time = feedback_receive_time;
msg.prior_in_flight = in_flight_.GetOutstandingData(network_route_);
msg.packet_feedbacks =
ProcessTransportFeedbackInner(feedback, feedback_receive_time);
if (msg.packet_feedbacks.empty())
return absl::nullopt;
auto it = history_.find(last_ack_seq_num_);
if (it != history_.end()) {
msg.first_unacked_send_time = it->second.sent.send_time;
}
msg.data_in_flight = in_flight_.GetOutstandingData(network_route_);
return msg;
}
2) 将 RTCP 消息转化为 TransportPacketsFeedback
struct TransportPacketsFeedback {
TransportPacketsFeedback();
TransportPacketsFeedback(const TransportPacketsFeedback& other);
~TransportPacketsFeedback();
Timestamp feedback_time = Timestamp::PlusInfinity();
Timestamp first_unacked_send_time = Timestamp::PlusInfinity();
DataSize data_in_flight = DataSize::Zero();
DataSize prior_in_flight = DataSize::Zero();
std::vector<PacketResult> packet_feedbacks;
// Arrival times for messages without send time information.
std::vector<Timestamp> sendless_arrival_times;
std::vector<PacketResult> ReceivedWithSendInfo() const;
std::vector<PacketResult> LostWithSendInfo() const;
std::vector<PacketResult> PacketsWithFeedback() const;
std::vector<PacketResult> SortedByReceiveTime() const;
};
可以跑这个单元测试来加深理解
# ./out/Default/modules_unittests --gtest_filter=TransportFeedbackAdapterTest.AdaptsFeedbackAndPopulatesSendTimes
class TransportFeedbackAdapterTest : public ::testing::Test {
public:
TransportFeedbackAdapterTest() : clock_(0) {}
virtual ~TransportFeedbackAdapterTest() {}
virtual void SetUp() {
adapter_.reset(new TransportFeedbackAdapter(&clock_));
}
virtual void TearDown() { adapter_.reset(); }
protected:
void OnReceivedEstimatedBitrate(uint32_t bitrate) {}
void OnReceivedRtcpReceiverReport(const ReportBlockList& report_blocks,
int64_t rtt,
int64_t now_ms) {}
void OnSentPacket(const PacketFeedback& packet_feedback) {
adapter_->AddPacket(kSsrc, packet_feedback.sequence_number,
packet_feedback.payload_size,
packet_feedback.pacing_info);
adapter_->OnSentPacket(packet_feedback.sequence_number,
packet_feedback.send_time_ms);
}
static constexpr uint32_t kSsrc = 8492;
SimulatedClock clock_;
std::unique_ptr<TransportFeedbackAdapter> adapter_;
};
3) Feedback 消息交由拥塞控制器来进行拥塞检测和带宽评估
基于延迟的带宽评估主要入口是下面这个方法 OnTransportPacketsFeedback(TransportPacketsFeedback report) 这个方法相当复杂,先把代码贴出来,详细流程需要再单独剖析
这个方法用到了若干个子类
- ProbleController, ProbeBitrateEstimatro 用来做初始化时,以及网络吞吐急剧变化的探测和带宽估计
- AcknowledgedBitrateEstimator 和 BitrateEstimator 用来计算网络上的吞吐量,用到了滑动窗口和卡尔曼滤波
- SendSideBandwidthEstimation 基于丢包进行带宽的评估, 它同时也参考了 Propagation RTT, 最后还要看下面 DelayBasedBwe 做的基于延迟的估算结果
- AlrDetector 处理了 Application Limited Region 的检测,并根据配置看要不要启用 Proble
- DelayBasedBwe 基于延迟的码率估算,通过指数移动平均 EWMV 和 Trendline filter 估算出带宽
- CongestionWindowPushbackController : 根据当前窗口内的数据用量,对目标码率做进一步的调整
NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(
TransportPacketsFeedback report) {
if (report.packet_feedbacks.empty()) {
// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard
// against building very large network queues.
return NetworkControlUpdate();
}
if (congestion_window_pushback_controller_) {
congestion_window_pushback_controller_->UpdateOutstandingData(
report.data_in_flight.bytes());
}
TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();
TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();
Timestamp max_recv_time = Timestamp::MinusInfinity();
std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();
for (const auto& feedback : feedbacks)
max_recv_time = std::max(max_recv_time, feedback.receive_time);
for (const auto& feedback : feedbacks) {
TimeDelta feedback_rtt =
report.feedback_time - feedback.sent_packet.send_time;
TimeDelta min_pending_time = feedback.receive_time - max_recv_time;
TimeDelta propagation_rtt = feedback_rtt - min_pending_time;
max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);
min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);
}
if (max_feedback_rtt.IsFinite()) {
feedback_max_rtts_.push_back(max_feedback_rtt.ms());
const size_t kMaxFeedbackRttWindow = 32;
if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)
feedback_max_rtts_.pop_front();
// TODO(srte): Use time since last unacknowledged packet.
bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time,
min_propagation_rtt);
}
if (packet_feedback_only_) {
if (!feedback_max_rtts_.empty()) {
int64_t sum_rtt_ms = std::accumulate(feedback_max_rtts_.begin(),
feedback_max_rtts_.end(), 0);
int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();
if (delay_based_bwe_)
delay_based_bwe_->OnRttUpdate(TimeDelta::Millis(mean_rtt_ms));
}
TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();
for (const auto& packet_feedback : feedbacks) {
TimeDelta pending_time = packet_feedback.receive_time - max_recv_time;
TimeDelta rtt = report.feedback_time -
packet_feedback.sent_packet.send_time - pending_time;
// Value used for predicting NACK round trip time in FEC controller.
feedback_min_rtt = std::min(rtt, feedback_min_rtt);
}
if (feedback_min_rtt.IsFinite()) {
bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);
}
expected_packets_since_last_loss_update_ +=
report.PacketsWithFeedback().size();
for (const auto& packet_feedback : report.PacketsWithFeedback()) {
if (!packet_feedback.IsReceived())
lost_packets_since_last_loss_update_ += 1;
}
if (report.feedback_time > next_loss_update_) {
next_loss_update_ = report.feedback_time + kLossUpdateInterval;
bandwidth_estimation_->UpdatePacketsLost(
lost_packets_since_last_loss_update_,
expected_packets_since_last_loss_update_, report.feedback_time);
expected_packets_since_last_loss_update_ = 0;
lost_packets_since_last_loss_update_ = 0;
}
}
absl::optional<int64_t> alr_start_time =
alr_detector_->GetApplicationLimitedRegionStartTime();
if (previously_in_alr_ && !alr_start_time.has_value()) {
int64_t now_ms = report.feedback_time.ms();
acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);
probe_controller_->SetAlrEndedTimeMs(now_ms);
}
previously_in_alr_ = alr_start_time.has_value();
acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(
report.SortedByReceiveTime());
auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate();
bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate,
report.feedback_time);
bandwidth_estimation_->IncomingPacketFeedbackVector(report);
for (const auto& feedback : report.SortedByReceiveTime()) {
if (feedback.sent_packet.pacing_info.probe_cluster_id !=
PacedPacketInfo::kNotAProbe) {
probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback);
}
}
if (network_estimator_) {
network_estimator_->OnTransportPacketsFeedback(report);
auto prev_estimate = estimate_;
estimate_ = network_estimator_->GetCurrentEstimate();
// TODO(srte): Make OnTransportPacketsFeedback signal whether the state
// changed to avoid the need for this check.
if (estimate_ && (!prev_estimate || estimate_->last_feed_time !=
prev_estimate->last_feed_time)) {
event_log_->Log(std::make_unique<RtcEventRemoteEstimate>(
estimate_->link_capacity_lower, estimate_->link_capacity_upper));
}
}
absl::optional<DataRate> probe_bitrate =
probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();
if (ignore_probes_lower_than_network_estimate_ && probe_bitrate &&
estimate_ && *probe_bitrate < delay_based_bwe_->last_estimate() &&
*probe_bitrate < estimate_->link_capacity_lower) {
probe_bitrate.reset();
}
if (limit_probes_lower_than_throughput_estimate_ && probe_bitrate &&
acknowledged_bitrate) {
// Limit the backoff to something slightly below the acknowledged
// bitrate. ("Slightly below" because we want to drain the queues
// if we are actually overusing.)
// The acknowledged bitrate shouldn't normally be higher than the delay
// based estimate, but it could happen e.g. due to packet bursts or
// encoder overshoot. We use std::min to ensure that a probe result
// below the current BWE never causes an increase.
DataRate limit =
std::min(delay_based_bwe_->last_estimate(),
*acknowledged_bitrate * kProbeDropThroughputFraction);
probe_bitrate = std::max(*probe_bitrate, limit);
}
NetworkControlUpdate update;
bool recovered_from_overuse = false;
bool backoff_in_alr = false;
DelayBasedBwe::Result result;
result = delay_based_bwe_->IncomingPacketFeedbackVector(
report, acknowledged_bitrate, probe_bitrate, estimate_,
alr_start_time.has_value());
if (result.updated) {
if (result.probe) {
bandwidth_estimation_->SetSendBitrate(result.target_bitrate,
report.feedback_time);
}
// Since SetSendBitrate now resets the delay-based estimate, we have to
// call UpdateDelayBasedEstimate after SetSendBitrate.
bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time,
result.target_bitrate);
// Update the estimate in the ProbeController, in case we want to probe.
MaybeTriggerOnNetworkChanged(&update, report.feedback_time);
}
recovered_from_overuse = result.recovered_from_overuse;
backoff_in_alr = result.backoff_in_alr;
if (recovered_from_overuse) {
probe_controller_->SetAlrStartTimeMs(alr_start_time);
auto probes = probe_controller_->RequestProbe(report.feedback_time.ms());
update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
probes.begin(), probes.end());
} else if (backoff_in_alr) {
// If we just backed off during ALR, request a new probe.
auto probes = probe_controller_->RequestProbe(report.feedback_time.ms());
update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),
probes.begin(), probes.end());
}
// No valid RTT could be because send-side BWE isn't used, in which case
// we don't try to limit the outstanding packets.
if (rate_control_settings_.UseCongestionWindow() &&
max_feedback_rtt.IsFinite()) {
UpdateCongestionWindowSize();
}
if (congestion_window_pushback_controller_ && current_data_window_) {
congestion_window_pushback_controller_->SetDataWindow(
*current_data_window_);
} else {
update.congestion_window = current_data_window_;
}
return update;
}
拥塞控制相关的单元测试
$ ./modules_unittests --gtest_filter="GoogCc*" --gtest_output="xml:goog-cc-ut-report.xml"
$ gtest2html.py --input=./goog-cc-ut-report.xml --output=goog-cc-ut-report.md
gtest2html.py 是我写的一个 python 脚本,将测试结果由 xml 转化成下面这样的 markdown 格式
Test suite: GoogCcNetworkControllerTest
- tests=6, failures=0, errors=0, disabled=0, time=0.076
# | suite | case | time | result |
---|---|---|---|---|
1 | GoogCcNetworkControllerTest | InitializeTargetRateOnFirstProcessInterval | 0.016 | pass |
2 | GoogCcNetworkControllerTest | ReactsToChangedNetworkConditions | 0 | pass |
3 | GoogCcNetworkControllerTest | OnNetworkRouteChanged | 0 | pass |
4 | GoogCcNetworkControllerTest | ProbeOnRouteChange | 0 | pass |
5 | GoogCcNetworkControllerTest | UpdatesDelayBasedEstimate | 0.054 | pass |
6 | GoogCcNetworkControllerTest | PaceAtMaxOfLowerLinkCapacityAndBwe | 0 | pass |
Test suite: GoogCcScenario
- tests=20, failures=0, errors=0, disabled=0, time=22.666
# | suite | case | time | result |
---|---|---|---|---|
7 | GoogCcScenario | CongestionWindowPushbackOnNetworkDelay | 0.509 | pass |
8 | GoogCcScenario | CongestionWindowPushbackDropFrameOnNetworkDelay | 0.432 | pass |
9 | GoogCcScenario | PaddingRateLimitedByCongestionWindowInTrial | 0.366 | pass |
10 | GoogCcScenario | LimitsToFloorIfRttIsHighInTrial | 0.255 | pass |
11 | GoogCcScenario | UpdatesTargetRateBasedOnLinkCapacity | 2.233 | pass |
12 | GoogCcScenario | StableEstimateDoesNotVaryInSteadyState | 1.559 | pass |
13 | GoogCcScenario | LossBasedControlUpdatesTargetRateBasedOnLinkCapacity | 2.23 | pass |
14 | GoogCcScenario | LossBasedControlDoesModestBackoffToHighLoss | 3.106 | pass |
15 | GoogCcScenario | LossBasedRecoversFasterAfterCrossInducedLoss | 5.93 | pass |
16 | GoogCcScenario | LossBasedEstimatorCapsRateAtModerateLoss | 1.211 | pass |
17 | GoogCcScenario | MaintainsLowRateInSafeResetTrial | 0.016 | pass |
18 | GoogCcScenario | CutsHighRateInSafeResetTrial | 0.015 | pass |
19 | GoogCcScenario | DetectsHighRateInSafeResetTrial | 0.079 | pass |
20 | GoogCcScenario | TargetRateReducedOnPacingBufferBuildupInTrial | 0.169 | pass |
21 | GoogCcScenario | NoBandwidthTogglingInLossControlTrial | 0.082 | pass |
22 | GoogCcScenario | NoRttBackoffCollapseWhenVideoStops | 0.071 | pass |
23 | GoogCcScenario | NoCrashOnVeryLateFeedback | 2.252 | pass |
24 | GoogCcScenario | IsFairToTCP | 0.289 | pass |
25 | GoogCcScenario | FastRampupOnRembCapLifted | 1.077 | pass |
26 | GoogCcScenario | SlowRampupOnRembCapLiftedWithFieldTrial | 0.775 | pass |
测试用例比较多,先看看下面这个比较简单的用例
./out/Default/modules_unittests --gtest_filter=GoogCcNetworkControllerTest.UpdatesDelayBasedEstimate --gtest_output="xml:goog_cc_ut.xml" > goog_cc_ut.log 2>&1
代码如下,其实挺简单
-
调用 PacketTransmissionAndFeedbackBlock, 也就是发送包并接收反馈消息,反馈的延迟为 0, 时长为 6000 ms 1.1 创建并发送RTP 包,调用 controller->OnSentPacket 1.2 构造反馈包,并喂给拥塞控制器,调用 update = controller->OnTransportPacketsFeedback(feedback); 1.3 再过 50ms 调用 controller->OnProcessInterval(), 取出 target_bitrate, 也就是估算出的带宽
-
调用 PacketTransmissionAndFeedbackBlock, 也就是发送包并接收反馈消息,反馈的延迟为 50ms, 时长为 6000 ms
-
检查第二次估算的带宽 bitrate , 它一定是小于第一次估算的延迟
而 OnTransportPacketsFeedback() 和 OnProcessInterval() 就是我们需要重点关注的方法
// Bandwidth estimation is updated when feedbacks are received.
// Feedbacks which show an increasing delay cause the estimation to be reduced.
TEST(GoogCcNetworkControllerTest, UpdatesDelayBasedEstimate) {
NetworkControllerTestFixture fixture;
std::unique_ptr<NetworkControllerInterface> controller =
fixture.CreateController();
const int64_t kRunTimeMs = 6000;
Timestamp current_time = Timestamp::Millis(123);
// The test must run and insert packets/feedback long enough that the
// BWE computes a valid estimate. This is first done in an environment which
// simulates no bandwidth limitation, and therefore not built-up delay.
absl::optional<DataRate> target_bitrate_before_delay =
PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 0,
current_time);
ASSERT_TRUE(target_bitrate_before_delay.has_value());
// Repeat, but this time with a building delay, and make sure that the
// estimation is adjusted downwards.
absl::optional<DataRate> target_bitrate_after_delay =
PacketTransmissionAndFeedbackBlock(controller.get(), kRunTimeMs, 50,
current_time);
EXPECT_LT(*target_bitrate_after_delay, *target_bitrate_before_delay);
}
// Simulate sending packets and receiving transport feedback during
// `runtime_ms`.
absl::optional<DataRate> PacketTransmissionAndFeedbackBlock(
NetworkControllerInterface* controller,
int64_t runtime_ms,
int64_t delay,
Timestamp& current_time) {
NetworkControlUpdate update;
absl::optional<DataRate> target_bitrate;
int64_t delay_buildup = 0;
int64_t start_time_ms = current_time.ms();
while (current_time.ms() - start_time_ms < runtime_ms) {
constexpr size_t kPayloadSize = 1000;
PacketResult packet =
CreatePacketResult(current_time + TimeDelta::Millis(delay_buildup),
current_time, kPayloadSize, PacedPacketInfo());
delay_buildup += delay;
update = controller->OnSentPacket(packet.sent_packet);
if (update.target_rate) {
target_bitrate = update.target_rate->target_rate;
}
TransportPacketsFeedback feedback;
feedback.feedback_time = packet.receive_time;
feedback.packet_feedbacks.push_back(packet);
update = controller->OnTransportPacketsFeedback(feedback);
if (update.target_rate) {
target_bitrate = update.target_rate->target_rate;
}
current_time += TimeDelta::Millis(50);
update = controller->OnProcessInterval({.at_time = current_time});
if (update.target_rate) {
target_bitrate = update.target_rate->target_rate;
}
}
return target_bitrate;
}
运行结果如下
[ RUN ] GoogCcNetworkControllerTest.UpdatesDelayBasedEstimate
(alr_experiment.cc:79): Using ALR experiment settings: pacing factor: 1, max pacer queue length: 2875, ALR bandwidth usage percent: 80, ALR start budget level percent: 40, ALR end budget level percent: -60, ALR experiment group ID: 3
(trendline_estimator.cc:185): Using Trendline filter for delay change estimation with settings sort:false,cap:false,beginning_packets:7,end_packets:7,cap_uncertainty:0,window_size:20 and no network state predictor
(trendline_estimator.cc:185): Using Trendline filter for delay change estimation with settings sort:false,cap:false,beginning_packets:7,end_packets:7,cap_uncertainty:0,window_size:20 and no network state predictor
(aimd_rate_control.cc:112): Using aimd rate control with back off factor 0.85
(delay_based_bwe.cc:88): Initialized DelayBasedBwe with separate audio overuse detectionenabled:false,packet_threshold:10,time_threshold:1 s and alr limited backoff disabled
(trendline_estimator.cc:185): Using Trendline filter for delay change estimation with settings sort:false,cap:false,beginning_packets:7,end_packets:7,cap_uncertainty:0,window_size:20 and no network state predictor
(trendline_estimator.cc:185): Using Trendline filter for delay change estimation with settings sort:false,cap:false,beginning_packets:7,end_packets:7,cap_uncertainty:0,window_size:20 and no network state predictor
(delay_based_bwe.cc:301): BWE Setting start bitrate to: 60 kbps
PLOT 1 fraction_loss_%:0@- 0.123000 0.000000
PLOT 1 rtt_ms:0@- 0.123000 0.000000
PLOT 1 Target_bitrate_kbps:0@- 0.123000 5.000000
PLOT 1 fraction_loss_%:0@- 0.173000 0.000000
PLOT 1 rtt_ms:0@- 0.173000 0.000000
PLOT 1 Target_bitrate_kbps:0@- 0.173000 60.000000
...
PLOT 1 rtt_ms:0@- 12.073000 0.000000
PLOT 1 Target_bitrate_kbps:0@- 12.073000 41.000000
PLOT 1 acknowledged_bitrate:0@- 18.023000 93079.859375
PLOT 1 accumulated_delay_ms:0@- 18.023000 5900.000000
PLOT 1 smoothed_delay_ms:0@- 18.023000 5450.001794
PLOT 1 trendline_slope:0@- 18.023000 0.499994
PLOT 1 T:0@- 18.023000 119.998624
PLOT 1 threshold:0@- 18.023000 119.998445
PLOT 1 fraction_loss_%:0@- 12.123000 0.000000
PLOT 1 rtt_ms:0@- 12.123000 0.000000
PLOT 1 Target_bitrate_kbps:0@- 12.123000 41.000000
[ OK ] GoogCcNetworkControllerTest.UpdatesDelayBasedEstimate (11 ms)
[----------] 1 test from GoogCcNetworkControllerTest (11 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (12 ms total)
[ PASSED ] 1 test.
以上的 PLOT 是由 宏 BWE_TEST_LOGGING_PLOT 调用
#define BWE_TEST_LOGGING_PLOT(figure, name, time, value) \
do { \
__BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \
static_cast<int64_t>(time), true); \
webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value); \
} while (0)
void Logging::Plot(int figure, const std::string& name, double value) {
Plot(figure, name, value, 0, "-");
}
void Logging::Plot(int figure,
const std::string& name,
double value,
uint32_t ssrc,
const std::string& alg_name) {
MutexLock lock(&mutex_);
ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId());
RTC_DCHECK(it != thread_map_.end());
const State& state = it->second.stack.top();
if (state.enabled) {
printf("PLOT\t%d\t%s:%" PRIu32 "@%s\t%f\t%f\n", figure, name.c_str(), ssrc,
alg_name.c_str(), state.timestamp_ms * 0.001, value);
}
}
第 1 列:PLOT 关键字 第 2 列:figure number 第 3 列:figure name + ssrc + alg name 第 4 列:timestamp 第 5 列:value
这样我们就可以根据这种格式绘制各种各样的图表了