diff --git a/video/BUILD.gn b/video/BUILD.gn index 1fa27dbd3d..4da0a9d644 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -43,8 +43,6 @@ rtc_library("video") { "encoder_rtcp_feedback.h", "quality_limitation_reason_tracker.cc", "quality_limitation_reason_tracker.h", - "quality_threshold.cc", - "quality_threshold.h", "receive_statistics_proxy.cc", "receive_statistics_proxy.h", "report_block_stats.cc", @@ -754,7 +752,6 @@ if (rtc_include_tests) { "picture_id_tests.cc", "quality_limitation_reason_tracker_unittest.cc", "quality_scaling_tests.cc", - "quality_threshold_unittest.cc", "receive_statistics_proxy_unittest.cc", "report_block_stats_unittest.cc", "rtp_video_stream_receiver2_unittest.cc", diff --git a/video/quality_threshold.cc b/video/quality_threshold.cc deleted file mode 100644 index 931b0b20f9..0000000000 --- a/video/quality_threshold.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 "video/quality_threshold.h" - -#include "rtc_base/checks.h" - -namespace webrtc { - -QualityThreshold::QualityThreshold(int low_threshold, - int high_threshold, - float fraction, - int max_measurements) - : buffer_(new int[max_measurements]), - max_measurements_(max_measurements), - fraction_(fraction), - low_threshold_(low_threshold), - high_threshold_(high_threshold), - until_full_(max_measurements), - next_index_(0), - sum_(0), - count_low_(0), - count_high_(0), - num_high_states_(0), - num_certain_states_(0) { - RTC_CHECK_GT(fraction, 0.5f); - RTC_CHECK_GT(max_measurements, 1); - RTC_CHECK_LT(low_threshold, high_threshold); -} - -QualityThreshold::~QualityThreshold() = default; - -void QualityThreshold::AddMeasurement(int measurement) { - int prev_val = until_full_ > 0 ? 0 : buffer_[next_index_]; - buffer_[next_index_] = measurement; - next_index_ = (next_index_ + 1) % max_measurements_; - - sum_ += measurement - prev_val; - - if (until_full_ == 0) { - if (prev_val <= low_threshold_) { - --count_low_; - } else if (prev_val >= high_threshold_) { - --count_high_; - } - } - - if (measurement <= low_threshold_) { - ++count_low_; - } else if (measurement >= high_threshold_) { - ++count_high_; - } - - float sufficient_majority = fraction_ * max_measurements_; - if (count_high_ >= sufficient_majority) { - is_high_ = true; - } else if (count_low_ >= sufficient_majority) { - is_high_ = false; - } - - if (until_full_ > 0) - --until_full_; - - if (is_high_) { - if (*is_high_) - ++num_high_states_; - ++num_certain_states_; - } -} - -absl::optional QualityThreshold::IsHigh() const { - return is_high_; -} - -absl::optional QualityThreshold::CalculateVariance() const { - if (until_full_ > 0) { - return absl::nullopt; - } - - double variance = 0; - double mean = static_cast(sum_) / max_measurements_; - for (int i = 0; i < max_measurements_; ++i) { - variance += (buffer_[i] - mean) * (buffer_[i] - mean); - } - return variance / (max_measurements_ - 1); -} - -absl::optional QualityThreshold::FractionHigh( - int min_required_samples) const { - RTC_DCHECK_GT(min_required_samples, 0); - if (num_certain_states_ < min_required_samples) - return absl::nullopt; - - return static_cast(num_high_states_) / num_certain_states_; -} - -} // namespace webrtc diff --git a/video/quality_threshold.h b/video/quality_threshold.h deleted file mode 100644 index a193aa7f01..0000000000 --- a/video/quality_threshold.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef VIDEO_QUALITY_THRESHOLD_H_ -#define VIDEO_QUALITY_THRESHOLD_H_ - -#include - -#include "absl/types/optional.h" - -namespace webrtc { - -class QualityThreshold { - public: - // Both thresholds are inclusive, i.e. measurement >= high signifies a high - // state, while measurement <= low signifies a low state. - QualityThreshold(int low_threshold, - int high_threshold, - float fraction, - int max_measurements); - ~QualityThreshold(); - - void AddMeasurement(int measurement); - absl::optional IsHigh() const; - absl::optional CalculateVariance() const; - absl::optional FractionHigh(int min_required_samples) const; - - private: - const std::unique_ptr buffer_; - const int max_measurements_; - const float fraction_; - const int low_threshold_; - const int high_threshold_; - int until_full_; - int next_index_; - absl::optional is_high_; - int sum_; - int count_low_; - int count_high_; - int num_high_states_; - int num_certain_states_; -}; - -} // namespace webrtc - -#endif // VIDEO_QUALITY_THRESHOLD_H_ diff --git a/video/quality_threshold_unittest.cc b/video/quality_threshold_unittest.cc deleted file mode 100644 index c9396d7188..0000000000 --- a/video/quality_threshold_unittest.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 "video/quality_threshold.h" - -#include "test/gtest.h" - -namespace webrtc { - -TEST(QualityThresholdTest, BackAndForth) { - const int kLowThreshold = 0; - const int kHighThreshold = 1; - const float kFraction = 0.75f; - const int kMaxMeasurements = 10; - - QualityThreshold thresh(kLowThreshold, kHighThreshold, kFraction, - kMaxMeasurements); - - const int kNeededMeasurements = - static_cast(kFraction * kMaxMeasurements + 1); - for (int i = 0; i < kNeededMeasurements; ++i) { - EXPECT_FALSE(thresh.IsHigh()); - thresh.AddMeasurement(kLowThreshold); - } - ASSERT_TRUE(thresh.IsHigh()); - for (int i = 0; i < kNeededMeasurements; ++i) { - EXPECT_FALSE(*thresh.IsHigh()); - thresh.AddMeasurement(kHighThreshold); - } - EXPECT_TRUE(*thresh.IsHigh()); - - for (int i = 0; i < kNeededMeasurements; ++i) { - EXPECT_TRUE(*thresh.IsHigh()); - thresh.AddMeasurement(kLowThreshold); - } - EXPECT_FALSE(*thresh.IsHigh()); -} - -TEST(QualityThresholdTest, Variance) { - const int kLowThreshold = 0; - const int kHighThreshold = 1; - const float kFraction = 0.8f; - const int kMaxMeasurements = 10; - const double kMaxError = 0.01; - - // Previously randomly generated values... - int values[] = {51, 79, 80, 56, 19, 20, 48, 57, 48, 25, 2, 25, 38, 37, 25}; - // ...with precomputed variances. - double variances[] = {476.9, 687.6, 552, 336.4, 278.767, 265.167}; - - QualityThreshold thresh(kLowThreshold, kHighThreshold, kFraction, - kMaxMeasurements); - - for (int i = 0; i < kMaxMeasurements; ++i) { - EXPECT_FALSE(thresh.CalculateVariance()); - thresh.AddMeasurement(values[i]); - } - - ASSERT_TRUE(thresh.CalculateVariance()); - EXPECT_NEAR(variances[0], *thresh.CalculateVariance(), kMaxError); - for (unsigned int i = 1; i < sizeof(variances) / sizeof(double); ++i) { - thresh.AddMeasurement(values[i + kMaxMeasurements - 1]); - EXPECT_NEAR(variances[i], *thresh.CalculateVariance(), kMaxError); - } - - for (int i = 0; i < kMaxMeasurements; ++i) { - thresh.AddMeasurement(42); - } - EXPECT_NEAR(0, *thresh.CalculateVariance(), kMaxError); -} - -TEST(QualityThresholdTest, BetweenThresholds) { - const int kLowThreshold = 0; - const int kHighThreshold = 2; - const float kFraction = 0.6f; - const int kMaxMeasurements = 10; - - const int kBetweenThresholds = (kLowThreshold + kHighThreshold) / 2; - - QualityThreshold thresh(kLowThreshold, kHighThreshold, kFraction, - kMaxMeasurements); - - for (int i = 0; i < 2 * kMaxMeasurements; ++i) { - EXPECT_FALSE(thresh.IsHigh()); - thresh.AddMeasurement(kBetweenThresholds); - } - EXPECT_FALSE(thresh.IsHigh()); -} - -TEST(QualityThresholdTest, FractionHigh) { - const int kLowThreshold = 0; - const int kHighThreshold = 2; - const float kFraction = 0.75f; - const int kMaxMeasurements = 10; - - const int kBetweenThresholds = (kLowThreshold + kHighThreshold) / 2; - const int kNeededMeasurements = - static_cast(kFraction * kMaxMeasurements + 1); - - QualityThreshold thresh(kLowThreshold, kHighThreshold, kFraction, - kMaxMeasurements); - - for (int i = 0; i < kMaxMeasurements; ++i) { - EXPECT_FALSE(thresh.FractionHigh(1)); - thresh.AddMeasurement(kBetweenThresholds); - } - - for (int i = 0; i < kNeededMeasurements; i++) { - EXPECT_FALSE(thresh.FractionHigh(1)); - thresh.AddMeasurement(kHighThreshold); - } - EXPECT_FALSE(thresh.FractionHigh(2)); - ASSERT_TRUE(thresh.FractionHigh(1)); - EXPECT_NEAR(*thresh.FractionHigh(1), 1, 0.001); - - for (int i = 0; i < kNeededMeasurements; i++) { - EXPECT_NEAR(*thresh.FractionHigh(1), 1, 0.001); - thresh.AddMeasurement(kLowThreshold); - } - EXPECT_NEAR( - *thresh.FractionHigh(1), - static_cast(kNeededMeasurements) / (kNeededMeasurements + 1), - 0.001); -} - -} // namespace webrtc diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc index daae7a3b68..f1ff07000b 100644 --- a/video/receive_statistics_proxy.cc +++ b/video/receive_statistics_proxy.cc @@ -30,23 +30,6 @@ namespace { // Periodic time interval for processing samples for `freq_offset_counter_`. const int64_t kFreqOffsetProcessIntervalMs = 40000; -// Configuration for bad call detection. -const int kBadCallMinRequiredSamples = 10; -const int kMinSampleLengthMs = 990; -const int kNumMeasurements = 10; -const int kNumMeasurementsVariance = kNumMeasurements * 1.5; -const float kBadFraction = 0.8f; -// For fps: -// Low means low enough to be bad, high means high enough to be good -const int kLowFpsThreshold = 12; -const int kHighFpsThreshold = 14; -// For qp and fps variance: -// Low means low enough to be good, high means high enough to be bad -const int kLowQpThresholdVp8 = 60; -const int kHighQpThresholdVp8 = 70; -const int kLowVarianceThreshold = 1; -const int kHighVarianceThreshold = 2; - // Some metrics are reported as a maximum over this period. // This should be synchronized with a typical getStats polling interval in // the clients. @@ -101,21 +84,6 @@ ReceiveStatisticsProxy::ReceiveStatisticsProxy(uint32_t remote_ssrc, TaskQueueBase* worker_thread) : clock_(clock), start_ms_(clock->TimeInMilliseconds()), - last_sample_time_(clock->TimeInMilliseconds()), - fps_threshold_(kLowFpsThreshold, - kHighFpsThreshold, - kBadFraction, - kNumMeasurements), - qp_threshold_(kLowQpThresholdVp8, - kHighQpThresholdVp8, - kBadFraction, - kNumMeasurements), - variance_threshold_(kLowVarianceThreshold, - kHighVarianceThreshold, - kBadFraction, - kNumMeasurementsVariance), - num_bad_states_(0), - num_certain_states_(0), remote_ssrc_(remote_ssrc), // 1000ms window, scale 1000 for ms to s. decode_fps_estimator_(1000, 1000), @@ -464,105 +432,11 @@ void ReceiveStatisticsProxy::UpdateHistograms( } } - if (num_certain_states_ >= kBadCallMinRequiredSamples) { - RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Any", - 100 * num_bad_states_ / num_certain_states_); - } - absl::optional fps_fraction = - fps_threshold_.FractionHigh(kBadCallMinRequiredSamples); - if (fps_fraction) { - RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRate", - static_cast(100 * (1 - *fps_fraction))); - } - absl::optional variance_fraction = - variance_threshold_.FractionHigh(kBadCallMinRequiredSamples); - if (variance_fraction) { - RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRateVariance", - static_cast(100 * *variance_fraction)); - } - absl::optional qp_fraction = - qp_threshold_.FractionHigh(kBadCallMinRequiredSamples); - if (qp_fraction) { - RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Qp", - static_cast(100 * *qp_fraction)); - } - RTC_LOG(LS_INFO) << log_stream.str(); video_quality_observer_->UpdateHistograms( videocontenttypehelpers::IsScreenshare(last_content_type_)); } -void ReceiveStatisticsProxy::QualitySample(Timestamp now) { - RTC_DCHECK_RUN_ON(&main_thread_); - - if (last_sample_time_ + kMinSampleLengthMs > now.ms()) - return; - - double fps = - render_fps_tracker_.ComputeRateForInterval(now.ms() - last_sample_time_); - absl::optional qp = qp_sample_.Avg(1); - - bool prev_fps_bad = !fps_threshold_.IsHigh().value_or(true); - bool prev_qp_bad = qp_threshold_.IsHigh().value_or(false); - bool prev_variance_bad = variance_threshold_.IsHigh().value_or(false); - bool prev_any_bad = prev_fps_bad || prev_qp_bad || prev_variance_bad; - - fps_threshold_.AddMeasurement(static_cast(fps)); - if (qp) - qp_threshold_.AddMeasurement(*qp); - absl::optional fps_variance_opt = fps_threshold_.CalculateVariance(); - double fps_variance = fps_variance_opt.value_or(0); - if (fps_variance_opt) { - variance_threshold_.AddMeasurement(static_cast(fps_variance)); - } - - bool fps_bad = !fps_threshold_.IsHigh().value_or(true); - bool qp_bad = qp_threshold_.IsHigh().value_or(false); - bool variance_bad = variance_threshold_.IsHigh().value_or(false); - bool any_bad = fps_bad || qp_bad || variance_bad; - - if (!prev_any_bad && any_bad) { - RTC_LOG(LS_INFO) << "Bad call (any) start: " << now.ms(); - } else if (prev_any_bad && !any_bad) { - RTC_LOG(LS_INFO) << "Bad call (any) end: " << now.ms(); - } - - if (!prev_fps_bad && fps_bad) { - RTC_LOG(LS_INFO) << "Bad call (fps) start: " << now.ms(); - } else if (prev_fps_bad && !fps_bad) { - RTC_LOG(LS_INFO) << "Bad call (fps) end: " << now.ms(); - } - - if (!prev_qp_bad && qp_bad) { - RTC_LOG(LS_INFO) << "Bad call (qp) start: " << now.ms(); - } else if (prev_qp_bad && !qp_bad) { - RTC_LOG(LS_INFO) << "Bad call (qp) end: " << now.ms(); - } - - if (!prev_variance_bad && variance_bad) { - RTC_LOG(LS_INFO) << "Bad call (variance) start: " << now.ms(); - } else if (prev_variance_bad && !variance_bad) { - RTC_LOG(LS_INFO) << "Bad call (variance) end: " << now.ms(); - } - - RTC_LOG(LS_VERBOSE) << "SAMPLE: sample_length: " - << (now.ms() - last_sample_time_) << " fps: " << fps - << " fps_bad: " << fps_bad << " qp: " << qp.value_or(-1) - << " qp_bad: " << qp_bad - << " variance_bad: " << variance_bad - << " fps_variance: " << fps_variance; - - last_sample_time_ = now.ms(); - qp_sample_.Reset(); - - if (fps_threshold_.IsHigh() || variance_threshold_.IsHigh() || - qp_threshold_.IsHigh()) { - if (any_bad) - ++num_bad_states_; - ++num_certain_states_; - } -} - void ReceiveStatisticsProxy::UpdateFramerate(int64_t now_ms) const { RTC_DCHECK_RUN_ON(&main_thread_); @@ -892,8 +766,6 @@ void ReceiveStatisticsProxy::OnRenderedFrame( content_specific_stats->e2e_delay_counter.Add(delay_ms); } } - - QualitySample(frame_meta.decode_timestamp); } void ReceiveStatisticsProxy::OnSyncOffsetUpdated(int64_t video_playout_ntp_ms, @@ -964,7 +836,6 @@ void ReceiveStatisticsProxy::OnPreDecode(VideoCodecType codec_type, int qp) { last_codec_type_ = codec_type; if (last_codec_type_ == kVideoCodecVP8 && qp != -1) { qp_counters_.vp8.Add(qp); - qp_sample_.Add(qp); } } diff --git a/video/receive_statistics_proxy.h b/video/receive_statistics_proxy.h index 9c38dec1d8..684b664608 100644 --- a/video/receive_statistics_proxy.h +++ b/video/receive_statistics_proxy.h @@ -31,7 +31,6 @@ #include "rtc_base/rate_tracker.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" -#include "video/quality_threshold.h" #include "video/stats_counter.h" #include "video/video_quality_observer2.h" #include "video/video_stream_buffer_controller.h" @@ -142,8 +141,6 @@ class ReceiveStatisticsProxy : public VideoStreamBufferControllerStatsObserver, rtc::HistogramPercentileCounter interframe_delay_percentiles; }; - void QualitySample(Timestamp now); - // Removes info about old frames and then updates the framerate. void UpdateFramerate(int64_t now_ms) const; @@ -153,14 +150,6 @@ class ReceiveStatisticsProxy : public VideoStreamBufferControllerStatsObserver, Clock* const clock_; const int64_t start_ms_; - int64_t last_sample_time_ RTC_GUARDED_BY(main_thread_); - - QualityThreshold fps_threshold_ RTC_GUARDED_BY(main_thread_); - QualityThreshold qp_threshold_ RTC_GUARDED_BY(main_thread_); - QualityThreshold variance_threshold_ RTC_GUARDED_BY(main_thread_); - rtc::SampleCounter qp_sample_ RTC_GUARDED_BY(main_thread_); - int num_bad_states_ RTC_GUARDED_BY(main_thread_); - int num_certain_states_ RTC_GUARDED_BY(main_thread_); // Note: The `stats_.rtp_stats` member is not used or populated by this class. mutable VideoReceiveStreamInterface::Stats stats_ RTC_GUARDED_BY(main_thread_); diff --git a/video/receive_statistics_proxy_unittest.cc b/video/receive_statistics_proxy_unittest.cc index b0c8a13d45..faca0d8668 100644 --- a/video/receive_statistics_proxy_unittest.cc +++ b/video/receive_statistics_proxy_unittest.cc @@ -699,37 +699,6 @@ TEST_F(ReceiveStatisticsProxyTest, 0, metrics::NumSamples("WebRTC.Video.ReceiveStreamLifetimeInSeconds")); } -TEST_F(ReceiveStatisticsProxyTest, BadCallHistogramsAreUpdated) { - // Based on the tuning parameters this will produce 7 uncertain states, - // then 10 certainly bad states. There has to be 10 certain states before - // any histograms are recorded. - const int kNumBadSamples = 17; - // We only count one sample per second. - const TimeDelta kBadFameInterval = TimeDelta::Millis(1100); - - StreamDataCounters counters; - counters.first_packet_time_ms = Now().ms(); - - webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight); - - for (int i = 0; i < kNumBadSamples; ++i) { - time_controller_.AdvanceTime(kBadFameInterval); - statistics_proxy_->OnRenderedFrame(MetaData(frame)); - } - statistics_proxy_->UpdateHistograms(absl::nullopt, counters, nullptr); - EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.BadCall.Any")); - EXPECT_METRIC_EQ(1, metrics::NumEvents("WebRTC.Video.BadCall.Any", 100)); - - EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.BadCall.FrameRate")); - EXPECT_METRIC_EQ(1, - metrics::NumEvents("WebRTC.Video.BadCall.FrameRate", 100)); - - EXPECT_METRIC_EQ( - 0, metrics::NumSamples("WebRTC.Video.BadCall.FrameRateVariance")); - - EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.BadCall.Qp")); -} - TEST_F(ReceiveStatisticsProxyTest, PacketLossHistogramIsUpdated) { statistics_proxy_->UpdateHistograms(10, StreamDataCounters(), nullptr); EXPECT_METRIC_EQ(