Harmonic frame rate metric.
This adds calculation and reporting of harmonic frame rate (HFR) metric.
HFR is calculated as call_duration_secs / sum(frame_duration_secs ^ 2).
It penalizes long freezes and could better represent user experience
related to smoothness of playback.
Bug: none
Change-Id: I4d2d46deaa44bb4221b53969a1c0a334e0c1bde9
Reviewed-on: https://webrtc-review.googlesource.com/c/117661
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26279}
diff --git a/video/receive_statistics_proxy_unittest.cc b/video/receive_statistics_proxy_unittest.cc
index 176c89c..29e4483 100644
--- a/video/receive_statistics_proxy_unittest.cc
+++ b/video/receive_statistics_proxy_unittest.cc
@@ -1104,6 +1104,41 @@
}
}
+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 5092cb8..ebe5bb1 100644
--- a/video/video_quality_observer.cc
+++ b/video/video_quality_observer.cc
@@ -11,6 +11,7 @@
#include "video/video_quality_observer.h"
#include <algorithm>
+#include <cmath>
#include <cstdint>
#include <string>
@@ -41,6 +42,7 @@
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 @@
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 @@
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<int> avg_interframe_delay =
render_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze);
diff --git a/video/video_quality_observer.h b/video/video_quality_observer.h
index af69dd4..0c60d9a 100644
--- a/video/video_quality_observer.h
+++ b/video/video_quality_observer.h
@@ -57,7 +57,7 @@
// 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_;