| /* |
| * Copyright (c) 2018 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. |
| */ |
| // Based on the Quic implementation in Chromium. |
| |
| #include "modules/congestion_controller/bbr/bandwidth_sampler.h" |
| |
| #include <algorithm> |
| |
| #include "rtc_base/logging.h" |
| |
| namespace webrtc { |
| namespace bbr { |
| namespace { |
| constexpr int64_t kMaxTrackedPackets = 10000; |
| } |
| |
| BandwidthSampler::BandwidthSampler() |
| : total_data_sent_(DataSize::Zero()), |
| total_data_acked_(DataSize::Zero()), |
| total_data_sent_at_last_acked_packet_(DataSize::Zero()), |
| last_acked_packet_sent_time_(), |
| last_acked_packet_ack_time_(), |
| last_sent_packet_(0), |
| is_app_limited_(false), |
| end_of_app_limited_phase_(0), |
| connection_state_map_() {} |
| |
| BandwidthSampler::~BandwidthSampler() {} |
| |
| void BandwidthSampler::OnPacketSent(Timestamp sent_time, |
| int64_t packet_number, |
| DataSize data_size, |
| DataSize data_in_flight) { |
| last_sent_packet_ = packet_number; |
| |
| total_data_sent_ += data_size; |
| |
| // If there are no packets in flight, the time at which the new transmission |
| // opens can be treated as the A_0 point for the purpose of bandwidth |
| // sampling. This underestimates bandwidth to some extent, and produces some |
| // artificially low samples for most packets in flight, but it provides with |
| // samples at important points where we would not have them otherwise, most |
| // importantly at the beginning of the connection. |
| if (data_in_flight.IsZero()) { |
| last_acked_packet_ack_time_ = sent_time; |
| total_data_sent_at_last_acked_packet_ = total_data_sent_; |
| |
| // In this situation ack compression is not a concern, set send rate to |
| // effectively infinite. |
| last_acked_packet_sent_time_ = sent_time; |
| } |
| |
| if (!connection_state_map_.IsEmpty() && |
| packet_number > |
| connection_state_map_.last_packet() + kMaxTrackedPackets) { |
| RTC_LOG(LS_WARNING) |
| << "BandwidthSampler in-flight packet map has exceeded maximum " |
| "number " |
| "of tracked packets."; |
| } |
| |
| bool success = |
| connection_state_map_.Emplace(packet_number, sent_time, data_size, *this); |
| if (!success) |
| RTC_LOG(LS_WARNING) << "BandwidthSampler failed to insert the packet " |
| "into the map, most likely because it's already " |
| "in it."; |
| } |
| |
| BandwidthSample BandwidthSampler::OnPacketAcknowledged(Timestamp ack_time, |
| int64_t packet_number) { |
| ConnectionStateOnSentPacket* sent_packet_pointer = |
| connection_state_map_.GetEntry(packet_number); |
| if (sent_packet_pointer == nullptr) { |
| return BandwidthSample(); |
| } |
| BandwidthSample sample = |
| OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer); |
| connection_state_map_.Remove(packet_number); |
| return sample; |
| } |
| |
| BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( |
| Timestamp ack_time, |
| int64_t packet_number, |
| const ConnectionStateOnSentPacket& sent_packet) { |
| total_data_acked_ += sent_packet.size; |
| total_data_sent_at_last_acked_packet_ = sent_packet.total_data_sent; |
| last_acked_packet_sent_time_ = sent_packet.sent_time; |
| last_acked_packet_ack_time_ = ack_time; |
| |
| // Exit app-limited phase once a packet that was sent while the connection is |
| // not app-limited is acknowledged. |
| if (is_app_limited_ && packet_number > end_of_app_limited_phase_) { |
| is_app_limited_ = false; |
| } |
| |
| // There might have been no packets acknowledged at the moment when the |
| // current packet was sent. In that case, there is no bandwidth sample to |
| // make. |
| if (!sent_packet.last_acked_packet_sent_time || |
| !sent_packet.last_acked_packet_ack_time) { |
| return BandwidthSample(); |
| } |
| |
| // Infinite rate indicates that the sampler is supposed to discard the |
| // current send rate sample and use only the ack rate. |
| DataRate send_rate = DataRate::Infinity(); |
| if (sent_packet.sent_time > *sent_packet.last_acked_packet_sent_time) { |
| DataSize sent_delta = sent_packet.total_data_sent - |
| sent_packet.total_data_sent_at_last_acked_packet; |
| TimeDelta time_delta = |
| sent_packet.sent_time - *sent_packet.last_acked_packet_sent_time; |
| send_rate = sent_delta / time_delta; |
| } |
| |
| // During the slope calculation, ensure that ack time of the current packet is |
| // always larger than the time of the previous packet, otherwise division by |
| // zero or integer underflow can occur. |
| if (ack_time <= *sent_packet.last_acked_packet_ack_time) { |
| RTC_LOG(LS_WARNING) |
| << "Time of the previously acked packet is larger than the time " |
| "of the current packet."; |
| return BandwidthSample(); |
| } |
| DataSize ack_delta = |
| total_data_acked_ - sent_packet.total_data_acked_at_the_last_acked_packet; |
| TimeDelta time_delta = ack_time - *sent_packet.last_acked_packet_ack_time; |
| DataRate ack_rate = ack_delta / time_delta; |
| |
| BandwidthSample sample; |
| sample.bandwidth = std::min(send_rate, ack_rate); |
| // Note: this sample does not account for delayed acknowledgement time. This |
| // means that the RTT measurements here can be artificially high, especially |
| // on low bandwidth connections. |
| sample.rtt = ack_time - sent_packet.sent_time; |
| // A sample is app-limited if the packet was sent during the app-limited |
| // phase. |
| sample.is_app_limited = sent_packet.is_app_limited; |
| return sample; |
| } |
| |
| void BandwidthSampler::OnPacketLost(int64_t packet_number) { |
| connection_state_map_.Remove(packet_number); |
| } |
| |
| void BandwidthSampler::OnAppLimited() { |
| is_app_limited_ = true; |
| end_of_app_limited_phase_ = last_sent_packet_; |
| } |
| |
| void BandwidthSampler::RemoveObsoletePackets(int64_t least_unacked) { |
| while (!connection_state_map_.IsEmpty() && |
| connection_state_map_.first_packet() < least_unacked) { |
| connection_state_map_.Remove(connection_state_map_.first_packet()); |
| } |
| } |
| |
| DataSize BandwidthSampler::total_data_acked() const { |
| return total_data_acked_; |
| } |
| |
| bool BandwidthSampler::is_app_limited() const { |
| return is_app_limited_; |
| } |
| |
| int64_t BandwidthSampler::end_of_app_limited_phase() const { |
| return end_of_app_limited_phase_; |
| } |
| |
| BandwidthSampler::ConnectionStateOnSentPacket::ConnectionStateOnSentPacket( |
| Timestamp sent_time, |
| DataSize size, |
| const BandwidthSampler& sampler) |
| : sent_time(sent_time), |
| size(size), |
| total_data_sent(sampler.total_data_sent_), |
| total_data_sent_at_last_acked_packet( |
| sampler.total_data_sent_at_last_acked_packet_), |
| last_acked_packet_sent_time(sampler.last_acked_packet_sent_time_), |
| last_acked_packet_ack_time(sampler.last_acked_packet_ack_time_), |
| total_data_acked_at_the_last_acked_packet(sampler.total_data_acked_), |
| is_app_limited(sampler.is_app_limited_) {} |
| |
| BandwidthSampler::ConnectionStateOnSentPacket::ConnectionStateOnSentPacket() |
| : sent_time(Timestamp::MinusInfinity()), |
| size(DataSize::Zero()), |
| total_data_sent(DataSize::Zero()), |
| total_data_sent_at_last_acked_packet(DataSize::Zero()), |
| last_acked_packet_sent_time(), |
| last_acked_packet_ack_time(), |
| total_data_acked_at_the_last_acked_packet(DataSize::Zero()), |
| is_app_limited(false) {} |
| |
| BandwidthSampler::ConnectionStateOnSentPacket::~ConnectionStateOnSentPacket() {} |
| |
| } // namespace bbr |
| } // namespace webrtc |