From 66d4b3741487da22a7bd63d07160052883413245 Mon Sep 17 00:00:00 2001 From: asapersson Date: Mon, 19 Dec 2016 06:50:53 -0800 Subject: [PATCH] Move histogram for number of pause events to per stream: "WebRTC.Call.NumberOfPauseEvents" -> "WebRTC.Video.NumberOfPauseEvents" Recorded if a certain time has passed (10 sec) since the first media packet was sent. Moved to per stream to know when media has started and to prevent logging stats for calls that was never in use. Add histogram for percentage of paused video time for sent video streams: "WebRTC.Video.PausedTimeInPercent" BUG=b/32659204 Review-Url: https://codereview.webrtc.org/2530393003 Cr-Commit-Position: refs/heads/master@{#15681} --- webrtc/video/end_to_end_tests.cc | 3 + webrtc/video/send_statistics_proxy.cc | 46 ++++++- webrtc/video/send_statistics_proxy.h | 26 ++-- .../video/send_statistics_proxy_unittest.cc | 127 ++++++++++++++++++ 4 files changed, 189 insertions(+), 13 deletions(-) diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc index de497f6d53..b7ae199de0 100644 --- a/webrtc/video/end_to_end_tests.cc +++ b/webrtc/video/end_to_end_tests.cc @@ -2391,6 +2391,9 @@ void EndToEndTest::VerifyHistogramStats(bool use_rtx, EXPECT_EQ(1, metrics::NumSamples(video_prefix + "EncodeTimeInMs")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.DecodeTimeInMs")); + EXPECT_EQ(1, metrics::NumSamples(video_prefix + "NumberOfPauseEvents")); + EXPECT_EQ(1, metrics::NumSamples(video_prefix + "PausedTimeInPercent")); + EXPECT_EQ(1, metrics::NumSamples(video_prefix + "BitrateSentInKbps")); EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.BitrateReceivedInKbps")); EXPECT_EQ(1, metrics::NumSamples(video_prefix + "MediaBitrateSentInKbps")); diff --git a/webrtc/video/send_statistics_proxy.cc b/webrtc/video/send_statistics_proxy.cc index 6c249f70a9..bd1441f903 100644 --- a/webrtc/video/send_statistics_proxy.cc +++ b/webrtc/video/send_statistics_proxy.cc @@ -322,6 +322,16 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms( int64_t elapsed_sec = (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000; if (elapsed_sec >= metrics::kMinRunTimeInSeconds) { + RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents", + target_rate_updates_.pause_resume_events); + + int paused_time_percent = + paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000); + if (paused_time_percent != -1) { + RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent", + paused_time_percent); + } + StreamDataCounters rtp; StreamDataCounters rtx; AccumulateRtxStats(current_stats, rtp_config.rtx.ssrcs, &rtp, &rtx); @@ -467,6 +477,25 @@ void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) { void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) { rtc::CritScope lock(&crit_); + if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0) + return; // Start on first non-zero bitrate, may initially be zero. + + int64_t now = clock_->TimeInMilliseconds(); + if (uma_container_->target_rate_updates_.last_ms != -1) { + bool was_paused = stats_.target_media_bitrate_bps == 0; + int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms; + uma_container_->paused_time_counter_.Add(was_paused, diff_ms); + + // Use last to not include update when stream is stopped and video disabled. + if (uma_container_->target_rate_updates_.last_paused_or_resumed) + ++uma_container_->target_rate_updates_.pause_resume_events; + + // Check if video is paused/resumed. + uma_container_->target_rate_updates_.last_paused_or_resumed = + (bitrate_bps == 0) != was_paused; + } + uma_container_->target_rate_updates_.last_ms = now; + stats_.target_media_bitrate_bps = bitrate_bps; } @@ -699,10 +728,11 @@ void SendStatisticsProxy::SampleCounter::Add(int sample) { ++num_samples; } -int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { +int SendStatisticsProxy::SampleCounter::Avg( + int64_t min_required_samples) const { if (num_samples < min_required_samples || num_samples == 0) return -1; - return (sum + (num_samples / 2)) / num_samples; + return static_cast((sum + (num_samples / 2)) / num_samples); } void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) { @@ -711,18 +741,24 @@ void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) { ++num_samples; } +void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) { + if (sample) + sum += count; + num_samples += count; +} int SendStatisticsProxy::BoolSampleCounter::Percent( - int min_required_samples) const { + int64_t min_required_samples) const { return Fraction(min_required_samples, 100.0f); } int SendStatisticsProxy::BoolSampleCounter::Permille( - int min_required_samples) const { + int64_t min_required_samples) const { return Fraction(min_required_samples, 1000.0f); } int SendStatisticsProxy::BoolSampleCounter::Fraction( - int min_required_samples, float multiplier) const { + int64_t min_required_samples, + float multiplier) const { if (num_samples < min_required_samples || num_samples == 0) return -1; return static_cast((sum * multiplier / num_samples) + 0.5f); diff --git a/webrtc/video/send_statistics_proxy.h b/webrtc/video/send_statistics_proxy.h index ec8bb6d28c..68d17e4a7a 100644 --- a/webrtc/video/send_statistics_proxy.h +++ b/webrtc/video/send_statistics_proxy.h @@ -113,30 +113,38 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, SampleCounter() : sum(0), num_samples(0) {} ~SampleCounter() {} void Add(int sample); - int Avg(int min_required_samples) const; + int Avg(int64_t min_required_samples) const; private: - int sum; - int num_samples; + int64_t sum; + int64_t num_samples; }; class BoolSampleCounter { public: BoolSampleCounter() : sum(0), num_samples(0) {} ~BoolSampleCounter() {} void Add(bool sample); - int Percent(int min_required_samples) const; - int Permille(int min_required_samples) const; + void Add(bool sample, int64_t count); + int Percent(int64_t min_required_samples) const; + int Permille(int64_t min_required_samples) const; private: - int Fraction(int min_required_samples, float multiplier) const; - int sum; - int num_samples; + int Fraction(int64_t min_required_samples, float multiplier) const; + int64_t sum; + int64_t num_samples; }; struct StatsUpdateTimes { StatsUpdateTimes() : resolution_update_ms(0), bitrate_update_ms(0) {} int64_t resolution_update_ms; int64_t bitrate_update_ms; }; + struct TargetRateUpdates { + TargetRateUpdates() + : pause_resume_events(0), last_paused_or_resumed(false), last_ms(-1) {} + int pause_resume_events; + bool last_paused_or_resumed; + int64_t last_ms; + }; struct QpCounters { SampleCounter vp8; // QP range: 0-127 SampleCounter vp9; // QP range: 0-255 @@ -192,6 +200,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver, RateCounter sent_fps_counter_; int64_t first_rtcp_stats_time_ms_; int64_t first_rtp_stats_time_ms_; + BoolSampleCounter paused_time_counter_; + TargetRateUpdates target_rate_updates_; ReportBlockStats report_block_stats_; const VideoSendStream::Stats start_stats_; std::map diff --git a/webrtc/video/send_statistics_proxy_unittest.cc b/webrtc/video/send_statistics_proxy_unittest.cc index acb8a306ca..7fa3ae3638 100644 --- a/webrtc/video/send_statistics_proxy_unittest.cc +++ b/webrtc/video/send_statistics_proxy_unittest.cc @@ -88,6 +88,13 @@ class SendStatisticsProxyTest : public ::testing::Test { return it->second; } + void UpdateDataCounters(uint32_t ssrc) { + StreamDataCountersCallback* proxy = + static_cast(statistics_proxy_.get()); + StreamDataCounters counters; + proxy->DataCountersUpdated(counters, ssrc); + } + void ExpectEqual(VideoSendStream::Stats one, VideoSendStream::Stats other) { EXPECT_EQ(one.input_frame_rate, other.input_frame_rate); EXPECT_EQ(one.encode_frame_rate, other.encode_frame_rate); @@ -510,6 +517,114 @@ TEST_F(SendStatisticsProxyTest, CodecTypeHistogramIsUpdated) { EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.Encoder.CodecType")); } +TEST_F(SendStatisticsProxyTest, PauseEventHistogramIsUpdated) { + // First RTP packet sent. + UpdateDataCounters(kFirstSsrc); + + // Min runtime has passed. + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000); + statistics_proxy_.reset(); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.NumberOfPauseEvents", 0)); +} + +TEST_F(SendStatisticsProxyTest, + PauseEventHistogramIsNotUpdatedIfMinRuntimeHasNotPassed) { + // First RTP packet sent. + UpdateDataCounters(kFirstSsrc); + + // Min runtime has not passed. + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000 - 1); + statistics_proxy_.reset(); + EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); + EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.PausedTimeInPercent")); +} + +TEST_F(SendStatisticsProxyTest, + PauseEventHistogramIsNotUpdatedIfNoMediaIsSent) { + // First RTP packet not sent. + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000); + statistics_proxy_.reset(); + EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); +} + +TEST_F(SendStatisticsProxyTest, NoPauseEvent) { + // First RTP packet sent and min runtime passed. + UpdateDataCounters(kFirstSsrc); + + // No change. Video: 10000 ms, paused: 0 ms (0%). + statistics_proxy_->OnSetEncoderTargetRate(50000); + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000); + statistics_proxy_->OnSetEncoderTargetRate(0); // VideoSendStream::Stop + + statistics_proxy_.reset(); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.NumberOfPauseEvents", 0)); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.PausedTimeInPercent")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.PausedTimeInPercent", 0)); +} + +TEST_F(SendStatisticsProxyTest, OnePauseEvent) { + // First RTP packet sent and min runtime passed. + UpdateDataCounters(kFirstSsrc); + + // One change. Video: 7000 ms, paused: 3000 ms (30%). + statistics_proxy_->OnSetEncoderTargetRate(50000); + fake_clock_.AdvanceTimeMilliseconds(7000); + statistics_proxy_->OnSetEncoderTargetRate(0); + fake_clock_.AdvanceTimeMilliseconds(3000); + statistics_proxy_->OnSetEncoderTargetRate(0); // VideoSendStream::Stop + + statistics_proxy_.reset(); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.NumberOfPauseEvents", 1)); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.PausedTimeInPercent")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.PausedTimeInPercent", 30)); +} + +TEST_F(SendStatisticsProxyTest, TwoPauseEvents) { + // First RTP packet sent. + UpdateDataCounters(kFirstSsrc); + + // Two changes. Video: 19000 ms, paused: 1000 ms (5%). + statistics_proxy_->OnSetEncoderTargetRate(0); + fake_clock_.AdvanceTimeMilliseconds(1000); + statistics_proxy_->OnSetEncoderTargetRate(50000); // Starts on bitrate > 0. + fake_clock_.AdvanceTimeMilliseconds(7000); + statistics_proxy_->OnSetEncoderTargetRate(60000); + fake_clock_.AdvanceTimeMilliseconds(3000); + statistics_proxy_->OnSetEncoderTargetRate(0); + fake_clock_.AdvanceTimeMilliseconds(250); + statistics_proxy_->OnSetEncoderTargetRate(0); + fake_clock_.AdvanceTimeMilliseconds(750); + statistics_proxy_->OnSetEncoderTargetRate(60000); + fake_clock_.AdvanceTimeMilliseconds(5000); + statistics_proxy_->OnSetEncoderTargetRate(50000); + fake_clock_.AdvanceTimeMilliseconds(4000); + statistics_proxy_->OnSetEncoderTargetRate(0); // VideoSendStream::Stop + + statistics_proxy_.reset(); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.NumberOfPauseEvents")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.NumberOfPauseEvents", 2)); + EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.PausedTimeInPercent")); + EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.PausedTimeInPercent", 5)); +} + +TEST_F(SendStatisticsProxyTest, + PausedTimeHistogramIsNotUpdatedIfMinRuntimeHasNotPassed) { + // First RTP packet sent. + UpdateDataCounters(kFirstSsrc); + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000); + + // Min runtime has not passed. + statistics_proxy_->OnSetEncoderTargetRate(50000); + fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000 - 1); + statistics_proxy_->OnSetEncoderTargetRate(0); // VideoSendStream::Stop + + statistics_proxy_.reset(); + EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.PausedTimeInPercent")); +} + TEST_F(SendStatisticsProxyTest, VerifyQpHistogramStats_Vp8) { EncodedImage encoded_image; CodecSpecificInfo codec_info; @@ -739,6 +854,18 @@ TEST_F(SendStatisticsProxyTest, GetStatsReportsBandwidthLimitedResolution) { EXPECT_TRUE(statistics_proxy_->GetStats().bw_limited_resolution); } +TEST_F(SendStatisticsProxyTest, GetStatsReportsTargetMediaBitrate) { + // Initially zero. + EXPECT_EQ(0, statistics_proxy_->GetStats().target_media_bitrate_bps); + + const int kBitrate = 100000; + statistics_proxy_->OnSetEncoderTargetRate(kBitrate); + EXPECT_EQ(kBitrate, statistics_proxy_->GetStats().target_media_bitrate_bps); + + statistics_proxy_->OnSetEncoderTargetRate(0); + EXPECT_EQ(0, statistics_proxy_->GetStats().target_media_bitrate_bps); +} + TEST_F(SendStatisticsProxyTest, NoSubstreams) { uint32_t excluded_ssrc = std::max(