// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cast/congestion_control/congestion_control.h" #include "base/logging.h" #include "media/cast/cast_config.h" #include "media/cast/cast_defines.h" namespace media { namespace cast { static const int64 kCongestionControlMinChangeIntervalMs = 10; static const int64 kCongestionControlMaxChangeIntervalMs = 100; // At 10 ms RTT TCP Reno would ramp 1500 * 8 * 100 = 1200 Kbit/s. // NACK is sent after a maximum of 10 ms. static const int kCongestionControlMaxBitrateIncreasePerMillisecond = 1200; static const int64 kMaxElapsedTimeMs = kCongestionControlMaxChangeIntervalMs; CongestionControl::CongestionControl(base::TickClock* clock, float congestion_control_back_off, uint32 max_bitrate_configured, uint32 min_bitrate_configured, uint32 start_bitrate) : clock_(clock), congestion_control_back_off_(congestion_control_back_off), max_bitrate_configured_(max_bitrate_configured), min_bitrate_configured_(min_bitrate_configured), bitrate_(start_bitrate) { DCHECK_GT(congestion_control_back_off, 0.0f) << "Invalid config"; DCHECK_LT(congestion_control_back_off, 1.0f) << "Invalid config"; DCHECK_GE(max_bitrate_configured, min_bitrate_configured) << "Invalid config"; DCHECK_GE(max_bitrate_configured, start_bitrate) << "Invalid config"; DCHECK_GE(start_bitrate, min_bitrate_configured) << "Invalid config"; } CongestionControl::~CongestionControl() {} bool CongestionControl::OnAck(base::TimeDelta rtt, uint32* new_bitrate) { base::TimeTicks now = clock_->NowTicks(); // First feedback? if (time_last_increase_.is_null()) { time_last_increase_ = now; time_last_decrease_ = now; return false; } // Are we at the max bitrate? if (max_bitrate_configured_ == bitrate_) return false; // Make sure RTT is never less than 1 ms. rtt = std::max(rtt, base::TimeDelta::FromMilliseconds(1)); base::TimeDelta elapsed_time = std::min(now - time_last_increase_, base::TimeDelta::FromMilliseconds(kMaxElapsedTimeMs)); base::TimeDelta change_interval = std::max( rtt, base::TimeDelta::FromMilliseconds(kCongestionControlMinChangeIntervalMs)); change_interval = std::min( change_interval, base::TimeDelta::FromMilliseconds(kCongestionControlMaxChangeIntervalMs)); // Have enough time have passed? if (elapsed_time < change_interval) return false; time_last_increase_ = now; // One packet per RTT multiplied by the elapsed time fraction. // 1500 * 8 * (1000 / rtt_ms) * (elapsed_time_ms / 1000) => // 1500 * 8 * elapsed_time_ms / rtt_ms. uint32 bitrate_increase = (1500 * 8 * elapsed_time.InMilliseconds()) / rtt.InMilliseconds(); uint32 max_bitrate_increase = kCongestionControlMaxBitrateIncreasePerMillisecond * elapsed_time.InMilliseconds(); bitrate_increase = std::min(max_bitrate_increase, bitrate_increase); *new_bitrate = std::min(bitrate_increase + bitrate_, max_bitrate_configured_); bitrate_ = *new_bitrate; return true; } bool CongestionControl::OnNack(base::TimeDelta rtt, uint32* new_bitrate) { base::TimeTicks now = clock_->NowTicks(); // First feedback? if (time_last_decrease_.is_null()) { time_last_increase_ = now; time_last_decrease_ = now; return false; } base::TimeDelta elapsed_time = std::min(now - time_last_decrease_, base::TimeDelta::FromMilliseconds(kMaxElapsedTimeMs)); base::TimeDelta change_interval = std::max( rtt, base::TimeDelta::FromMilliseconds(kCongestionControlMinChangeIntervalMs)); change_interval = std::min( change_interval, base::TimeDelta::FromMilliseconds(kCongestionControlMaxChangeIntervalMs)); // Have enough time have passed? if (elapsed_time < change_interval) return false; time_last_decrease_ = now; time_last_increase_ = now; *new_bitrate = std::max(static_cast(bitrate_ * congestion_control_back_off_), min_bitrate_configured_); bitrate_ = *new_bitrate; return true; } } // namespace cast } // namespace media