xref: /aosp_15_r20/external/webrtc/video/video_quality_observer2.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "video/video_quality_observer2.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <cmath>
15*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
16*d9f75844SAndroid Build Coastguard Worker #include <string>
17*d9f75844SAndroid Build Coastguard Worker 
18*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
19*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
20*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/metrics.h"
21*d9f75844SAndroid Build Coastguard Worker #include "video/video_receive_stream2.h"
22*d9f75844SAndroid Build Coastguard Worker 
23*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
24*d9f75844SAndroid Build Coastguard Worker namespace internal {
25*d9f75844SAndroid Build Coastguard Worker const uint32_t VideoQualityObserver::kMinFrameSamplesToDetectFreeze = 5;
26*d9f75844SAndroid Build Coastguard Worker const uint32_t VideoQualityObserver::kMinIncreaseForFreezeMs = 150;
27*d9f75844SAndroid Build Coastguard Worker const uint32_t VideoQualityObserver::kAvgInterframeDelaysWindowSizeFrames = 30;
28*d9f75844SAndroid Build Coastguard Worker 
29*d9f75844SAndroid Build Coastguard Worker namespace {
30*d9f75844SAndroid Build Coastguard Worker constexpr int kMinVideoDurationMs = 3000;
31*d9f75844SAndroid Build Coastguard Worker constexpr int kMinRequiredSamples = 1;
32*d9f75844SAndroid Build Coastguard Worker constexpr int kPixelsInHighResolution =
33*d9f75844SAndroid Build Coastguard Worker     960 * 540;  // CPU-adapted HD still counts.
34*d9f75844SAndroid Build Coastguard Worker constexpr int kPixelsInMediumResolution = 640 * 360;
35*d9f75844SAndroid Build Coastguard Worker constexpr int kBlockyQpThresholdVp8 = 70;
36*d9f75844SAndroid Build Coastguard Worker constexpr int kBlockyQpThresholdVp9 = 180;
37*d9f75844SAndroid Build Coastguard Worker constexpr int kMaxNumCachedBlockyFrames = 100;
38*d9f75844SAndroid Build Coastguard Worker // TODO(ilnik): Add H264/HEVC thresholds.
39*d9f75844SAndroid Build Coastguard Worker }  // namespace
40*d9f75844SAndroid Build Coastguard Worker 
VideoQualityObserver()41*d9f75844SAndroid Build Coastguard Worker VideoQualityObserver::VideoQualityObserver()
42*d9f75844SAndroid Build Coastguard Worker     : last_frame_rendered_ms_(-1),
43*d9f75844SAndroid Build Coastguard Worker       num_frames_rendered_(0),
44*d9f75844SAndroid Build Coastguard Worker       first_frame_rendered_ms_(-1),
45*d9f75844SAndroid Build Coastguard Worker       last_frame_pixels_(0),
46*d9f75844SAndroid Build Coastguard Worker       is_last_frame_blocky_(false),
47*d9f75844SAndroid Build Coastguard Worker       last_unfreeze_time_ms_(0),
48*d9f75844SAndroid Build Coastguard Worker       render_interframe_delays_(kAvgInterframeDelaysWindowSizeFrames),
49*d9f75844SAndroid Build Coastguard Worker       sum_squared_interframe_delays_secs_(0.0),
50*d9f75844SAndroid Build Coastguard Worker       time_in_resolution_ms_(3, 0),
51*d9f75844SAndroid Build Coastguard Worker       current_resolution_(Resolution::Low),
52*d9f75844SAndroid Build Coastguard Worker       num_resolution_downgrades_(0),
53*d9f75844SAndroid Build Coastguard Worker       time_in_blocky_video_ms_(0),
54*d9f75844SAndroid Build Coastguard Worker       is_paused_(false) {}
55*d9f75844SAndroid Build Coastguard Worker 
UpdateHistograms(bool screenshare)56*d9f75844SAndroid Build Coastguard Worker void VideoQualityObserver::UpdateHistograms(bool screenshare) {
57*d9f75844SAndroid Build Coastguard Worker   // TODO(bugs.webrtc.org/11489): Called on the decoder thread - which _might_
58*d9f75844SAndroid Build Coastguard Worker   // be the same as the construction thread.
59*d9f75844SAndroid Build Coastguard Worker 
60*d9f75844SAndroid Build Coastguard Worker   // Don't report anything on an empty video stream.
61*d9f75844SAndroid Build Coastguard Worker   if (num_frames_rendered_ == 0) {
62*d9f75844SAndroid Build Coastguard Worker     return;
63*d9f75844SAndroid Build Coastguard Worker   }
64*d9f75844SAndroid Build Coastguard Worker 
65*d9f75844SAndroid Build Coastguard Worker   char log_stream_buf[2 * 1024];
66*d9f75844SAndroid Build Coastguard Worker   rtc::SimpleStringBuilder log_stream(log_stream_buf);
67*d9f75844SAndroid Build Coastguard Worker 
68*d9f75844SAndroid Build Coastguard Worker   if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
69*d9f75844SAndroid Build Coastguard Worker     smooth_playback_durations_.Add(last_frame_rendered_ms_ -
70*d9f75844SAndroid Build Coastguard Worker                                    last_unfreeze_time_ms_);
71*d9f75844SAndroid Build Coastguard Worker   }
72*d9f75844SAndroid Build Coastguard Worker 
73*d9f75844SAndroid Build Coastguard Worker   std::string uma_prefix =
74*d9f75844SAndroid Build Coastguard Worker       screenshare ? "WebRTC.Video.Screenshare" : "WebRTC.Video";
75*d9f75844SAndroid Build Coastguard Worker 
76*d9f75844SAndroid Build Coastguard Worker   auto mean_time_between_freezes =
77*d9f75844SAndroid Build Coastguard Worker       smooth_playback_durations_.Avg(kMinRequiredSamples);
78*d9f75844SAndroid Build Coastguard Worker   if (mean_time_between_freezes) {
79*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix + ".MeanTimeBetweenFreezesMs",
80*d9f75844SAndroid Build Coastguard Worker                                        *mean_time_between_freezes);
81*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".MeanTimeBetweenFreezesMs "
82*d9f75844SAndroid Build Coastguard Worker                << *mean_time_between_freezes << "\n";
83*d9f75844SAndroid Build Coastguard Worker   }
84*d9f75844SAndroid Build Coastguard Worker   auto avg_freeze_length = freezes_durations_.Avg(kMinRequiredSamples);
85*d9f75844SAndroid Build Coastguard Worker   if (avg_freeze_length) {
86*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix + ".MeanFreezeDurationMs",
87*d9f75844SAndroid Build Coastguard Worker                                        *avg_freeze_length);
88*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".MeanFreezeDurationMs " << *avg_freeze_length
89*d9f75844SAndroid Build Coastguard Worker                << "\n";
90*d9f75844SAndroid Build Coastguard Worker   }
91*d9f75844SAndroid Build Coastguard Worker 
92*d9f75844SAndroid Build Coastguard Worker   int64_t video_duration_ms =
93*d9f75844SAndroid Build Coastguard Worker       last_frame_rendered_ms_ - first_frame_rendered_ms_;
94*d9f75844SAndroid Build Coastguard Worker 
95*d9f75844SAndroid Build Coastguard Worker   if (video_duration_ms >= kMinVideoDurationMs) {
96*d9f75844SAndroid Build Coastguard Worker     int time_spent_in_hd_percentage = static_cast<int>(
97*d9f75844SAndroid Build Coastguard Worker         time_in_resolution_ms_[Resolution::High] * 100 / video_duration_ms);
98*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".TimeInHdPercentage",
99*d9f75844SAndroid Build Coastguard Worker                                     time_spent_in_hd_percentage);
100*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".TimeInHdPercentage "
101*d9f75844SAndroid Build Coastguard Worker                << time_spent_in_hd_percentage << "\n";
102*d9f75844SAndroid Build Coastguard Worker 
103*d9f75844SAndroid Build Coastguard Worker     int time_with_blocky_video_percentage =
104*d9f75844SAndroid Build Coastguard Worker         static_cast<int>(time_in_blocky_video_ms_ * 100 / video_duration_ms);
105*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".TimeInBlockyVideoPercentage",
106*d9f75844SAndroid Build Coastguard Worker                                     time_with_blocky_video_percentage);
107*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".TimeInBlockyVideoPercentage "
108*d9f75844SAndroid Build Coastguard Worker                << time_with_blocky_video_percentage << "\n";
109*d9f75844SAndroid Build Coastguard Worker 
110*d9f75844SAndroid Build Coastguard Worker     int num_resolution_downgrades_per_minute =
111*d9f75844SAndroid Build Coastguard Worker         num_resolution_downgrades_ * 60000 / video_duration_ms;
112*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100(
113*d9f75844SAndroid Build Coastguard Worker         uma_prefix + ".NumberResolutionDownswitchesPerMinute",
114*d9f75844SAndroid Build Coastguard Worker         num_resolution_downgrades_per_minute);
115*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".NumberResolutionDownswitchesPerMinute "
116*d9f75844SAndroid Build Coastguard Worker                << num_resolution_downgrades_per_minute << "\n";
117*d9f75844SAndroid Build Coastguard Worker 
118*d9f75844SAndroid Build Coastguard Worker     int num_freezes_per_minute =
119*d9f75844SAndroid Build Coastguard Worker         freezes_durations_.NumSamples() * 60000 / video_duration_ms;
120*d9f75844SAndroid Build Coastguard Worker     RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".NumberFreezesPerMinute",
121*d9f75844SAndroid Build Coastguard Worker                                     num_freezes_per_minute);
122*d9f75844SAndroid Build Coastguard Worker     log_stream << uma_prefix << ".NumberFreezesPerMinute "
123*d9f75844SAndroid Build Coastguard Worker                << num_freezes_per_minute << "\n";
124*d9f75844SAndroid Build Coastguard Worker 
125*d9f75844SAndroid Build Coastguard Worker     if (sum_squared_interframe_delays_secs_ > 0.0) {
126*d9f75844SAndroid Build Coastguard Worker       int harmonic_framerate_fps = std::round(
127*d9f75844SAndroid Build Coastguard Worker           video_duration_ms / (1000 * sum_squared_interframe_delays_secs_));
128*d9f75844SAndroid Build Coastguard Worker       RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".HarmonicFrameRate",
129*d9f75844SAndroid Build Coastguard Worker                                       harmonic_framerate_fps);
130*d9f75844SAndroid Build Coastguard Worker       log_stream << uma_prefix << ".HarmonicFrameRate "
131*d9f75844SAndroid Build Coastguard Worker                  << harmonic_framerate_fps << "\n";
132*d9f75844SAndroid Build Coastguard Worker     }
133*d9f75844SAndroid Build Coastguard Worker   }
134*d9f75844SAndroid Build Coastguard Worker   RTC_LOG(LS_INFO) << log_stream.str();
135*d9f75844SAndroid Build Coastguard Worker }
136*d9f75844SAndroid Build Coastguard Worker 
OnRenderedFrame(const VideoFrameMetaData & frame_meta)137*d9f75844SAndroid Build Coastguard Worker void VideoQualityObserver::OnRenderedFrame(
138*d9f75844SAndroid Build Coastguard Worker     const VideoFrameMetaData& frame_meta) {
139*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_LE(last_frame_rendered_ms_, frame_meta.decode_timestamp.ms());
140*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_LE(last_unfreeze_time_ms_, frame_meta.decode_timestamp.ms());
141*d9f75844SAndroid Build Coastguard Worker 
142*d9f75844SAndroid Build Coastguard Worker   if (num_frames_rendered_ == 0) {
143*d9f75844SAndroid Build Coastguard Worker     first_frame_rendered_ms_ = last_unfreeze_time_ms_ =
144*d9f75844SAndroid Build Coastguard Worker         frame_meta.decode_timestamp.ms();
145*d9f75844SAndroid Build Coastguard Worker   }
146*d9f75844SAndroid Build Coastguard Worker 
147*d9f75844SAndroid Build Coastguard Worker   auto blocky_frame_it = blocky_frames_.find(frame_meta.rtp_timestamp);
148*d9f75844SAndroid Build Coastguard Worker 
149*d9f75844SAndroid Build Coastguard Worker   if (num_frames_rendered_ > 0) {
150*d9f75844SAndroid Build Coastguard Worker     // Process inter-frame delay.
151*d9f75844SAndroid Build Coastguard Worker     const int64_t interframe_delay_ms =
152*d9f75844SAndroid Build Coastguard Worker         frame_meta.decode_timestamp.ms() - last_frame_rendered_ms_;
153*d9f75844SAndroid Build Coastguard Worker     const double interframe_delays_secs = interframe_delay_ms / 1000.0;
154*d9f75844SAndroid Build Coastguard Worker 
155*d9f75844SAndroid Build Coastguard Worker     // Sum of squared inter frame intervals is used to calculate the harmonic
156*d9f75844SAndroid Build Coastguard Worker     // frame rate metric. The metric aims to reflect overall experience related
157*d9f75844SAndroid Build Coastguard Worker     // to smoothness of video playback and includes both freezes and pauses.
158*d9f75844SAndroid Build Coastguard Worker     sum_squared_interframe_delays_secs_ +=
159*d9f75844SAndroid Build Coastguard Worker         interframe_delays_secs * interframe_delays_secs;
160*d9f75844SAndroid Build Coastguard Worker 
161*d9f75844SAndroid Build Coastguard Worker     if (!is_paused_) {
162*d9f75844SAndroid Build Coastguard Worker       render_interframe_delays_.AddSample(interframe_delay_ms);
163*d9f75844SAndroid Build Coastguard Worker 
164*d9f75844SAndroid Build Coastguard Worker       bool was_freeze = false;
165*d9f75844SAndroid Build Coastguard Worker       if (render_interframe_delays_.Size() >= kMinFrameSamplesToDetectFreeze) {
166*d9f75844SAndroid Build Coastguard Worker         const absl::optional<int64_t> avg_interframe_delay =
167*d9f75844SAndroid Build Coastguard Worker             render_interframe_delays_.GetAverageRoundedDown();
168*d9f75844SAndroid Build Coastguard Worker         RTC_DCHECK(avg_interframe_delay);
169*d9f75844SAndroid Build Coastguard Worker         was_freeze = interframe_delay_ms >=
170*d9f75844SAndroid Build Coastguard Worker                      std::max(3 * *avg_interframe_delay,
171*d9f75844SAndroid Build Coastguard Worker                               *avg_interframe_delay + kMinIncreaseForFreezeMs);
172*d9f75844SAndroid Build Coastguard Worker       }
173*d9f75844SAndroid Build Coastguard Worker 
174*d9f75844SAndroid Build Coastguard Worker       if (was_freeze) {
175*d9f75844SAndroid Build Coastguard Worker         freezes_durations_.Add(interframe_delay_ms);
176*d9f75844SAndroid Build Coastguard Worker         smooth_playback_durations_.Add(last_frame_rendered_ms_ -
177*d9f75844SAndroid Build Coastguard Worker                                        last_unfreeze_time_ms_);
178*d9f75844SAndroid Build Coastguard Worker         last_unfreeze_time_ms_ = frame_meta.decode_timestamp.ms();
179*d9f75844SAndroid Build Coastguard Worker       } else {
180*d9f75844SAndroid Build Coastguard Worker         // Count spatial metrics if there were no freeze.
181*d9f75844SAndroid Build Coastguard Worker         time_in_resolution_ms_[current_resolution_] += interframe_delay_ms;
182*d9f75844SAndroid Build Coastguard Worker 
183*d9f75844SAndroid Build Coastguard Worker         if (is_last_frame_blocky_) {
184*d9f75844SAndroid Build Coastguard Worker           time_in_blocky_video_ms_ += interframe_delay_ms;
185*d9f75844SAndroid Build Coastguard Worker         }
186*d9f75844SAndroid Build Coastguard Worker       }
187*d9f75844SAndroid Build Coastguard Worker     }
188*d9f75844SAndroid Build Coastguard Worker   }
189*d9f75844SAndroid Build Coastguard Worker 
190*d9f75844SAndroid Build Coastguard Worker   if (is_paused_) {
191*d9f75844SAndroid Build Coastguard Worker     // If the stream was paused since the previous frame, do not count the
192*d9f75844SAndroid Build Coastguard Worker     // pause toward smooth playback. Explicitly count the part before it and
193*d9f75844SAndroid Build Coastguard Worker     // start the new smooth playback interval from this frame.
194*d9f75844SAndroid Build Coastguard Worker     is_paused_ = false;
195*d9f75844SAndroid Build Coastguard Worker     if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
196*d9f75844SAndroid Build Coastguard Worker       smooth_playback_durations_.Add(last_frame_rendered_ms_ -
197*d9f75844SAndroid Build Coastguard Worker                                      last_unfreeze_time_ms_);
198*d9f75844SAndroid Build Coastguard Worker     }
199*d9f75844SAndroid Build Coastguard Worker     last_unfreeze_time_ms_ = frame_meta.decode_timestamp.ms();
200*d9f75844SAndroid Build Coastguard Worker 
201*d9f75844SAndroid Build Coastguard Worker     if (num_frames_rendered_ > 0) {
202*d9f75844SAndroid Build Coastguard Worker       pauses_durations_.Add(frame_meta.decode_timestamp.ms() -
203*d9f75844SAndroid Build Coastguard Worker                             last_frame_rendered_ms_);
204*d9f75844SAndroid Build Coastguard Worker     }
205*d9f75844SAndroid Build Coastguard Worker   }
206*d9f75844SAndroid Build Coastguard Worker 
207*d9f75844SAndroid Build Coastguard Worker   int64_t pixels = frame_meta.width * frame_meta.height;
208*d9f75844SAndroid Build Coastguard Worker   if (pixels >= kPixelsInHighResolution) {
209*d9f75844SAndroid Build Coastguard Worker     current_resolution_ = Resolution::High;
210*d9f75844SAndroid Build Coastguard Worker   } else if (pixels >= kPixelsInMediumResolution) {
211*d9f75844SAndroid Build Coastguard Worker     current_resolution_ = Resolution::Medium;
212*d9f75844SAndroid Build Coastguard Worker   } else {
213*d9f75844SAndroid Build Coastguard Worker     current_resolution_ = Resolution::Low;
214*d9f75844SAndroid Build Coastguard Worker   }
215*d9f75844SAndroid Build Coastguard Worker 
216*d9f75844SAndroid Build Coastguard Worker   if (pixels < last_frame_pixels_) {
217*d9f75844SAndroid Build Coastguard Worker     ++num_resolution_downgrades_;
218*d9f75844SAndroid Build Coastguard Worker   }
219*d9f75844SAndroid Build Coastguard Worker 
220*d9f75844SAndroid Build Coastguard Worker   last_frame_pixels_ = pixels;
221*d9f75844SAndroid Build Coastguard Worker   last_frame_rendered_ms_ = frame_meta.decode_timestamp.ms();
222*d9f75844SAndroid Build Coastguard Worker 
223*d9f75844SAndroid Build Coastguard Worker   is_last_frame_blocky_ = blocky_frame_it != blocky_frames_.end();
224*d9f75844SAndroid Build Coastguard Worker   if (is_last_frame_blocky_) {
225*d9f75844SAndroid Build Coastguard Worker     blocky_frames_.erase(blocky_frames_.begin(), ++blocky_frame_it);
226*d9f75844SAndroid Build Coastguard Worker   }
227*d9f75844SAndroid Build Coastguard Worker 
228*d9f75844SAndroid Build Coastguard Worker   ++num_frames_rendered_;
229*d9f75844SAndroid Build Coastguard Worker }
230*d9f75844SAndroid Build Coastguard Worker 
OnDecodedFrame(uint32_t rtp_frame_timestamp,absl::optional<uint8_t> qp,VideoCodecType codec)231*d9f75844SAndroid Build Coastguard Worker void VideoQualityObserver::OnDecodedFrame(uint32_t rtp_frame_timestamp,
232*d9f75844SAndroid Build Coastguard Worker                                           absl::optional<uint8_t> qp,
233*d9f75844SAndroid Build Coastguard Worker                                           VideoCodecType codec) {
234*d9f75844SAndroid Build Coastguard Worker   if (!qp)
235*d9f75844SAndroid Build Coastguard Worker     return;
236*d9f75844SAndroid Build Coastguard Worker 
237*d9f75844SAndroid Build Coastguard Worker   absl::optional<int> qp_blocky_threshold;
238*d9f75844SAndroid Build Coastguard Worker   // TODO(ilnik): add other codec types when we have QP for them.
239*d9f75844SAndroid Build Coastguard Worker   switch (codec) {
240*d9f75844SAndroid Build Coastguard Worker     case kVideoCodecVP8:
241*d9f75844SAndroid Build Coastguard Worker       qp_blocky_threshold = kBlockyQpThresholdVp8;
242*d9f75844SAndroid Build Coastguard Worker       break;
243*d9f75844SAndroid Build Coastguard Worker     case kVideoCodecVP9:
244*d9f75844SAndroid Build Coastguard Worker       qp_blocky_threshold = kBlockyQpThresholdVp9;
245*d9f75844SAndroid Build Coastguard Worker       break;
246*d9f75844SAndroid Build Coastguard Worker     default:
247*d9f75844SAndroid Build Coastguard Worker       qp_blocky_threshold = absl::nullopt;
248*d9f75844SAndroid Build Coastguard Worker   }
249*d9f75844SAndroid Build Coastguard Worker 
250*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(blocky_frames_.find(rtp_frame_timestamp) == blocky_frames_.end());
251*d9f75844SAndroid Build Coastguard Worker 
252*d9f75844SAndroid Build Coastguard Worker   if (qp_blocky_threshold && *qp > *qp_blocky_threshold) {
253*d9f75844SAndroid Build Coastguard Worker     // Cache blocky frame. Its duration will be calculated in render callback.
254*d9f75844SAndroid Build Coastguard Worker     if (blocky_frames_.size() > kMaxNumCachedBlockyFrames) {
255*d9f75844SAndroid Build Coastguard Worker       RTC_LOG(LS_WARNING) << "Overflow of blocky frames cache.";
256*d9f75844SAndroid Build Coastguard Worker       blocky_frames_.erase(
257*d9f75844SAndroid Build Coastguard Worker           blocky_frames_.begin(),
258*d9f75844SAndroid Build Coastguard Worker           std::next(blocky_frames_.begin(), kMaxNumCachedBlockyFrames / 2));
259*d9f75844SAndroid Build Coastguard Worker     }
260*d9f75844SAndroid Build Coastguard Worker 
261*d9f75844SAndroid Build Coastguard Worker     blocky_frames_.insert(rtp_frame_timestamp);
262*d9f75844SAndroid Build Coastguard Worker   }
263*d9f75844SAndroid Build Coastguard Worker }
264*d9f75844SAndroid Build Coastguard Worker 
OnStreamInactive()265*d9f75844SAndroid Build Coastguard Worker void VideoQualityObserver::OnStreamInactive() {
266*d9f75844SAndroid Build Coastguard Worker   is_paused_ = true;
267*d9f75844SAndroid Build Coastguard Worker }
268*d9f75844SAndroid Build Coastguard Worker 
NumFreezes() const269*d9f75844SAndroid Build Coastguard Worker uint32_t VideoQualityObserver::NumFreezes() const {
270*d9f75844SAndroid Build Coastguard Worker   return freezes_durations_.NumSamples();
271*d9f75844SAndroid Build Coastguard Worker }
272*d9f75844SAndroid Build Coastguard Worker 
NumPauses() const273*d9f75844SAndroid Build Coastguard Worker uint32_t VideoQualityObserver::NumPauses() const {
274*d9f75844SAndroid Build Coastguard Worker   return pauses_durations_.NumSamples();
275*d9f75844SAndroid Build Coastguard Worker }
276*d9f75844SAndroid Build Coastguard Worker 
TotalFreezesDurationMs() const277*d9f75844SAndroid Build Coastguard Worker uint32_t VideoQualityObserver::TotalFreezesDurationMs() const {
278*d9f75844SAndroid Build Coastguard Worker   return freezes_durations_.Sum(kMinRequiredSamples).value_or(0);
279*d9f75844SAndroid Build Coastguard Worker }
280*d9f75844SAndroid Build Coastguard Worker 
TotalPausesDurationMs() const281*d9f75844SAndroid Build Coastguard Worker uint32_t VideoQualityObserver::TotalPausesDurationMs() const {
282*d9f75844SAndroid Build Coastguard Worker   return pauses_durations_.Sum(kMinRequiredSamples).value_or(0);
283*d9f75844SAndroid Build Coastguard Worker }
284*d9f75844SAndroid Build Coastguard Worker 
TotalFramesDurationMs() const285*d9f75844SAndroid Build Coastguard Worker uint32_t VideoQualityObserver::TotalFramesDurationMs() const {
286*d9f75844SAndroid Build Coastguard Worker   return last_frame_rendered_ms_ - first_frame_rendered_ms_;
287*d9f75844SAndroid Build Coastguard Worker }
288*d9f75844SAndroid Build Coastguard Worker 
SumSquaredFrameDurationsSec() const289*d9f75844SAndroid Build Coastguard Worker double VideoQualityObserver::SumSquaredFrameDurationsSec() const {
290*d9f75844SAndroid Build Coastguard Worker   return sum_squared_interframe_delays_secs_;
291*d9f75844SAndroid Build Coastguard Worker }
292*d9f75844SAndroid Build Coastguard Worker 
293*d9f75844SAndroid Build Coastguard Worker }  // namespace internal
294*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
295