From 875841d9d8cf52d07326bc632e07761fc98dd3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85sa=20Persson?= Date: Mon, 8 Jan 2018 08:49:53 +0100 Subject: [PATCH] Exclude initial adapt downs in stats for quality adapt changes per minute. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Commit-Queue: Åsa Persson Cr-Commit-Position: refs/heads/master@{#21585} --- video/send_statistics_proxy.cc | 27 +++++++ video/send_statistics_proxy.h | 9 +++ video/send_statistics_proxy_unittest.cc | 96 +++++++++++++++++++++++++ video/video_stream_encoder.cc | 4 ++ 4 files changed, 136 insertions(+) diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc index 0d2f8f665e..4375e840c3 100644 --- a/video/send_statistics_proxy.cc +++ b/video/send_statistics_proxy.cc @@ -439,6 +439,11 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms( 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 @@ void SendStatisticsProxy::OnQualityAdaptationChanged( 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 @@ void SendStatisticsProxy::UpdateAdaptationStats( 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 c90f88ac3d..1aa836ebca 100644 --- a/video/send_statistics_proxy.h +++ b/video/send_statistics_proxy.h @@ -74,6 +74,7 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, 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 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, 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 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, 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 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, 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 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 1d2edc6a78..01cbfe8dda 100644 --- a/video/send_statistics_proxy_unittest.cc +++ b/video/send_statistics_proxy_unittest.cc @@ -513,6 +513,102 @@ TEST_F(SendStatisticsProxyTest, CpuAdaptChangesReported) { 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 9d209dc3a1..7ec7ab3cbc 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -767,7 +767,11 @@ void VideoStreamEncoder::EncodeVideoFrame(const VideoFrame& video_frame, 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; }