Exclude initial adapt downs in stats for quality adapt changes per minute.
Make WebRTC.Video.AdaptChangesPerMinute.Quality stats only based on changes during a call.
Discard initial quality adapt changes due to bitrate (MaximumFrameSizeForBitrate).
Makes stats only based on changes determined by the quality scaler.
Bug: none
Change-Id: I461b65e65634565ade87b1336cf5206aa14926ff
Reviewed-on: https://webrtc-review.googlesource.com/37660
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21585}
diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc
index 0d2f8f6..4375e84 100644
--- a/video/send_statistics_proxy.cc
+++ b/video/send_statistics_proxy.cc
@@ -439,6 +439,11 @@
if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
int quality_changes = current_stats.number_of_quality_adapt_changes -
start_stats_.number_of_quality_adapt_changes;
+ // Only base stats on changes during a call, discard initial changes.
+ int initial_changes =
+ initial_quality_changes_.down + initial_quality_changes_.up;
+ if (initial_changes <= quality_changes)
+ quality_changes -= initial_changes;
RTC_HISTOGRAMS_COUNTS_100(kIndex,
uma_prefix_ + "AdaptChangesPerMinute.Quality",
quality_changes * 60 / elapsed_sec);
@@ -987,6 +992,7 @@
const VideoStreamEncoder::AdaptCounts& cpu_counts,
const VideoStreamEncoder::AdaptCounts& quality_counts) {
rtc::CritScope lock(&crit_);
+ TryUpdateInitialQualityResolutionAdaptUp(quality_counts);
++stats_.number_of_quality_adapt_changes;
UpdateAdaptationStats(cpu_counts, quality_counts);
}
@@ -1003,6 +1009,27 @@
stats_.bw_limited_framerate = quality_counts.fps > 0;
}
+// TODO(asapersson): Include fps changes.
+void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
+ rtc::CritScope lock(&crit_);
+ ++uma_container_->initial_quality_changes_.down;
+}
+
+void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
+ const VideoStreamEncoder::AdaptCounts& quality_counts) {
+ if (uma_container_->initial_quality_changes_.down == 0)
+ return;
+
+ if (quality_downscales_ > 0 &&
+ quality_counts.resolution < quality_downscales_) {
+ // Adapting up in quality.
+ if (uma_container_->initial_quality_changes_.down >
+ uma_container_->initial_quality_changes_.up) {
+ ++uma_container_->initial_quality_changes_.up;
+ }
+ }
+}
+
void SendStatisticsProxy::SetAdaptTimer(
const VideoStreamEncoder::AdaptCounts& counts,
StatsTimer* timer) {
diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h
index c90f88a..1aa836e 100644
--- a/video/send_statistics_proxy.h
+++ b/video/send_statistics_proxy.h
@@ -74,6 +74,7 @@
const VideoStreamEncoder::AdaptCounts& cpu_counts,
const VideoStreamEncoder::AdaptCounts& quality_counts);
void OnMinPixelLimitReached();
+ void OnInitialQualityResolutionAdaptDown();
void OnSuspendChange(bool is_suspended);
void OnInactiveSsrc(uint32_t ssrc);
@@ -181,6 +182,10 @@
SampleCounter vp9; // QP range: 0-255.
SampleCounter h264; // QP range: 0-51.
};
+ struct AdaptChanges {
+ int down = 0;
+ int up = 0;
+ };
// Map holding encoded frames (mapped by timestamp).
// If simulcast layers are encoded on different threads, there is no guarantee
@@ -217,6 +222,9 @@
const VideoStreamEncoder::AdaptCounts& cpu_counts,
const VideoStreamEncoder::AdaptCounts& quality_counts)
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
+ void TryUpdateInitialQualityResolutionAdaptUp(
+ const VideoStreamEncoder::AdaptCounts& quality_counts)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
void UpdateEncoderFallbackStats(const CodecSpecificInfo* codec_info,
int pixels)
@@ -298,6 +306,7 @@
size_t num_streams_; // Number of configured streams to encoder.
size_t num_pixels_highest_stream_;
EncodedFrameMap encoded_frames_;
+ AdaptChanges initial_quality_changes_;
std::map<int, QpCounters>
qp_counters_; // QP counters mapped by spatial idx.
diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc
index 1d2edc6..01cbfe8 100644
--- a/video/send_statistics_proxy_unittest.cc
+++ b/video/send_statistics_proxy_unittest.cc
@@ -513,6 +513,102 @@
EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Cpu", 6));
}
+TEST_F(SendStatisticsProxyTest, ExcludesInitialQualityAdaptDownChange) {
+ // First RTP packet sent.
+ UpdateDataCounters(kFirstSsrc);
+ // Enable adaptation.
+ VideoStreamEncoder::AdaptCounts cpu_counts;
+ VideoStreamEncoder::AdaptCounts quality_counts;
+ statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
+ // Adapt changes: 1 (1 initial) = 0, elapsed time: 10 sec => 0 per minute.
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ fake_clock_.AdvanceTimeMilliseconds(10000);
+ statistics_proxy_.reset();
+ EXPECT_EQ(1,
+ metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
+ EXPECT_EQ(
+ 1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 0));
+}
+
+TEST_F(SendStatisticsProxyTest, ExcludesInitialQualityAdaptDownChanges) {
+ // First RTP packet sent.
+ UpdateDataCounters(kFirstSsrc);
+ // Enable adaptation.
+ VideoStreamEncoder::AdaptCounts cpu_counts;
+ VideoStreamEncoder::AdaptCounts quality_counts;
+ statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
+ // Adapt changes: 3 (2 initial) = 1, elapsed time: 10 sec => 6 per minute.
+ quality_counts.resolution = 1;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ quality_counts.resolution = 2;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ quality_counts.resolution = 3;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ fake_clock_.AdvanceTimeMilliseconds(10000);
+ statistics_proxy_.reset();
+ EXPECT_EQ(1,
+ metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
+ EXPECT_EQ(
+ 1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 6));
+}
+
+TEST_F(SendStatisticsProxyTest, InitialQualityAdaptChangesNotExcludedOnError) {
+ // First RTP packet sent.
+ UpdateDataCounters(kFirstSsrc);
+ // Enable adaptation.
+ VideoStreamEncoder::AdaptCounts cpu_counts;
+ VideoStreamEncoder::AdaptCounts quality_counts;
+ statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
+ // Adapt changes: 1 (2 initial) = 1, elapsed time: 10 sec => 6 per minute.
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ fake_clock_.AdvanceTimeMilliseconds(10000);
+ statistics_proxy_.reset();
+ EXPECT_EQ(1,
+ metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
+ EXPECT_EQ(
+ 1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 6));
+}
+
+TEST_F(SendStatisticsProxyTest, ExcludesInitialQualityAdaptDownAndUpChanges) {
+ // First RTP packet sent.
+ UpdateDataCounters(kFirstSsrc);
+ // Enable adaptation.
+ VideoStreamEncoder::AdaptCounts cpu_counts;
+ VideoStreamEncoder::AdaptCounts quality_counts;
+ statistics_proxy_->SetAdaptationStats(cpu_counts, quality_counts);
+ // Adapt changes: 8 (4 initial) = 4, elapsed time: 10 sec => 24 per minute.
+ quality_counts.resolution = 1;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ quality_counts.resolution = 2;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ statistics_proxy_->OnInitialQualityResolutionAdaptDown();
+ quality_counts.resolution = 3;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ quality_counts.fps = 1;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ quality_counts.fps = 0;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ quality_counts.resolution = 2; // Initial resolution up.
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ quality_counts.resolution = 1; // Initial resolution up.
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+ quality_counts.resolution = 0;
+ statistics_proxy_->OnQualityAdaptationChanged(cpu_counts, quality_counts);
+
+ fake_clock_.AdvanceTimeMilliseconds(10000);
+ statistics_proxy_.reset();
+ EXPECT_EQ(1,
+ metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
+ EXPECT_EQ(
+ 1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 24));
+}
+
TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) {
// First RTP packet sent.
UpdateDataCounters(kFirstSsrc);
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 9d209dc..7ec7ab3 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -767,7 +767,11 @@
video_frame.size() >
MaximumFrameSizeForBitrate(encoder_start_bitrate_bps_ / 1000)) {
RTC_LOG(LS_INFO) << "Dropping frame. Too large for target bitrate.";
+ int count = GetConstAdaptCounter().ResolutionCount(kQuality);
AdaptDown(kQuality);
+ if (GetConstAdaptCounter().ResolutionCount(kQuality) > count) {
+ stats_proxy_->OnInitialQualityResolutionAdaptDown();
+ }
++initial_rampup_;
return;
}