Update stats for cpu/quality adaptation changes to excluded time when video is suspended.

BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/2804653002
Cr-Commit-Position: refs/heads/master@{#17583}
diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc
index afaeed9..822ed97 100644
--- a/webrtc/video/send_statistics_proxy.cc
+++ b/webrtc/video/send_statistics_proxy.cc
@@ -89,6 +89,8 @@
       start_ms_(clock->TimeInMilliseconds()),
       last_sent_frame_timestamp_(0),
       encode_time_(kEncodeTimeWeigthFactor),
+      quality_downscales_(-1),
+      cpu_downscales_(-1),
       uma_container_(
           new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
 }
@@ -293,22 +295,25 @@
     }
   }
 
-  quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
-  int64_t elapsed_sec = quality_scaling_timer_.total_ms / 1000;
-  if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
-    int quality_changes = current_stats.number_of_quality_adapt_changes -
-                          start_stats_.number_of_quality_adapt_changes;
-    RTC_HISTOGRAMS_COUNTS_100(kIndex,
-                              uma_prefix_ + "AdaptChangesPerMinute.Quality",
-                              quality_changes * 60 / elapsed_sec);
-  }
-  cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
-  elapsed_sec = cpu_scaling_timer_.total_ms / 1000;
-  if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
-    int cpu_changes = current_stats.number_of_cpu_adapt_changes -
-                      start_stats_.number_of_cpu_adapt_changes;
-    RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "AdaptChangesPerMinute.Cpu",
-                              cpu_changes * 60 / elapsed_sec);
+  if (first_rtp_stats_time_ms_ != -1) {
+    quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
+    int64_t elapsed_sec = quality_scaling_timer_.total_ms / 1000;
+    if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
+      int quality_changes = current_stats.number_of_quality_adapt_changes -
+                            start_stats_.number_of_quality_adapt_changes;
+      RTC_HISTOGRAMS_COUNTS_100(kIndex,
+                                uma_prefix_ + "AdaptChangesPerMinute.Quality",
+                                quality_changes * 60 / elapsed_sec);
+    }
+    cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
+    elapsed_sec = cpu_scaling_timer_.total_ms / 1000;
+    if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
+      int cpu_changes = current_stats.number_of_cpu_adapt_changes -
+                        start_stats_.number_of_cpu_adapt_changes;
+      RTC_HISTOGRAMS_COUNTS_100(kIndex,
+                                uma_prefix_ + "AdaptChangesPerMinute.Cpu",
+                                cpu_changes * 60 / elapsed_sec);
+    }
   }
 
   if (first_rtcp_stats_time_ms_ != -1) {
@@ -473,6 +478,7 @@
 }
 
 void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
+  int64_t now_ms = clock_->TimeInMilliseconds();
   rtc::CritScope lock(&crit_);
   stats_.suspended = is_suspended;
   if (is_suspended) {
@@ -488,7 +494,15 @@
     uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
     uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
     uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
+    // Stop adaptation stats.
+    uma_container_->cpu_scaling_timer_.Stop(now_ms);
+    uma_container_->quality_scaling_timer_.Stop(now_ms);
   } else {
+    // Start adaptation stats if scaling is enabled.
+    if (cpu_downscales_ >= 0)
+      uma_container_->cpu_scaling_timer_.Start(now_ms);
+    if (quality_downscales_ >= 0)
+      uma_container_->quality_scaling_timer_.Start(now_ms);
     // Stop pause explicitly for stats that may be zero/not updated for some
     // time.
     uma_container_->rtx_byte_counter_.ProcessAndStopPause();
@@ -704,14 +718,16 @@
 
 void SendStatisticsProxy::SetCpuScalingStats(int num_cpu_downscales) {
   rtc::CritScope lock(&crit_);
+  cpu_downscales_ = num_cpu_downscales;
   stats_.cpu_limited_resolution = num_cpu_downscales > 0;
 
   if (num_cpu_downscales >= 0) {
     // Scaling enabled.
-    uma_container_->cpu_scaling_timer_.Start(clock_->TimeInMilliseconds());
-  } else {
-    uma_container_->cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
+    if (!stats_.suspended)
+      uma_container_->cpu_scaling_timer_.Start(clock_->TimeInMilliseconds());
+    return;
   }
+  uma_container_->cpu_scaling_timer_.Stop(clock_->TimeInMilliseconds());
 }
 
 void SendStatisticsProxy::SetQualityScalingStats(int num_quality_downscales) {
@@ -721,10 +737,13 @@
 
   if (num_quality_downscales >= 0) {
     // Scaling enabled.
-    uma_container_->quality_scaling_timer_.Start(clock_->TimeInMilliseconds());
-  } else {
-    uma_container_->quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
+    if (!stats_.suspended) {
+      uma_container_->quality_scaling_timer_.Start(
+          clock_->TimeInMilliseconds());
+    }
+    return;
   }
+  uma_container_->quality_scaling_timer_.Stop(clock_->TimeInMilliseconds());
 }
 
 void SendStatisticsProxy::OnCpuRestrictedResolutionChanged(
diff --git a/webrtc/video/send_statistics_proxy.h b/webrtc/video/send_statistics_proxy.h
index 23558ab..10d72c7 100644
--- a/webrtc/video/send_statistics_proxy.h
+++ b/webrtc/video/send_statistics_proxy.h
@@ -170,7 +170,8 @@
   uint32_t last_sent_frame_timestamp_ GUARDED_BY(crit_);
   std::map<uint32_t, StatsUpdateTimes> update_times_ GUARDED_BY(crit_);
   rtc::ExpFilter encode_time_ GUARDED_BY(crit_);
-  int quality_downscales_ GUARDED_BY(crit_) = 0;
+  int quality_downscales_ GUARDED_BY(crit_);
+  int cpu_downscales_ GUARDED_BY(crit_);
 
   // Contains stats used for UMA histograms. These stats will be reset if
   // content type changes between real-time video and screenshare, since these
diff --git a/webrtc/video/send_statistics_proxy_unittest.cc b/webrtc/video/send_statistics_proxy_unittest.cc
index b3aa506..e2cb2fd 100644
--- a/webrtc/video/send_statistics_proxy_unittest.cc
+++ b/webrtc/video/send_statistics_proxy_unittest.cc
@@ -422,6 +422,8 @@
 }
 
 TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_ScalingNotEnabled) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
   // Min runtime has passed.
   fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
   statistics_proxy_.reset();
@@ -431,6 +433,8 @@
 }
 
 TEST_F(SendStatisticsProxyTest, AdaptChangesNotReported_MinRuntimeNotPassed) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
   // Enable scaling.
   statistics_proxy_->SetQualityScalingStats(0);
   statistics_proxy_->SetCpuScalingStats(0);
@@ -443,6 +447,8 @@
 }
 
 TEST_F(SendStatisticsProxyTest, ZeroCpuAdaptChangesReported) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
   // Enable scaling.
   statistics_proxy_->SetCpuScalingStats(0);
   // Min runtime has passed.
@@ -453,6 +459,8 @@
 }
 
 TEST_F(SendStatisticsProxyTest, ZeroQualityAdaptChangesReported) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
   // Enable scaling.
   statistics_proxy_->SetQualityScalingStats(0);
   // Min runtime has passed.
@@ -465,8 +473,10 @@
 }
 
 TEST_F(SendStatisticsProxyTest, CpuAdaptChangesReported) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
   // Enable scaling.
-  // Adapt changes: 1, elapsed time: 10 ms => 6 per minute.
+  // Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
   statistics_proxy_->SetCpuScalingStats(0);
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
   fake_clock_.AdvanceTimeMilliseconds(10000);
@@ -476,12 +486,15 @@
 }
 
 TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
+
   // Disable scaling.
   statistics_proxy_->SetQualityScalingStats(-1);
   fake_clock_.AdvanceTimeMilliseconds(10000);
 
   // Enable scaling.
-  // Adapt changes: 2, elapsed time: 20 ms.
+  // Adapt changes: 2, elapsed time: 20 sec.
   statistics_proxy_->SetQualityScalingStats(0);
   fake_clock_.AdvanceTimeMilliseconds(5000);
   statistics_proxy_->SetQualityScalingStats(1);
@@ -495,7 +508,7 @@
   fake_clock_.AdvanceTimeMilliseconds(30000);
 
   // Enable scaling.
-  // Adapt changes: 1, elapsed time: 10 ms.
+  // Adapt changes: 1, elapsed time: 10 sec.
   statistics_proxy_->SetQualityScalingStats(0);
   statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
   fake_clock_.AdvanceTimeMilliseconds(10000);
@@ -506,7 +519,7 @@
   statistics_proxy_->SetQualityScalingStats(-1);
   fake_clock_.AdvanceTimeMilliseconds(20000);
 
-  // Adapt changes: 3, elapsed time: 30 ms => 6 per minute.
+  // Adapt changes: 3, elapsed time: 30 sec => 6 per minute.
   statistics_proxy_.reset();
   EXPECT_EQ(1,
             metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
@@ -514,23 +527,128 @@
       1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 6));
 }
 
-TEST_F(SendStatisticsProxyTest, AdaptChangesStatsRestartsOnFirstSentPacket) {
-  StreamDataCounters counters;
-  StreamDataCountersCallback* proxy =
-      static_cast<StreamDataCountersCallback*>(statistics_proxy_.get());
+TEST_F(SendStatisticsProxyTest,
+       AdaptChangesNotReported_ScalingNotEnabledVideoResumed) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
 
+  // Suspend and resume video.
+  statistics_proxy_->OnSuspendChange(true);
+  fake_clock_.AdvanceTimeMilliseconds(5000);
+  statistics_proxy_->OnSuspendChange(false);
+
+  // Min runtime has passed but scaling not enabled.
+  fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
+  statistics_proxy_.reset();
+  EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
+  EXPECT_EQ(0,
+            metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
+}
+
+TEST_F(SendStatisticsProxyTest, QualityAdaptChangesStatsExcludesSuspendedTime) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
+
+  // Enable scaling.
+  // Adapt changes: 2, elapsed time: 20 sec.
+  statistics_proxy_->SetQualityScalingStats(0);
+  fake_clock_.AdvanceTimeMilliseconds(20000);
+  statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
+  statistics_proxy_->OnQualityRestrictedResolutionChanged(2);
+
+  // Suspend and resume video.
+  statistics_proxy_->OnSuspendChange(true);
+  fake_clock_.AdvanceTimeMilliseconds(30000);
+  statistics_proxy_->OnSuspendChange(false);
+
+  // Adapt changes: 1, elapsed time: 10 sec.
+  statistics_proxy_->OnQualityRestrictedResolutionChanged(3);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+
+  // Adapt changes: 3, elapsed time: 30 sec => 6 per minute.
+  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, CpuAdaptChangesStatsExcludesSuspendedTime) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
+
+  // Video not suspended.
+  statistics_proxy_->OnSuspendChange(false);
+  fake_clock_.AdvanceTimeMilliseconds(30000);
+
+  // Enable scaling.
+  // Adapt changes: 1, elapsed time: 20 sec.
+  statistics_proxy_->SetCpuScalingStats(0);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+  statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
+
+  // Video not suspended, stats time already started.
+  statistics_proxy_->OnSuspendChange(false);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+
+  // Disable scaling.
+  statistics_proxy_->SetCpuScalingStats(-1);
+  fake_clock_.AdvanceTimeMilliseconds(30000);
+
+  // Suspend and resume video, stats time not started when scaling not enabled.
+  statistics_proxy_->OnSuspendChange(true);
+  fake_clock_.AdvanceTimeMilliseconds(30000);
+  statistics_proxy_->OnSuspendChange(false);
+  fake_clock_.AdvanceTimeMilliseconds(30000);
+
+  // Enable scaling.
+  // Adapt changes: 1, elapsed time: 10 sec.
+  statistics_proxy_->SetCpuScalingStats(0);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+  statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
+
+  // Adapt changes: 2, elapsed time: 30 sec => 4 per minute.
+  statistics_proxy_.reset();
+  EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
+  EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Cpu", 4));
+}
+
+TEST_F(SendStatisticsProxyTest, AdaptChangesStatsNotStartedIfVideoSuspended) {
+  // First RTP packet sent.
+  UpdateDataCounters(kFirstSsrc);
+
+  // Video suspended.
+  statistics_proxy_->OnSuspendChange(true);
+
+  // Enable scaling, stats time not started when suspended.
+  statistics_proxy_->SetCpuScalingStats(0);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+
+  // Resume video, stats time started.
+  // Adapt changes: 1, elapsed time: 10 sec.
+  statistics_proxy_->OnSuspendChange(false);
+  fake_clock_.AdvanceTimeMilliseconds(10000);
+  statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
+
+  // Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
+  statistics_proxy_.reset();
+  EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
+  EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Cpu", 6));
+}
+
+TEST_F(SendStatisticsProxyTest, AdaptChangesStatsRestartsOnFirstSentPacket) {
   // Send first packet, scaling enabled.
   // Elapsed time before first packet is sent should be excluded.
   statistics_proxy_->SetQualityScalingStats(0);
   fake_clock_.AdvanceTimeMilliseconds(10000);
-  proxy->DataCountersUpdated(counters, kFirstSsrc);
+  UpdateDataCounters(kFirstSsrc);
 
-  // Adapt changes: 1, elapsed time: 10 ms.
+  // Adapt changes: 1, elapsed time: 10 sec.
   fake_clock_.AdvanceTimeMilliseconds(10000);
   statistics_proxy_->OnQualityRestrictedResolutionChanged(1);
-  proxy->DataCountersUpdated(counters, kFirstSsrc);
+  UpdateDataCounters(kFirstSsrc);
 
-  // Adapt changes: 1, elapsed time: 10 ms => 6 per minute.
+  // Adapt changes: 1, elapsed time: 10 sec => 6 per minute.
   statistics_proxy_.reset();
   EXPECT_EQ(1,
             metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
@@ -539,10 +657,6 @@
 }
 
 TEST_F(SendStatisticsProxyTest, AdaptChangesStatsStartedAfterFirstSentPacket) {
-  StreamDataCounters counters;
-  StreamDataCountersCallback* proxy =
-      static_cast<StreamDataCountersCallback*>(statistics_proxy_.get());
-
   // Enable and disable scaling.
   statistics_proxy_->SetCpuScalingStats(0);
   fake_clock_.AdvanceTimeMilliseconds(60000);
@@ -550,29 +664,30 @@
 
   // Send first packet, scaling disabled.
   // Elapsed time before first packet is sent should be excluded.
-  proxy->DataCountersUpdated(counters, kFirstSsrc);
+  UpdateDataCounters(kFirstSsrc);
   fake_clock_.AdvanceTimeMilliseconds(60000);
 
   // Enable scaling.
   statistics_proxy_->SetCpuScalingStats(0);
   fake_clock_.AdvanceTimeMilliseconds(10000);
-  proxy->DataCountersUpdated(counters, kFirstSsrc);
+  UpdateDataCounters(kFirstSsrc);
 
-  // Adapt changes: 1, elapsed time: 20 ms.
+  // Adapt changes: 1, elapsed time: 20 sec.
   fake_clock_.AdvanceTimeMilliseconds(10000);
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
 
-  // Adapt changes: 1, elapsed time: 20 ms => 3 per minute.
+  // Adapt changes: 1, elapsed time: 20 sec => 3 per minute.
   statistics_proxy_.reset();
   EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Cpu"));
   EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Cpu", 3));
 }
 
 TEST_F(SendStatisticsProxyTest, AdaptChangesReportedAfterContentSwitch) {
-  // Enable scaling.
+  // First RTP packet sent, scaling enabled.
+  UpdateDataCounters(kFirstSsrc);
   statistics_proxy_->SetCpuScalingStats(0);
 
-  // Adapt changes: 2, elapsed time: 15 ms => 8 per minute.
+  // Adapt changes: 2, elapsed time: 15 sec => 8 per minute.
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
   fake_clock_.AdvanceTimeMilliseconds(6000);
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
@@ -587,10 +702,11 @@
   EXPECT_EQ(0,
             metrics::NumSamples("WebRTC.Video.AdaptChangesPerMinute.Quality"));
 
-  // Enable scaling.
+  // First RTP packet sent, scaling enabled.
+  UpdateDataCounters(kFirstSsrc);
   statistics_proxy_->SetCpuScalingStats(0);
 
-  // Adapt changes: 4, elapsed time: 120 ms => 2 per minute.
+  // Adapt changes: 4, elapsed time: 120 sec => 2 per minute.
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
   statistics_proxy_->OnCpuRestrictedResolutionChanged(true);
@@ -1034,7 +1150,9 @@
 
 TEST_F(SendStatisticsProxyTest,
        QualityLimitedHistogramsUpdatedWhenEnabled_NoResolutionDownscale) {
+  const int kNumDownscales = 0;
   EncodedImage encoded_image;
+  statistics_proxy_->SetQualityScalingStats(kNumDownscales);
   for (int i = 0; i < SendStatisticsProxy::kMinRequiredMetricsSamples; ++i)
     statistics_proxy_->OnSendEncodedImage(encoded_image, &kDefaultCodecInfo);
 
diff --git a/webrtc/video/vie_encoder_unittest.cc b/webrtc/video/vie_encoder_unittest.cc
index a5bc914..704f90d 100644
--- a/webrtc/video/vie_encoder_unittest.cc
+++ b/webrtc/video/vie_encoder_unittest.cc
@@ -788,7 +788,7 @@
   sink_.WaitForEncodedFrame(frame_timestamp);
   frame_timestamp += kFrameIntervalMs;
 
-  // Default degradation preference in maintain-framerate, so will lower max
+  // Default degradation preference is maintain-framerate, so will lower max
   // wanted resolution.
   EXPECT_FALSE(video_source_.sink_wants().target_pixel_count);
   EXPECT_LT(video_source_.sink_wants().max_pixel_count,
@@ -1112,8 +1112,8 @@
 TEST_F(ViEEncoderTest, StatsTracksAdaptationStatsWhenSwitchingSource) {
   vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
 
-  int kWidth = 1280;
-  int kHeight = 720;
+  const int kWidth = 1280;
+  const int kHeight = 720;
   int sequence = 1;
 
   video_source_.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
@@ -1404,7 +1404,7 @@
   sink_.ExpectDroppedFrame();
 
   // Expect the sink_wants to specify a scaled frame.
-  EXPECT_LT(video_source_.sink_wants().max_pixel_count, 1000 * 1000);
+  EXPECT_LT(video_source_.sink_wants().max_pixel_count, kWidth * kHeight);
 
   int last_pixel_count = video_source_.sink_wants().max_pixel_count;