diff --git a/video/receive_statistics_proxy_unittest.cc b/video/receive_statistics_proxy_unittest.cc index 176c89c13e..29e4483c2b 100644 --- a/video/receive_statistics_proxy_unittest.cc +++ b/video/receive_statistics_proxy_unittest.cc @@ -1104,6 +1104,41 @@ TEST_P(ReceiveStatisticsProxyTest, FreezesAreReported) { } } +TEST_P(ReceiveStatisticsProxyTest, HarmonicFrameRateIsReported) { + const VideoContentType content_type = GetParam(); + const int kInterFrameDelayMs = 33; + const int kFreezeDelayMs = 200; + const int kCallDurationMs = + kMinRequiredSamples * kInterFrameDelayMs + kFreezeDelayMs; + webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight); + + for (int i = 0; i < kMinRequiredSamples; ++i) { + fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs); + statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type); + statistics_proxy_->OnRenderedFrame(frame); + } + // Add extra freeze. + fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs); + statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type); + statistics_proxy_->OnRenderedFrame(frame); + + statistics_proxy_.reset(); + double kSumSquaredInterframeDelaysSecs = + (kMinRequiredSamples - 1) * + (kInterFrameDelayMs / 1000.0 * kInterFrameDelayMs / 1000.0); + kSumSquaredInterframeDelaysSecs += + kFreezeDelayMs / 1000.0 * kFreezeDelayMs / 1000.0; + const int kExpectedHarmonicFrameRateFps = + std::round(kCallDurationMs / (1000 * kSumSquaredInterframeDelaysSecs)); + if (videocontenttypehelpers::IsScreenshare(content_type)) { + EXPECT_EQ(kExpectedHarmonicFrameRateFps, + metrics::MinSample("WebRTC.Video.Screenshare.HarmonicFrameRate")); + } else { + EXPECT_EQ(kExpectedHarmonicFrameRateFps, + metrics::MinSample("WebRTC.Video.HarmonicFrameRate")); + } +} + TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) { const VideoContentType content_type = GetParam(); const int kInterFrameDelayMs = 33; diff --git a/video/video_quality_observer.cc b/video/video_quality_observer.cc index 5092cb8558..ebe5bb119c 100644 --- a/video/video_quality_observer.cc +++ b/video/video_quality_observer.cc @@ -11,6 +11,7 @@ #include "video/video_quality_observer.h" #include +#include #include #include @@ -41,6 +42,7 @@ VideoQualityObserver::VideoQualityObserver(VideoContentType content_type) last_frame_pixels_(0), is_last_frame_blocky_(false), last_unfreeze_time_(0), + sum_squared_interframe_delays_secs_(0.0), time_in_resolution_ms_(3, 0), current_resolution_(Resolution::Low), num_resolution_downgrades_(0), @@ -118,6 +120,15 @@ void VideoQualityObserver::UpdateHistograms() { num_freezes_per_minute); log_stream << uma_prefix << ".NumberFreezesPerMinute " << num_freezes_per_minute << "\n"; + + if (sum_squared_interframe_delays_secs_ > 0.0) { + int harmonic_framerate_fps = std::round( + video_duration_ms / (1000 * sum_squared_interframe_delays_secs_)); + RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".HarmonicFrameRate", + harmonic_framerate_fps); + log_stream << uma_prefix << ".HarmonicFrameRate " + << harmonic_framerate_fps << "\n"; + } } RTC_LOG(LS_INFO) << log_stream.str(); } @@ -134,7 +145,10 @@ void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame, if (!is_paused_ && num_frames_rendered_ > 1) { // Process inter-frame delay. - int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_; + const int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_; + const float interframe_delays_secs = interframe_delay_ms / 1000.0; + sum_squared_interframe_delays_secs_ += + interframe_delays_secs * interframe_delays_secs; render_interframe_delays_.Add(interframe_delay_ms); absl::optional avg_interframe_delay = render_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze); diff --git a/video/video_quality_observer.h b/video/video_quality_observer.h index af69dd4496..0c60d9a4c7 100644 --- a/video/video_quality_observer.h +++ b/video/video_quality_observer.h @@ -57,7 +57,7 @@ class VideoQualityObserver { // Decoded timestamp of the last delayed frame. int64_t last_unfreeze_time_; rtc::SampleCounter render_interframe_delays_; - rtc::SampleCounter decode_interframe_delays_; + double sum_squared_interframe_delays_secs_; // An inter-frame delay is counted as a freeze if it's significantly longer // than average inter-frame delay. rtc::SampleCounter freezes_durations_;