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

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: WebRTC | 程序人生