Google Congestion control 的实现分析之一
Table of Contents
Overview
影响因素有 Packet loss, RTT 和 OWDV(One Way Delay Varion)
-
基于丢包的控制器:以丢包率, RTT 和 REMB 消息来估算一个目标发送码率
-
基于延迟的控制器:以包的到达信息,或者在接收方,或者在发送方接收反馈,估算一个最大的码率,然后传送给基于丢包的控制器
基于丢包的估算是保底的,丢包率大于 10% 就往下降码率,小于 2% 就往上升码率
基于延迟的控制器一开始用的是 Kalman filter, 后来改为 Trendline filter , 便于预测网络变化的趋势
Trendline filter 的输入参数有
Main interface
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 | 当网络状态估计更新时,还在开发中 |
// NetworkControllerInterface is implemented by network controllers. A network
// controller is a class that uses information about network state and traffic
// to estimate network parameters such as round trip time and bandwidth. Network
// controllers does not guarantee thread safety, the interface must be used in a
// non-concurrent fashion.
class NetworkControllerInterface {
public:
virtual ~NetworkControllerInterface() = default;
// Called when network availabilty changes. -- 当网络有效或无效时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkAvailability(NetworkAvailability) = 0;
// Called when the receiving or sending endpoint changes address. -- 当网络地址更改时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkRouteChange(NetworkRouteChange) = 0;
// Called periodically with a periodicy as specified by
// NetworkControllerFactoryInterface::GetProcessInterval. -- 定时回调,以检查网络
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnProcessInterval( ProcessInterval) = 0;
// Called when remotely calculated bitrate is received. -- 当收到 REMB RTCP 消息时回调
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnRemoteBitrateReport(RemoteBitrateReport) = 0;
// Called round trip time has been calculated by protocol specific mechanisms. -- 当 RTT 更改时回调(可通过 RTCP RR)
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnRoundTripTimeUpdate(RoundTripTimeUpdate) = 0;
// Called when a packet is sent on the network. -- 当发出一个 RTP 包时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnSentPacket(SentPacket) = 0;
// Called when a packet is received from the remote client. -- 当收到一个 RTP 包时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnReceivedPacket(ReceivedPacket) = 0;
// Called when the stream specific configuration has been updated. -- 当有流相关的配置更新时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnStreamsConfig(StreamsConfig) = 0;
// Called when target transfer rate constraints has been changed. -- 当目标速率约束更改时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTargetRateConstraints(TargetRateConstraints) = 0;
// Called when a protocol specific calculation of packet loss has been made. -- 当收到 TransportLossReport 时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTransportLossReport(TransportLossReport) = 0;
// Called with per packet feedback regarding receive time. -- 当收到 TransportPacketsFeedback 时
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnTransportPacketsFeedback(TransportPacketsFeedback) = 0;
// Called with network state estimate updates. -- 当网络状态估计更新时,还在开发中
ABSL_MUST_USE_RESULT virtual NetworkControlUpdate OnNetworkStateEstimate(NetworkStateEstimate) = 0;
};
Main Flow
Bandwidth Probe
- class ProbeController
- class ProbeBitrateEstimator
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
GoogCcNetworkController 是核心类
class NetworkControllerTestFixture {
public:
NetworkControllerTestFixture() : factory_() {}
std::unique_ptr<NetworkControllerInterface> CreateController() {
NetworkControllerConfig config = InitialConfig();
std::unique_ptr<NetworkControllerInterface> controller =
factory_.Create(config);
return controller;
}
private:
NetworkControllerConfig InitialConfig(
int starting_bandwidth_kbps = kInitialBitrateKbps,
int min_data_rate_kbps = 0,
int max_data_rate_kbps = 5 * kInitialBitrateKbps) {
NetworkControllerConfig config;
config.constraints.at_time = Timestamp::Millis(0);
config.constraints.min_data_rate =
DataRate::KilobitsPerSec(min_data_rate_kbps);
config.constraints.max_data_rate =
DataRate::KilobitsPerSec(max_data_rate_kbps);
config.constraints.starting_rate =
DataRate::KilobitsPerSec(starting_bandwidth_kbps);
config.event_log = &event_log_;
return config;
}
NiceMock<MockRtcEventLog> event_log_;
GoogCcNetworkControllerFactory factory_;
};
TEST(GoogCcNetworkControllerTest, InitializeTargetRateOnFirstProcessInterval) {
NetworkControllerTestFixture fixture;
std::unique_ptr<NetworkControllerInterface> controller =
fixture.CreateController();
NetworkControlUpdate update =
controller->OnProcessInterval({.at_time = Timestamp::Millis(123456)});
EXPECT_EQ(update.target_rate->target_rate, kInitialBitrate);
EXPECT_EQ(update.pacer_config->data_rate(),
kInitialBitrate * kDefaultPacingRate);
EXPECT_EQ(update.probe_cluster_configs[0].target_data_rate,
kInitialBitrate * 3);
EXPECT_EQ(update.probe_cluster_configs[1].target_data_rate,
kInitialBitrate * 5);
}
run the uni test case
./out/Default/modules_unittests --gtest_filter=GoogCcNetworkControllerTest.InitializeTargetRateOnFirstProcessInterval
(field_trial.cc:140): Setting field trial string:
Note: Google Test filter = GoogCcNetworkControllerTest.InitializeTargetRateOnFirstProcessInterval
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from GoogCcNetworkControllerTest
[ RUN ] GoogCcNetworkControllerTest.InitializeTargetRateOnFirstProcessInterval
(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
(delay_based_bwe.cc:301): BWE Setting start bitrate to: 60 kbps
PLOT 1 fraction_loss_%:0@- 123.456000 0.000000
PLOT 1 rtt_ms:0@- 123.456000 0.000000
PLOT 1 Target_bitrate_kbps:0@- 123.456000 60.000000
[ OK ] GoogCcNetworkControllerTest.InitializeTargetRateOnFirstProcessInterval (1 ms)
[----------] 1 test from GoogCcNetworkControllerTest (1 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (2 ms total)
[ PASSED ] 1 test.
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;
}
将 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="GoogCcNetworkControllerTest.*"
./out/Default/modules_unittests --gtest_filter=GoogCcNetworkControllerTest.UpdatesDelayBasedEstimate
./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_;
};
Unit test
./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
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 |