Delete WebRTC.Video.BadCall.* histograms.

Reasons:
* Old code that has not been updated or tuned in several years.
* Nobody seems to intentionally use it.
* The application can do this itself by looking at GetStats.

Bug: None
Change-Id: Ib34bbebcf5885cf41ba05036506b500db7eb6b69
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/306160
Commit-Queue: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40104}
This commit is contained in:
Rasmus Brandt 2023-05-22 12:21:09 +02:00 committed by WebRTC LUCI CQ
parent b4015689b8
commit b7a688c68e
7 changed files with 0 additions and 464 deletions

View File

@ -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",

View File

@ -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<bool> QualityThreshold::IsHigh() const {
return is_high_;
}
absl::optional<double> QualityThreshold::CalculateVariance() const {
if (until_full_ > 0) {
return absl::nullopt;
}
double variance = 0;
double mean = static_cast<double>(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<double> 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<double>(num_high_states_) / num_certain_states_;
}
} // namespace webrtc

View File

@ -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 <memory>
#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<bool> IsHigh() const;
absl::optional<double> CalculateVariance() const;
absl::optional<double> FractionHigh(int min_required_samples) const;
private:
const std::unique_ptr<int[]> buffer_;
const int max_measurements_;
const float fraction_;
const int low_threshold_;
const int high_threshold_;
int until_full_;
int next_index_;
absl::optional<bool> 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_

View File

@ -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<int>(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<int>(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<double>(kNeededMeasurements) / (kNeededMeasurements + 1),
0.001);
}
} // namespace webrtc

View File

@ -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<double> fps_fraction =
fps_threshold_.FractionHigh(kBadCallMinRequiredSamples);
if (fps_fraction) {
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRate",
static_cast<int>(100 * (1 - *fps_fraction)));
}
absl::optional<double> variance_fraction =
variance_threshold_.FractionHigh(kBadCallMinRequiredSamples);
if (variance_fraction) {
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRateVariance",
static_cast<int>(100 * *variance_fraction));
}
absl::optional<double> qp_fraction =
qp_threshold_.FractionHigh(kBadCallMinRequiredSamples);
if (qp_fraction) {
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Qp",
static_cast<int>(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<int> 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<int>(fps));
if (qp)
qp_threshold_.AddMeasurement(*qp);
absl::optional<double> fps_variance_opt = fps_threshold_.CalculateVariance();
double fps_variance = fps_variance_opt.value_or(0);
if (fps_variance_opt) {
variance_threshold_.AddMeasurement(static_cast<int>(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);
}
}

View File

@ -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_);

View File

@ -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(