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}
This commit is contained in:
Sergey Silkin 2019-01-16 13:41:46 +01:00 committed by Commit Bot
parent dcc70297cd
commit 50e77457db
3 changed files with 51 additions and 2 deletions

View File

@ -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;

View File

@ -11,6 +11,7 @@
#include "video/video_quality_observer.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <string>
@ -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<int> avg_interframe_delay =
render_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze);

View File

@ -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_;