bitrate_prober.cc (7448B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/pacing/bitrate_prober.h" 12 13 #include <algorithm> 14 #include <cstddef> 15 #include <optional> 16 17 #include "api/field_trials_view.h" 18 #include "api/transport/network_types.h" 19 #include "api/units/data_rate.h" 20 #include "api/units/data_size.h" 21 #include "api/units/time_delta.h" 22 #include "api/units/timestamp.h" 23 #include "rtc_base/checks.h" 24 #include "rtc_base/experiments/field_trial_parser.h" 25 #include "rtc_base/logging.h" 26 27 namespace webrtc { 28 29 namespace { 30 constexpr TimeDelta kProbeClusterTimeout = TimeDelta::Seconds(5); 31 constexpr size_t kMaxPendingProbeClusters = 5; 32 33 } // namespace 34 35 BitrateProberConfig::BitrateProberConfig( 36 const FieldTrialsView* key_value_config) 37 : max_probe_delay("max_probe_delay", TimeDelta::Millis(10)), 38 min_packet_size("min_packet_size", DataSize::Bytes(200)) { 39 ParseFieldTrial({&max_probe_delay, &min_packet_size}, 40 key_value_config->Lookup("WebRTC-Bwe-ProbingBehavior")); 41 } 42 43 BitrateProber::BitrateProber(const FieldTrialsView& field_trials) 44 : probing_state_(ProbingState::kDisabled), 45 next_probe_time_(Timestamp::PlusInfinity()), 46 config_(&field_trials) { 47 SetEnabled(true); 48 } 49 50 void BitrateProber::SetEnabled(bool enable) { 51 if (enable) { 52 if (probing_state_ == ProbingState::kDisabled) { 53 probing_state_ = ProbingState::kInactive; 54 RTC_LOG(LS_INFO) << "Bandwidth probing enabled, set to inactive"; 55 } 56 } else { 57 probing_state_ = ProbingState::kDisabled; 58 RTC_LOG(LS_INFO) << "Bandwidth probing disabled"; 59 } 60 } 61 62 void BitrateProber::SetAllowProbeWithoutMediaPacket(bool allow) { 63 config_.allow_start_probing_immediately = allow; 64 MaybeSetActiveState(/*packet_size=*/DataSize::Zero()); 65 } 66 67 void BitrateProber::MaybeSetActiveState(DataSize packet_size) { 68 if (ReadyToSetActiveState(packet_size)) { 69 next_probe_time_ = Timestamp::MinusInfinity(); 70 probing_state_ = ProbingState::kActive; 71 } 72 } 73 74 bool BitrateProber::ReadyToSetActiveState(DataSize packet_size) const { 75 if (clusters_.empty()) { 76 RTC_DCHECK(probing_state_ == ProbingState::kDisabled || 77 probing_state_ == ProbingState::kInactive); 78 return false; 79 } 80 switch (probing_state_) { 81 case ProbingState::kDisabled: 82 case ProbingState::kActive: 83 return false; 84 case ProbingState::kInactive: 85 if (config_.allow_start_probing_immediately) { 86 return true; 87 } 88 // If config_.min_packet_size > 0, a "large enough" packet must be 89 // sent first, before a probe can be generated and sent. Otherwise, 90 // send the probe asap. 91 return packet_size >= 92 std::min(RecommendedMinProbeSize(), config_.min_packet_size.Get()); 93 } 94 } 95 96 void BitrateProber::OnIncomingPacket(DataSize packet_size) { 97 MaybeSetActiveState(packet_size); 98 } 99 100 void BitrateProber::CreateProbeCluster( 101 const ProbeClusterConfig& cluster_config) { 102 RTC_DCHECK(probing_state_ != ProbingState::kDisabled); 103 RTC_DCHECK(cluster_config.min_probe_delta > TimeDelta::Zero()); 104 105 while (!clusters_.empty() && 106 (cluster_config.at_time - clusters_.front().requested_at > 107 kProbeClusterTimeout || 108 clusters_.size() > kMaxPendingProbeClusters)) { 109 clusters_.pop(); 110 } 111 112 ProbeCluster cluster; 113 cluster.requested_at = cluster_config.at_time; 114 cluster.pace_info.probe_cluster_min_probes = 115 cluster_config.target_probe_count; 116 cluster.pace_info.probe_cluster_min_bytes = 117 (cluster_config.target_data_rate * cluster_config.target_duration) 118 .bytes(); 119 RTC_DCHECK_GE(cluster.pace_info.probe_cluster_min_bytes, 0); 120 cluster.min_probe_delta = cluster_config.min_probe_delta; 121 cluster.pace_info.send_bitrate = cluster_config.target_data_rate; 122 cluster.pace_info.probe_cluster_id = cluster_config.id; 123 clusters_.push(cluster); 124 125 MaybeSetActiveState(/*packet_size=*/DataSize::Zero()); 126 127 RTC_DCHECK(probing_state_ == ProbingState::kActive || 128 probing_state_ == ProbingState::kInactive); 129 130 RTC_LOG(LS_INFO) << "Probe cluster (bitrate_bps:min bytes:min packets): (" 131 << cluster.pace_info.send_bitrate << ":" 132 << cluster.pace_info.probe_cluster_min_bytes << ":" 133 << cluster.pace_info.probe_cluster_min_probes << ", " 134 << (probing_state_ == ProbingState::kInactive ? "Inactive" 135 : "Active") 136 << ")"; 137 } 138 139 Timestamp BitrateProber::NextProbeTime(Timestamp /* now */) const { 140 // Probing is not active or probing is already complete. 141 if (probing_state_ != ProbingState::kActive || clusters_.empty()) { 142 return Timestamp::PlusInfinity(); 143 } 144 145 return next_probe_time_; 146 } 147 148 std::optional<PacedPacketInfo> BitrateProber::CurrentCluster(Timestamp now) { 149 if (clusters_.empty() || probing_state_ != ProbingState::kActive) { 150 return std::nullopt; 151 } 152 153 if (next_probe_time_.IsFinite() && 154 now - next_probe_time_ > config_.max_probe_delay.Get()) { 155 RTC_DLOG(LS_WARNING) << "Probe delay too high" 156 " (next_ms:" 157 << next_probe_time_.ms() << ", now_ms: " << now.ms() 158 << "), discarding probe cluster."; 159 clusters_.pop(); 160 if (clusters_.empty()) { 161 probing_state_ = ProbingState::kInactive; 162 return std::nullopt; 163 } 164 } 165 166 PacedPacketInfo info = clusters_.front().pace_info; 167 info.probe_cluster_bytes_sent = clusters_.front().sent_bytes; 168 return info; 169 } 170 171 DataSize BitrateProber::RecommendedMinProbeSize() const { 172 if (clusters_.empty()) { 173 return DataSize::Zero(); 174 } 175 DataRate send_rate = clusters_.front().pace_info.send_bitrate; 176 return send_rate * clusters_.front().min_probe_delta; 177 } 178 179 void BitrateProber::ProbeSent(Timestamp now, DataSize size) { 180 RTC_DCHECK(probing_state_ == ProbingState::kActive); 181 RTC_DCHECK(!size.IsZero()); 182 183 if (!clusters_.empty()) { 184 ProbeCluster* cluster = &clusters_.front(); 185 if (cluster->sent_probes == 0) { 186 RTC_DCHECK(cluster->started_at.IsInfinite()); 187 cluster->started_at = now; 188 } 189 cluster->sent_bytes += size.bytes<int>(); 190 cluster->sent_probes += 1; 191 next_probe_time_ = CalculateNextProbeTime(*cluster); 192 if (cluster->sent_bytes >= cluster->pace_info.probe_cluster_min_bytes && 193 cluster->sent_probes >= cluster->pace_info.probe_cluster_min_probes) { 194 clusters_.pop(); 195 } 196 if (clusters_.empty()) { 197 probing_state_ = ProbingState::kInactive; 198 } 199 } 200 } 201 202 Timestamp BitrateProber::CalculateNextProbeTime( 203 const ProbeCluster& cluster) const { 204 RTC_CHECK_GT(cluster.pace_info.send_bitrate.bps(), 0); 205 RTC_CHECK(cluster.started_at.IsFinite()); 206 207 // Compute the time delta from the cluster start to ensure probe bitrate stays 208 // close to the target bitrate. Result is in milliseconds. 209 DataSize sent_bytes = DataSize::Bytes(cluster.sent_bytes); 210 DataRate send_bitrate = cluster.pace_info.send_bitrate; 211 212 TimeDelta delta = sent_bytes / send_bitrate; 213 return cluster.started_at + delta; 214 } 215 216 } // namespace webrtc