Compute video freeze metrics on rendered frames instead of on decoded

Bug: webrtc:9828
Change-Id: I1390c736785759a2d8712e71398db4f5069ebcd4
Reviewed-on: https://webrtc-review.googlesource.com/c/105100
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25116}
This commit is contained in:
Ilya Nikolaevskiy
2018-10-10 13:15:09 +02:00
committed by Commit Bot
parent 3bdbc84888
commit cdc959fb42
4 changed files with 63 additions and 26 deletions

View File

@ -759,6 +759,9 @@ void ReceiveStatisticsProxy::OnRenderedFrame(const VideoFrame& frame) {
RTC_DCHECK_GT(height, 0); RTC_DCHECK_GT(height, 0);
int64_t now_ms = clock_->TimeInMilliseconds(); int64_t now_ms = clock_->TimeInMilliseconds();
rtc::CritScope lock(&crit_); rtc::CritScope lock(&crit_);
video_quality_observer_->OnRenderedFrame(now_ms);
ContentSpecificStats* content_specific_stats = ContentSpecificStats* content_specific_stats =
&content_specific_stats_[last_content_type_]; &content_specific_stats_[last_content_type_];
renders_fps_estimator_.Update(1, now_ms); renders_fps_estimator_.Update(1, now_ms);

View File

@ -1057,15 +1057,19 @@ TEST_P(ReceiveStatisticsProxyTest, FreezesAreReported) {
const int kFreezeDelayMs = 200; const int kFreezeDelayMs = 200;
const int kCallDurationMs = const int kCallDurationMs =
kMinRequiredSamples * kInterFrameDelayMs + kFreezeDelayMs; kMinRequiredSamples * kInterFrameDelayMs + kFreezeDelayMs;
webrtc::VideoFrame frame(webrtc::I420Buffer::Create(1, 1), 0, 0,
webrtc::kVideoRotation_0);
for (int i = 0; i < kMinRequiredSamples; ++i) { for (int i = 0; i < kMinRequiredSamples; ++i) {
statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight, statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight,
content_type); content_type);
statistics_proxy_->OnRenderedFrame(frame);
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs); fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
} }
// Add extra freeze. // Add extra freeze.
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs); fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight, statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight,
content_type); content_type);
statistics_proxy_->OnRenderedFrame(frame);
statistics_proxy_.reset(); statistics_proxy_.reset();
const int kExpectedTimeBetweenFreezes = const int kExpectedTimeBetweenFreezes =
@ -1095,9 +1099,12 @@ TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
const VideoContentType content_type = GetParam(); const VideoContentType content_type = GetParam();
const int kInterFrameDelayMs = 33; const int kInterFrameDelayMs = 33;
const int kPauseDurationMs = 10000; const int kPauseDurationMs = 10000;
webrtc::VideoFrame frame(webrtc::I420Buffer::Create(1, 1), 0, 0,
webrtc::kVideoRotation_0);
for (int i = 0; i <= kMinRequiredSamples; ++i) { for (int i = 0; i <= kMinRequiredSamples; ++i) {
statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight, statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight,
content_type); content_type);
statistics_proxy_->OnRenderedFrame(frame);
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs); fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
} }
// Add a pause. // Add a pause.
@ -1108,6 +1115,7 @@ TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
for (int i = 0; i <= kMinRequiredSamples * 3; ++i) { for (int i = 0; i <= kMinRequiredSamples * 3; ++i) {
statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight, statistics_proxy_->OnDecodedFrame(absl::nullopt, kWidth, kHeight,
content_type); content_type);
statistics_proxy_->OnRenderedFrame(frame);
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs); fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
} }

View File

@ -34,7 +34,9 @@ const int kBlockyQpThresholdVp9 = 60; // TODO(ilnik): tune this value.
VideoQualityObserver::VideoQualityObserver(VideoContentType content_type) VideoQualityObserver::VideoQualityObserver(VideoContentType content_type)
: last_frame_decoded_ms_(-1), : last_frame_decoded_ms_(-1),
last_frame_rendered_ms_(-1),
num_frames_decoded_(0), num_frames_decoded_(0),
num_frames_rendered_(0),
first_frame_decoded_ms_(-1), first_frame_decoded_ms_(-1),
last_frame_pixels_(0), last_frame_pixels_(0),
last_frame_qp_(0), last_frame_qp_(0),
@ -119,6 +121,46 @@ void VideoQualityObserver::UpdateHistograms() {
RTC_LOG(LS_INFO) << log_stream.str(); RTC_LOG(LS_INFO) << log_stream.str();
} }
void VideoQualityObserver::OnRenderedFrame(int64_t now_ms) {
if (num_frames_rendered_ == 0) {
last_unfreeze_time_ = now_ms;
}
++num_frames_rendered_;
if (!is_paused_ && num_frames_rendered_ > 1) {
// Process inter-frame delay.
int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_;
render_interframe_delays_.Add(interframe_delay_ms);
absl::optional<int> avg_interframe_delay =
render_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze);
// Check if it was a freeze.
if (avg_interframe_delay &&
interframe_delay_ms >=
std::max(3 * *avg_interframe_delay,
*avg_interframe_delay + kMinIncreaseForFreezeMs)) {
freezes_durations_.Add(interframe_delay_ms);
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
last_unfreeze_time_);
last_unfreeze_time_ = now_ms;
}
}
if (is_paused_) {
// If the stream was paused since the previous frame, do not count the
// pause toward smooth playback. Explicitly count the part before it and
// start the new smooth playback interval from this frame.
is_paused_ = false;
if (last_frame_rendered_ms_ > last_unfreeze_time_) {
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
last_unfreeze_time_);
}
last_unfreeze_time_ = now_ms;
}
last_frame_rendered_ms_ = now_ms;
}
void VideoQualityObserver::OnDecodedFrame(absl::optional<uint8_t> qp, void VideoQualityObserver::OnDecodedFrame(absl::optional<uint8_t> qp,
int width, int width,
int height, int height,
@ -126,7 +168,6 @@ void VideoQualityObserver::OnDecodedFrame(absl::optional<uint8_t> qp,
VideoCodecType codec) { VideoCodecType codec) {
if (num_frames_decoded_ == 0) { if (num_frames_decoded_ == 0) {
first_frame_decoded_ms_ = now_ms; first_frame_decoded_ms_ = now_ms;
last_unfreeze_time_ = now_ms;
} }
++num_frames_decoded_; ++num_frames_decoded_;
@ -134,21 +175,14 @@ void VideoQualityObserver::OnDecodedFrame(absl::optional<uint8_t> qp,
if (!is_paused_ && num_frames_decoded_ > 1) { if (!is_paused_ && num_frames_decoded_ > 1) {
// Process inter-frame delay. // Process inter-frame delay.
int64_t interframe_delay_ms = now_ms - last_frame_decoded_ms_; int64_t interframe_delay_ms = now_ms - last_frame_decoded_ms_;
interframe_delays_.Add(interframe_delay_ms); decode_interframe_delays_.Add(interframe_delay_ms);
absl::optional<int> avg_interframe_delay = absl::optional<int> avg_interframe_delay =
interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze); decode_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze);
// Check if it was a freeze. // Count spatial metrics if there were no freeze.
if (avg_interframe_delay && if (!avg_interframe_delay ||
interframe_delay_ms >= interframe_delay_ms <
std::max(3 * *avg_interframe_delay, std::max(3 * *avg_interframe_delay,
*avg_interframe_delay + kMinIncreaseForFreezeMs)) { *avg_interframe_delay + kMinIncreaseForFreezeMs)) {
freezes_durations_.Add(interframe_delay_ms);
smooth_playback_durations_.Add(last_frame_decoded_ms_ -
last_unfreeze_time_);
last_unfreeze_time_ = now_ms;
} else {
// Only count inter-frame delay as playback time if there
// was no freeze.
time_in_resolution_ms_[current_resolution_] += interframe_delay_ms; time_in_resolution_ms_[current_resolution_] += interframe_delay_ms;
absl::optional<int> qp_blocky_threshold; absl::optional<int> qp_blocky_threshold;
// TODO(ilnik): add other codec types when we have QP for them. // TODO(ilnik): add other codec types when we have QP for them.
@ -168,18 +202,6 @@ void VideoQualityObserver::OnDecodedFrame(absl::optional<uint8_t> qp,
} }
} }
if (is_paused_) {
// If the stream was paused since the previous frame, do not count the
// pause toward smooth playback. Explicitly count the part before it and
// start the new smooth playback interval from this frame.
is_paused_ = false;
if (last_frame_decoded_ms_ > last_unfreeze_time_) {
smooth_playback_durations_.Add(last_frame_decoded_ms_ -
last_unfreeze_time_);
}
last_unfreeze_time_ = now_ms;
}
int64_t pixels = width * height; int64_t pixels = width * height;
if (pixels >= kPixelsInHighResolution) { if (pixels >= kPixelsInHighResolution) {
current_resolution_ = Resolution::High; current_resolution_ = Resolution::High;

View File

@ -35,6 +35,7 @@ class VideoQualityObserver {
int height, int height,
int64_t now_ms, int64_t now_ms,
VideoCodecType codec); VideoCodecType codec);
void OnRenderedFrame(int64_t now_ms);
void OnStreamInactive(); void OnStreamInactive();
@ -48,13 +49,16 @@ class VideoQualityObserver {
}; };
int64_t last_frame_decoded_ms_; int64_t last_frame_decoded_ms_;
int64_t last_frame_rendered_ms_;
int64_t num_frames_decoded_; int64_t num_frames_decoded_;
int64_t num_frames_rendered_;
int64_t first_frame_decoded_ms_; int64_t first_frame_decoded_ms_;
int64_t last_frame_pixels_; int64_t last_frame_pixels_;
uint8_t last_frame_qp_; uint8_t last_frame_qp_;
// Decoded timestamp of the last delayed frame. // Decoded timestamp of the last delayed frame.
int64_t last_unfreeze_time_; int64_t last_unfreeze_time_;
rtc::SampleCounter interframe_delays_; rtc::SampleCounter render_interframe_delays_;
rtc::SampleCounter decode_interframe_delays_;
// An inter-frame delay is counted as a freeze if it's significantly longer // An inter-frame delay is counted as a freeze if it's significantly longer
// than average inter-frame delay. // than average inter-frame delay.
rtc::SampleCounter freezes_durations_; rtc::SampleCounter freezes_durations_;