/* * Copyright (c) 2016 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 "webrtc/modules/congestion_controller/delay_based_bwe.h" #include #include #include #include "webrtc/base/checks.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/logging.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/modules/congestion_controller/include/congestion_controller.h" #include "webrtc/modules/pacing/paced_sender.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "webrtc/system_wrappers/include/field_trial.h" #include "webrtc/system_wrappers/include/metrics.h" #include "webrtc/typedefs.h" namespace { constexpr int kTimestampGroupLengthMs = 5; constexpr int kAbsSendTimeFraction = 18; constexpr int kAbsSendTimeInterArrivalUpshift = 8; constexpr int kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift; constexpr double kTimestampToMs = 1000.0 / static_cast(1 << kInterArrivalShift); // This ssrc is used to fulfill the current API but will be removed // after the API has been changed. constexpr uint32_t kFixedSsrc = 0; constexpr int kInitialRateWindowMs = 500; constexpr int kRateWindowMs = 150; constexpr size_t kDefaultTrendlineWindowSize = 15; constexpr double kDefaultTrendlineSmoothingCoeff = 0.9; constexpr double kDefaultTrendlineThresholdGain = 4.0; const char kBitrateEstimateExperiment[] = "WebRTC-ImprovedBitrateEstimate"; const char kBweTrendlineFilterExperiment[] = "WebRTC-BweTrendlineFilter"; bool BitrateEstimateExperimentIsEnabled() { return webrtc::field_trial::FindFullName(kBitrateEstimateExperiment) == "Enabled"; } bool TrendlineFilterExperimentIsEnabled() { std::string experiment_string = webrtc::field_trial::FindFullName(kBweTrendlineFilterExperiment); // The experiment is enabled iff the field trial string begins with "Enabled". return experiment_string.find("Enabled") == 0; } bool ReadTrendlineFilterExperimentParameters(size_t* window_points, double* smoothing_coef, double* threshold_gain) { RTC_DCHECK(TrendlineFilterExperimentIsEnabled()); std::string experiment_string = webrtc::field_trial::FindFullName(kBweTrendlineFilterExperiment); int parsed_values = sscanf(experiment_string.c_str(), "Enabled-%zu,%lf,%lf", window_points, smoothing_coef, threshold_gain); if (parsed_values == 3) { RTC_CHECK_GT(*window_points, 1) << "Need at least 2 points to fit a line."; RTC_CHECK(0 <= *smoothing_coef && *smoothing_coef <= 1) << "Coefficient needs to be between 0 and 1 for weighted average."; RTC_CHECK_GT(*threshold_gain, 0) << "Threshold gain needs to be positive."; return true; } LOG(LS_WARNING) << "Failed to parse parameters for BweTrendlineFilter " "experiment from field trial string. Using default."; *window_points = kDefaultTrendlineWindowSize; *smoothing_coef = kDefaultTrendlineSmoothingCoeff; *threshold_gain = kDefaultTrendlineThresholdGain; return false; } } // namespace namespace webrtc { DelayBasedBwe::BitrateEstimator::BitrateEstimator() : sum_(0), current_win_ms_(0), prev_time_ms_(-1), bitrate_estimate_(-1.0f), bitrate_estimate_var_(50.0f), old_estimator_(kBitrateWindowMs, 8000), in_experiment_(BitrateEstimateExperimentIsEnabled()) {} void DelayBasedBwe::BitrateEstimator::Update(int64_t now_ms, int bytes) { if (!in_experiment_) { old_estimator_.Update(bytes, now_ms); rtc::Optional rate = old_estimator_.Rate(now_ms); bitrate_estimate_ = -1.0f; if (rate) bitrate_estimate_ = *rate / 1000.0f; return; } int rate_window_ms = kRateWindowMs; // We use a larger window at the beginning to get a more stable sample that // we can use to initialize the estimate. if (bitrate_estimate_ < 0.f) rate_window_ms = kInitialRateWindowMs; float bitrate_sample = UpdateWindow(now_ms, bytes, rate_window_ms); if (bitrate_sample < 0.0f) return; if (bitrate_estimate_ < 0.0f) { // This is the very first sample we get. Use it to initialize the estimate. bitrate_estimate_ = bitrate_sample; return; } // Define the sample uncertainty as a function of how far away it is from the // current estimate. float sample_uncertainty = 10.0f * std::abs(bitrate_estimate_ - bitrate_sample) / bitrate_estimate_; float sample_var = sample_uncertainty * sample_uncertainty; // Update a bayesian estimate of the rate, weighting it lower if the sample // uncertainty is large. // The bitrate estimate uncertainty is increased with each update to model // that the bitrate changes over time. float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f; bitrate_estimate_ = (sample_var * bitrate_estimate_ + pred_bitrate_estimate_var * bitrate_sample) / (sample_var + pred_bitrate_estimate_var); bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var / (sample_var + pred_bitrate_estimate_var); } float DelayBasedBwe::BitrateEstimator::UpdateWindow(int64_t now_ms, int bytes, int rate_window_ms) { // Reset if time moves backwards. if (now_ms < prev_time_ms_) { prev_time_ms_ = -1; sum_ = 0; current_win_ms_ = 0; } if (prev_time_ms_ >= 0) { current_win_ms_ += now_ms - prev_time_ms_; // Reset if nothing has been received for more than a full window. if (now_ms - prev_time_ms_ > rate_window_ms) { sum_ = 0; current_win_ms_ %= rate_window_ms; } } prev_time_ms_ = now_ms; float bitrate_sample = -1.0f; if (current_win_ms_ >= rate_window_ms) { bitrate_sample = 8.0f * sum_ / static_cast(rate_window_ms); current_win_ms_ -= rate_window_ms; sum_ = 0; } sum_ += bytes; return bitrate_sample; } rtc::Optional DelayBasedBwe::BitrateEstimator::bitrate_bps() const { if (bitrate_estimate_ < 0.f) return rtc::Optional(); return rtc::Optional(bitrate_estimate_ * 1000); } DelayBasedBwe::DelayBasedBwe(Clock* clock) : clock_(clock), inter_arrival_(), kalman_estimator_(), trendline_estimator_(), detector_(OverUseDetectorOptions()), receiver_incoming_bitrate_(), last_update_ms_(-1), last_seen_packet_ms_(-1), uma_recorded_(false), trendline_window_size_(kDefaultTrendlineWindowSize), trendline_smoothing_coeff_(kDefaultTrendlineSmoothingCoeff), trendline_threshold_gain_(kDefaultTrendlineThresholdGain), in_trendline_experiment_(TrendlineFilterExperimentIsEnabled()) { if (in_trendline_experiment_) { ReadTrendlineFilterExperimentParameters(&trendline_window_size_, &trendline_smoothing_coeff_, &trendline_threshold_gain_); } network_thread_.DetachFromThread(); } DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector( const std::vector& packet_feedback_vector) { RTC_DCHECK(network_thread_.CalledOnValidThread()); if (!uma_recorded_) { RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, BweNames::kSendSideTransportSeqNum, BweNames::kBweNamesMax); uma_recorded_ = true; } Result aggregated_result; for (const auto& packet_info : packet_feedback_vector) { Result result = IncomingPacketInfo(packet_info); if (result.updated) aggregated_result = result; } return aggregated_result; } DelayBasedBwe::Result DelayBasedBwe::IncomingPacketInfo( const PacketInfo& info) { int64_t now_ms = clock_->TimeInMilliseconds(); receiver_incoming_bitrate_.Update(info.arrival_time_ms, info.payload_size); Result result; // Reset if the stream has timed out. if (last_seen_packet_ms_ == -1 || now_ms - last_seen_packet_ms_ > kStreamTimeOutMs) { inter_arrival_.reset( new InterArrival((kTimestampGroupLengthMs << kInterArrivalShift) / 1000, kTimestampToMs, true)); kalman_estimator_.reset(new OveruseEstimator(OverUseDetectorOptions())); trendline_estimator_.reset(new TrendlineEstimator( trendline_window_size_, trendline_smoothing_coeff_, trendline_threshold_gain_)); } last_seen_packet_ms_ = now_ms; uint32_t send_time_24bits = static_cast( ((static_cast(info.send_time_ms) << kAbsSendTimeFraction) + 500) / 1000) & 0x00FFFFFF; // Shift up send time to use the full 32 bits that inter_arrival works with, // so wrapping works properly. uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift; uint32_t ts_delta = 0; int64_t t_delta = 0; int size_delta = 0; if (inter_arrival_->ComputeDeltas(timestamp, info.arrival_time_ms, now_ms, info.payload_size, &ts_delta, &t_delta, &size_delta)) { double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift); if (in_trendline_experiment_) { trendline_estimator_->Update(t_delta, ts_delta_ms, info.arrival_time_ms); detector_.Detect(trendline_estimator_->trendline_slope(), ts_delta_ms, trendline_estimator_->num_of_deltas(), info.arrival_time_ms); } else { kalman_estimator_->Update(t_delta, ts_delta_ms, size_delta, detector_.State(), info.arrival_time_ms); detector_.Detect(kalman_estimator_->offset(), ts_delta_ms, kalman_estimator_->num_of_deltas(), info.arrival_time_ms); } } int probing_bps = 0; if (info.probe_cluster_id != PacketInfo::kNotAProbe) { probing_bps = probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(info); } rtc::Optional acked_bitrate_bps = receiver_incoming_bitrate_.bitrate_bps(); // Currently overusing the bandwidth. if (detector_.State() == kBwOverusing) { if (acked_bitrate_bps && rate_control_.TimeToReduceFurther(now_ms, *acked_bitrate_bps)) { result.updated = UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps, &result.target_bitrate_bps); } } else if (probing_bps > 0) { // No overuse, but probing measured a bitrate. rate_control_.SetEstimate(probing_bps, info.arrival_time_ms); result.probe = true; result.updated = UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps, &result.target_bitrate_bps); } if (!result.updated && (last_update_ms_ == -1 || now_ms - last_update_ms_ > rate_control_.GetFeedbackInterval())) { result.updated = UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps, &result.target_bitrate_bps); } if (result.updated) last_update_ms_ = now_ms; return result; } bool DelayBasedBwe::UpdateEstimate(int64_t arrival_time_ms, int64_t now_ms, rtc::Optional acked_bitrate_bps, uint32_t* target_bitrate_bps) { // TODO(terelius): RateControlInput::noise_var is deprecated and will be // removed. In the meantime, we set it to zero. const RateControlInput input(detector_.State(), acked_bitrate_bps, 0); rate_control_.Update(&input, now_ms); *target_bitrate_bps = rate_control_.UpdateBandwidthEstimate(now_ms); return rate_control_.ValidEstimate(); } void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) { rate_control_.SetRtt(avg_rtt_ms); } bool DelayBasedBwe::LatestEstimate(std::vector* ssrcs, uint32_t* bitrate_bps) const { // Currently accessed from both the process thread (see // ModuleRtpRtcpImpl::Process()) and the configuration thread (see // Call::GetStats()). Should in the future only be accessed from a single // thread. RTC_DCHECK(ssrcs); RTC_DCHECK(bitrate_bps); if (!rate_control_.ValidEstimate()) return false; *ssrcs = {kFixedSsrc}; *bitrate_bps = rate_control_.LatestEstimate(); return true; } void DelayBasedBwe::SetMinBitrate(int min_bitrate_bps) { // Called from both the configuration thread and the network thread. Shouldn't // be called from the network thread in the future. rate_control_.SetMinBitrate(min_bitrate_bps); } } // namespace webrtc