/* * Copyright (c) 2013 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/remote_bitrate_estimator/overuse_estimator.h" #include #include #include #include "api/network_state_predictor.h" #include "modules/remote_bitrate_estimator/test/bwe_test_logging.h" #include "rtc_base/logging.h" namespace webrtc { namespace { constexpr int kMinFramePeriodHistoryLength = 60; constexpr int kDeltaCounterMax = 1000; } // namespace OveruseEstimator::OveruseEstimator() = default; void OveruseEstimator::Update(int64_t t_delta, double ts_delta, int size_delta, BandwidthUsage current_hypothesis, int64_t now_ms) { const double min_frame_period = UpdateMinFramePeriod(ts_delta); const double t_ts_delta = t_delta - ts_delta; BWE_TEST_LOGGING_PLOT(1, "dm_ms", now_ms, t_ts_delta); double fs_delta = size_delta; ++num_of_deltas_; if (num_of_deltas_ > kDeltaCounterMax) { num_of_deltas_ = kDeltaCounterMax; } // Update the Kalman filter. E_[0][0] += process_noise_[0]; E_[1][1] += process_noise_[1]; if ((current_hypothesis == BandwidthUsage::kBwOverusing && offset_ < prev_offset_) || (current_hypothesis == BandwidthUsage::kBwUnderusing && offset_ > prev_offset_)) { E_[1][1] += 10 * process_noise_[1]; } const double h[2] = {fs_delta, 1.0}; const double Eh[2] = {E_[0][0] * h[0] + E_[0][1] * h[1], E_[1][0] * h[0] + E_[1][1] * h[1]}; BWE_TEST_LOGGING_PLOT(1, "d_ms", now_ms, slope_ * h[0] - offset_); const double residual = t_ts_delta - slope_ * h[0] - offset_; const bool in_stable_state = (current_hypothesis == BandwidthUsage::kBwNormal); const double max_residual = 3.0 * sqrt(var_noise_); // We try to filter out very late frames. For instance periodic key // frames doesn't fit the Gaussian model well. if (fabs(residual) < max_residual) { UpdateNoiseEstimate(residual, min_frame_period, in_stable_state); } else { UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual, min_frame_period, in_stable_state); } const double denom = var_noise_ + h[0] * Eh[0] + h[1] * Eh[1]; const double K[2] = {Eh[0] / denom, Eh[1] / denom}; const double IKh[2][2] = {{1.0 - K[0] * h[0], -K[0] * h[1]}, {-K[1] * h[0], 1.0 - K[1] * h[1]}}; const double e00 = E_[0][0]; const double e01 = E_[0][1]; // Update state. E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1]; E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1]; E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1]; E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1]; // The covariance matrix must be positive semi-definite. bool positive_semi_definite = E_[0][0] + E_[1][1] >= 0 && E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0; RTC_DCHECK(positive_semi_definite); if (!positive_semi_definite) { RTC_LOG(LS_ERROR) << "The over-use estimator's covariance matrix is no longer " "semi-definite."; } slope_ = slope_ + K[0] * residual; prev_offset_ = offset_; offset_ = offset_ + K[1] * residual; BWE_TEST_LOGGING_PLOT(1, "kc", now_ms, K[0]); BWE_TEST_LOGGING_PLOT(1, "km", now_ms, K[1]); BWE_TEST_LOGGING_PLOT(1, "slope_1/bps", now_ms, slope_); BWE_TEST_LOGGING_PLOT(1, "var_noise", now_ms, var_noise_); } double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) { double min_frame_period = ts_delta; if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) { ts_delta_hist_.pop_front(); } for (const double old_ts_delta : ts_delta_hist_) { min_frame_period = std::min(old_ts_delta, min_frame_period); } ts_delta_hist_.push_back(ts_delta); return min_frame_period; } void OveruseEstimator::UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state) { if (!stable_state) { return; } // Faster filter during startup to faster adapt to the jitter level // of the network. `alpha` is tuned for 30 frames per second, but is scaled // according to `ts_delta`. double alpha = 0.01; if (num_of_deltas_ > 10 * 30) { alpha = 0.002; } // Only update the noise estimate if we're not over-using. `beta` is a // function of alpha and the time delta since the previous update. const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0); avg_noise_ = beta * avg_noise_ + (1 - beta) * residual; var_noise_ = beta * var_noise_ + (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual); if (var_noise_ < 1) { var_noise_ = 1; } } } // namespace webrtc