| /* |
| * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "absl/strings/string_view.h" |
| #include "api/transport/network_types.h" |
| #include "api/units/data_rate.h" |
| #include "api/units/data_size.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h" |
| #include "test/explicit_key_value_config.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| |
| RobustThroughputEstimatorSettings CreateRobustThroughputEstimatorSettings( |
| absl::string_view field_trial_string) { |
| test::ExplicitKeyValueConfig trials(field_trial_string); |
| RobustThroughputEstimatorSettings settings(&trials); |
| return settings; |
| } |
| |
| class FeedbackGenerator { |
| public: |
| std::vector<PacketResult> CreateFeedbackVector(size_t number_of_packets, |
| DataSize packet_size, |
| DataRate send_rate, |
| DataRate recv_rate) { |
| std::vector<PacketResult> packet_feedback_vector(number_of_packets); |
| for (size_t i = 0; i < number_of_packets; i++) { |
| packet_feedback_vector[i].sent_packet.send_time = send_clock_; |
| packet_feedback_vector[i].sent_packet.sequence_number = sequence_number_; |
| packet_feedback_vector[i].sent_packet.size = packet_size; |
| send_clock_ += packet_size / send_rate; |
| recv_clock_ += packet_size / recv_rate; |
| sequence_number_ += 1; |
| packet_feedback_vector[i].receive_time = recv_clock_; |
| } |
| return packet_feedback_vector; |
| } |
| |
| Timestamp CurrentReceiveClock() { return recv_clock_; } |
| |
| void AdvanceReceiveClock(TimeDelta delta) { recv_clock_ += delta; } |
| |
| void AdvanceSendClock(TimeDelta delta) { send_clock_ += delta; } |
| |
| private: |
| Timestamp send_clock_ = Timestamp::Millis(100000); |
| Timestamp recv_clock_ = Timestamp::Millis(10000); |
| uint16_t sequence_number_ = 100; |
| }; |
| |
| TEST(RobustThroughputEstimatorTest, DefaultEnabled) { |
| RobustThroughputEstimatorSettings settings = |
| CreateRobustThroughputEstimatorSettings(""); |
| EXPECT_TRUE(settings.enabled); |
| } |
| |
| TEST(RobustThroughputEstimatorTest, CanDisable) { |
| RobustThroughputEstimatorSettings settings = |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:false/"); |
| EXPECT_FALSE(settings.enabled); |
| } |
| |
| TEST(RobustThroughputEstimatorTest, InitialEstimate) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| // No estimate until the estimator has enough data. |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(9, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| EXPECT_FALSE(throughput_estimator.bitrate().has_value()); |
| |
| // Estimate once `required_packets` packets have been received. |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 1, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| |
| // Estimate remains stable when send and receive rates are stable. |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 15, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| |
| TEST(RobustThroughputEstimatorTest, EstimateAdapts) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| |
| // 1 second, 800kbps, estimate is stable. |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| for (int i = 0; i < 10; ++i) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| |
| // 1 second, 1600kbps, estimate increases |
| send_rate = DataRate::BytesPerSec(200000); |
| recv_rate = DataRate::BytesPerSec(200000); |
| for (int i = 0; i < 20; ++i) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_GE(throughput.value(), DataRate::BytesPerSec(100000)); |
| EXPECT_LE(throughput.value(), send_rate); |
| } |
| |
| // 1 second, 1600kbps, estimate is stable |
| for (int i = 0; i < 20; ++i) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| |
| // 1 second, 400kbps, estimate decreases |
| send_rate = DataRate::BytesPerSec(50000); |
| recv_rate = DataRate::BytesPerSec(50000); |
| for (int i = 0; i < 5; ++i) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_LE(throughput.value(), DataRate::BytesPerSec(200000)); |
| EXPECT_GE(throughput.value(), send_rate); |
| } |
| |
| // 1 second, 400kbps, estimate is stable |
| send_rate = DataRate::BytesPerSec(50000); |
| recv_rate = DataRate::BytesPerSec(50000); |
| for (int i = 0; i < 5; ++i) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| } |
| |
| TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(25000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| recv_rate.bytes_per_sec<double>(), |
| 0.05 * recv_rate.bytes_per_sec<double>()); // Allow 5% error |
| } |
| |
| TEST(RobustThroughputEstimatorTest, CappedBySendRate) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(50000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>(), |
| 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error |
| } |
| |
| TEST(RobustThroughputEstimatorTest, DelaySpike) { |
| FeedbackGenerator feedback_generator; |
| // This test uses a 500ms window to amplify the effect |
| // of a delay spike. |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true,window_duration:500ms/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| |
| // Delay spike. 25 packets sent, but none received. |
| feedback_generator.AdvanceReceiveClock(TimeDelta::Millis(250)); |
| |
| // Deliver all of the packets during the next 50 ms. (During this time, |
| // we'll have sent an additional 5 packets, so we need to receive 30 |
| // packets at 1000 bytes each in 50 ms, i.e. 600000 bytes per second). |
| recv_rate = DataRate::BytesPerSec(600000); |
| // Estimate should not drop. |
| for (int i = 0; i < 30; ++i) { |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 1, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>(), |
| 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error |
| } |
| |
| // Delivery at normal rate. When the packets received before the gap |
| // has left the estimator's window, the receive rate will be high, but the |
| // estimate should be capped by the send rate. |
| recv_rate = DataRate::BytesPerSec(100000); |
| for (int i = 0; i < 20; ++i) { |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 5, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>(), |
| 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error |
| } |
| } |
| |
| TEST(RobustThroughputEstimatorTest, HighLoss) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| |
| // 50% loss |
| for (size_t i = 0; i < packet_feedback.size(); i++) { |
| if (i % 2 == 1) { |
| packet_feedback[i].receive_time = Timestamp::PlusInfinity(); |
| } |
| } |
| |
| std::sort(packet_feedback.begin(), packet_feedback.end(), |
| PacketResult::ReceiveTimeOrder()); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>() / 2, |
| 0.05 * send_rate.bytes_per_sec<double>() / 2); // Allow 5% error |
| } |
| |
| TEST(RobustThroughputEstimatorTest, ReorderedFeedback) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| |
| std::vector<PacketResult> delayed_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 10, DataSize::Bytes(1000), send_rate, recv_rate); |
| |
| // Since we're missing some feedback, it's expected that the |
| // estimate will drop. |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_LT(throughput.value(), send_rate); |
| |
| // But it should completely recover as soon as we get the feedback. |
| throughput_estimator.IncomingPacketFeedbackVector(delayed_feedback); |
| throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| |
| // It should then remain stable (as if the feedbacks weren't reordered.) |
| for (int i = 0; i < 10; ++i) { |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 15, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| } |
| |
| TEST(RobustThroughputEstimatorTest, DeepReordering) { |
| FeedbackGenerator feedback_generator; |
| // This test uses a 500ms window to amplify the |
| // effect of reordering. |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true,window_duration:500ms/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> delayed_packets = |
| feedback_generator.CreateFeedbackVector(1, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| |
| for (int i = 0; i < 10; i++) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| |
| // Delayed packet arrives ~1 second after it should have. |
| // Since the window is 500 ms, the delayed packet was sent ~500 |
| // ms before the second oldest packet. However, the send rate |
| // should not drop. |
| delayed_packets.front().receive_time = |
| feedback_generator.CurrentReceiveClock(); |
| throughput_estimator.IncomingPacketFeedbackVector(delayed_packets); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>(), |
| 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error |
| |
| // Thoughput should stay stable. |
| for (int i = 0; i < 10; i++) { |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| ASSERT_TRUE(throughput.has_value()); |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| send_rate.bytes_per_sec<double>(), |
| 0.05 * send_rate.bytes_per_sec<double>()); // Allow 5% error |
| } |
| } |
| TEST(RobustThroughputEstimatorTest, ResetsIfReceiveClockChangeBackwards) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| EXPECT_EQ(throughput_estimator.bitrate(), send_rate); |
| |
| feedback_generator.AdvanceReceiveClock(TimeDelta::Seconds(-2)); |
| send_rate = DataRate::BytesPerSec(200000); |
| recv_rate = DataRate::BytesPerSec(200000); |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 20, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| EXPECT_EQ(throughput_estimator.bitrate(), send_rate); |
| } |
| |
| TEST(RobustThroughputEstimatorTest, StreamPausedAndResumed) { |
| FeedbackGenerator feedback_generator; |
| RobustThroughputEstimator throughput_estimator( |
| CreateRobustThroughputEstimatorSettings( |
| "WebRTC-Bwe-RobustThroughputEstimatorSettings/" |
| "enabled:true/")); |
| DataRate send_rate(DataRate::BytesPerSec(100000)); |
| DataRate recv_rate(DataRate::BytesPerSec(100000)); |
| |
| std::vector<PacketResult> packet_feedback = |
| feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), |
| send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| auto throughput = throughput_estimator.bitrate(); |
| EXPECT_TRUE(throughput.has_value()); |
| double expected_bytes_per_sec = 100 * 1000.0; |
| EXPECT_NEAR(throughput.value().bytes_per_sec<double>(), |
| expected_bytes_per_sec, |
| 0.05 * expected_bytes_per_sec); // Allow 5% error |
| |
| // No packets sent or feedback received for 60s. |
| feedback_generator.AdvanceSendClock(TimeDelta::Seconds(60)); |
| feedback_generator.AdvanceReceiveClock(TimeDelta::Seconds(60)); |
| |
| // Resume sending packets at the same rate as before. The estimate |
| // will initially be invalid, due to lack of recent data. |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 5, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| EXPECT_FALSE(throughput.has_value()); |
| |
| // But be back to the normal level once we have enough data. |
| for (int i = 0; i < 4; ++i) { |
| packet_feedback = feedback_generator.CreateFeedbackVector( |
| 5, DataSize::Bytes(1000), send_rate, recv_rate); |
| throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); |
| throughput = throughput_estimator.bitrate(); |
| EXPECT_EQ(throughput, send_rate); |
| } |
| } |
| |
| } // namespace webrtc |