Implement QualityLimitationReasonTracker and expose "reason".
This CL implements the logic behind qualityLimitationReason[1] and
qualityLimitationDurations[2]
This CL also exposes qualityLimitationReason in the standard getStats()
API, but does not expose qualityLimitationDurations because that is
blocked on supporting the "record<>" type in RTCStatsMember[3].
[1] https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationreason
[2] https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations
[3] https://crbug.com/webrtc/10685
TBR=stefan@webrtc.org
Bug: webrtc:10451, webrtc:10686
Change-Id: Ifff0be4ddd64eaec23d59c02af99fdbb1feb3841
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138825
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28090}
diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc
index 58514e5..928bc8b 100644
--- a/video/send_statistics_proxy_unittest.cc
+++ b/video/send_statistics_proxy_unittest.cc
@@ -1067,6 +1067,145 @@
"WebRTC.Video.Screenshare.AdaptChangesPerMinute.Quality"));
}
+TEST_F(SendStatisticsProxyTest,
+ QualityLimitationReasonIsCpuWhenCpuIsResolutionLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ cpu_counts.num_resolution_reductions = 1;
+
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kCpu,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest,
+ QualityLimitationReasonIsCpuWhenCpuIsFramerateLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ cpu_counts.num_framerate_reductions = 1;
+
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kCpu,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest,
+ QualityLimitationReasonIsBandwidthWhenQualityIsResolutionLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ quality_counts.num_resolution_reductions = 1;
+
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kQuality, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kBandwidth,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest,
+ QualityLimitationReasonIsBandwidthWhenQualityIsFramerateLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ quality_counts.num_framerate_reductions = 1;
+
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kQuality, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kBandwidth,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest,
+ QualityLimitationReasonIsBandwidthWhenBothCpuAndQualityIsLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ cpu_counts.num_resolution_reductions = 1;
+ quality_counts.num_resolution_reductions = 1;
+
+ // Even if the last adaptation reason is kCpu, if the counters indicate being
+ // both CPU and quality (=bandwidth) limited, kBandwidth takes precedence.
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kBandwidth,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest, QualityLimitationReasonIsNoneWhenNotLimited) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ // Observe a limitation due to CPU. This makes sure the test doesn't pass
+ // due to "none" being the default value.
+ cpu_counts.num_resolution_reductions = 1;
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+ // Go back to not being limited.
+ cpu_counts.num_resolution_reductions = 0;
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kNone, cpu_counts,
+ quality_counts);
+
+ EXPECT_EQ(QualityLimitationReason::kNone,
+ statistics_proxy_->GetStats().quality_limitation_reason);
+}
+
+TEST_F(SendStatisticsProxyTest, QualityLimitationDurationIncreasesWithTime) {
+ SendStatisticsProxy::AdaptationSteps cpu_counts;
+ SendStatisticsProxy::AdaptationSteps quality_counts;
+
+ // Not limited for 3000 ms
+ fake_clock_.AdvanceTimeMilliseconds(3000);
+ // CPU limited for 2000 ms
+ cpu_counts.num_resolution_reductions = 1;
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+ fake_clock_.AdvanceTimeMilliseconds(2000);
+ // Bandwidth limited for 1000 ms
+ cpu_counts.num_resolution_reductions = 0;
+ quality_counts.num_resolution_reductions = 1;
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kQuality, cpu_counts,
+ quality_counts);
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ // CPU limited for another 2000 ms
+ cpu_counts.num_resolution_reductions = 1;
+ quality_counts.num_resolution_reductions = 0;
+ statistics_proxy_->OnAdaptationChanged(
+ VideoStreamEncoderObserver::AdaptationReason::kCpu, cpu_counts,
+ quality_counts);
+ fake_clock_.AdvanceTimeMilliseconds(2000);
+
+ auto quality_limitation_durations_ms =
+ statistics_proxy_->GetStats().quality_limitation_durations_ms;
+
+ EXPECT_EQ(3000,
+ quality_limitation_durations_ms[QualityLimitationReason::kNone]);
+ EXPECT_EQ(4000,
+ quality_limitation_durations_ms[QualityLimitationReason::kCpu]);
+ EXPECT_EQ(
+ 1000,
+ quality_limitation_durations_ms[QualityLimitationReason::kBandwidth]);
+ EXPECT_EQ(0,
+ quality_limitation_durations_ms[QualityLimitationReason::kOther]);
+}
+
TEST_F(SendStatisticsProxyTest, SwitchContentTypeUpdatesHistograms) {
for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
statistics_proxy_->OnIncomingFrame(kWidth, kHeight);