From 0944a80f3e12fd877e5856e68d941699d8f3e8c3 Mon Sep 17 00:00:00 2001 From: asapersson Date: Fri, 7 Apr 2017 00:57:58 -0700 Subject: [PATCH] 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} --- webrtc/video/send_statistics_proxy.cc | 63 ++++--- webrtc/video/send_statistics_proxy.h | 3 +- .../video/send_statistics_proxy_unittest.cc | 166 +++++++++++++++--- webrtc/video/vie_encoder_unittest.cc | 8 +- 4 files changed, 189 insertions(+), 51 deletions(-) diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc index afaeed904d..822ed97695 100644 --- a/webrtc/video/send_statistics_proxy.cc +++ b/webrtc/video/send_statistics_proxy.cc @@ -89,6 +89,8 @@ SendStatisticsProxy::SendStatisticsProxy( 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 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms( } } - 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::OnEncodedFrameTimeMeasured( } 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 @@ void SendStatisticsProxy::OnSuspendChange(bool is_suspended) { 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::OnIncomingFrame(int width, int height) { 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 @@ void SendStatisticsProxy::SetQualityScalingStats(int num_quality_downscales) { 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 23558ab8f6..10d72c7796 100644 --- a/webrtc/video/send_statistics_proxy.h +++ b/webrtc/video/send_statistics_proxy.h @@ -170,7 +170,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, uint32_t last_sent_frame_timestamp_ GUARDED_BY(crit_); std::map 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 b3aa50667d..e2cb2fdbb2 100644 --- a/webrtc/video/send_statistics_proxy_unittest.cc +++ b/webrtc/video/send_statistics_proxy_unittest.cc @@ -422,6 +422,8 @@ TEST_F(SendStatisticsProxyTest, GetStatsReportsQualityResolutionChanges) { } 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_ScalingNotEnabled) { } 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, AdaptChangesNotReported_MinRuntimeNotPassed) { } 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, ZeroCpuAdaptChangesReported) { } 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, ZeroQualityAdaptChangesReported) { } 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, CpuAdaptChangesReported) { } 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 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) { 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 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) { 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 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsExcludesDisabledTime) { 1, metrics::NumEvents("WebRTC.Video.AdaptChangesPerMinute.Quality", 6)); } -TEST_F(SendStatisticsProxyTest, AdaptChangesStatsRestartsOnFirstSentPacket) { - StreamDataCounters counters; - StreamDataCountersCallback* proxy = - static_cast(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, AdaptChangesStatsRestartsOnFirstSentPacket) { } TEST_F(SendStatisticsProxyTest, AdaptChangesStatsStartedAfterFirstSentPacket) { - StreamDataCounters counters; - StreamDataCountersCallback* proxy = - static_cast(statistics_proxy_.get()); - // Enable and disable scaling. statistics_proxy_->SetCpuScalingStats(0); fake_clock_.AdvanceTimeMilliseconds(60000); @@ -550,29 +664,30 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesStatsStartedAfterFirstSentPacket) { // 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 @@ TEST_F(SendStatisticsProxyTest, AdaptChangesReportedAfterContentSwitch) { 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, 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 a5bc91474d..704f90d9a5 100644 --- a/webrtc/video/vie_encoder_unittest.cc +++ b/webrtc/video/vie_encoder_unittest.cc @@ -788,7 +788,7 @@ TEST_F(ViEEncoderTest, SinkWantsStoredByDegradationPreference) { 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, QualityAdaptationStatsAreResetWhenScalerIsDisabled) { 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 @@ TEST_F(ViEEncoderTest, DropsFramesAndScalesWhenBitrateIsTooLow) { 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;