OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/video/receive_statistics_proxy.h" | 11 #include "webrtc/video/receive_statistics_proxy.h" |
12 | 12 |
13 #include <cmath> | 13 #include <cmath> |
14 | 14 |
15 #include "webrtc/base/checks.h" | 15 #include "webrtc/base/checks.h" |
| 16 #include "webrtc/base/logging.h" |
16 #include "webrtc/modules/video_coding/include/video_codec_interface.h" | 17 #include "webrtc/modules/video_coding/include/video_codec_interface.h" |
17 #include "webrtc/system_wrappers/include/clock.h" | 18 #include "webrtc/system_wrappers/include/clock.h" |
18 #include "webrtc/system_wrappers/include/field_trial.h" | 19 #include "webrtc/system_wrappers/include/field_trial.h" |
19 #include "webrtc/system_wrappers/include/metrics.h" | 20 #include "webrtc/system_wrappers/include/metrics.h" |
20 | 21 |
21 namespace webrtc { | 22 namespace webrtc { |
22 namespace { | 23 namespace { |
23 // Periodic time interval for processing samples for |freq_offset_counter_|. | 24 // Periodic time interval for processing samples for |freq_offset_counter_|. |
24 const int64_t kFreqOffsetProcessIntervalMs = 40000; | 25 const int64_t kFreqOffsetProcessIntervalMs = 40000; |
| 26 |
| 27 // Configuration for bad call detection. |
| 28 const int kMinSampleLengthMs = 990; |
| 29 const int kNumMeasurements = 10; |
| 30 const int kNumMeasurementsVariance = kNumMeasurements * 1.5; |
| 31 const float kBadFraction = 0.8f; |
| 32 // For fps: |
| 33 // Low means low enough to be bad, high means high enough to be good |
| 34 const int kLowFpsThreshold = 12; |
| 35 const int kHighFpsThreshold = 14; |
| 36 // For qp and fps variance: |
| 37 // Low means low enough to be good, high means high enough to be bad |
| 38 const int kLowQpThresholdVp8 = 60; |
| 39 const int kHighQpThresholdVp8 = 70; |
| 40 const int kLowVarianceThreshold = 1; |
| 41 const int kHighVarianceThreshold = 2; |
25 } // namespace | 42 } // namespace |
26 | 43 |
27 ReceiveStatisticsProxy::ReceiveStatisticsProxy( | 44 ReceiveStatisticsProxy::ReceiveStatisticsProxy( |
28 const VideoReceiveStream::Config* config, | 45 const VideoReceiveStream::Config* config, |
29 Clock* clock) | 46 Clock* clock) |
30 : clock_(clock), | 47 : clock_(clock), |
31 config_(*config), | 48 config_(*config), |
32 start_ms_(clock->TimeInMilliseconds()), | 49 start_ms_(clock->TimeInMilliseconds()), |
| 50 last_sample_time_(clock->TimeInMilliseconds()), |
| 51 fps_threshold_(kLowFpsThreshold, |
| 52 kHighFpsThreshold, |
| 53 kBadFraction, |
| 54 kNumMeasurements), |
| 55 qp_threshold_(kLowQpThresholdVp8, |
| 56 kHighQpThresholdVp8, |
| 57 kBadFraction, |
| 58 kNumMeasurements), |
| 59 variance_threshold_(kLowVarianceThreshold, |
| 60 kHighVarianceThreshold, |
| 61 kBadFraction, |
| 62 kNumMeasurementsVariance), |
33 // 1000ms window, scale 1000 for ms to s. | 63 // 1000ms window, scale 1000 for ms to s. |
34 decode_fps_estimator_(1000, 1000), | 64 decode_fps_estimator_(1000, 1000), |
35 renders_fps_estimator_(1000, 1000), | 65 renders_fps_estimator_(1000, 1000), |
36 render_fps_tracker_(100, 10u), | 66 render_fps_tracker_(100, 10u), |
37 render_pixel_tracker_(100, 10u), | 67 render_pixel_tracker_(100, 10u), |
38 freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs), | 68 freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs), |
39 first_report_block_time_ms_(-1) { | 69 first_report_block_time_ms_(-1) { |
40 stats_.ssrc = config_.rtp.remote_ssrc; | 70 stats_.ssrc = config_.rtp.remote_ssrc; |
41 for (auto it : config_.rtp.rtx) | 71 for (auto it : config_.rtp.rtx) |
42 rtx_stats_[it.second.ssrc] = StreamDataCounters(); | 72 rtx_stats_[it.second.ssrc] = StreamDataCounters(); |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 counters.fir_packets * 60 / elapsed_sec); | 196 counters.fir_packets * 60 / elapsed_sec); |
167 RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute", | 197 RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute", |
168 counters.pli_packets * 60 / elapsed_sec); | 198 counters.pli_packets * 60 / elapsed_sec); |
169 if (counters.nack_requests > 0) { | 199 if (counters.nack_requests > 0) { |
170 RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent", | 200 RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent", |
171 counters.UniqueNackRequestsInPercent()); | 201 counters.UniqueNackRequestsInPercent()); |
172 } | 202 } |
173 } | 203 } |
174 } | 204 } |
175 | 205 |
| 206 void ReceiveStatisticsProxy::QualitySample() { |
| 207 int64_t now = clock_->TimeInMilliseconds(); |
| 208 if (last_sample_time_ + kMinSampleLengthMs > now) |
| 209 return; |
| 210 |
| 211 double fps = |
| 212 render_fps_tracker_.ComputeRateForInterval(now - last_sample_time_); |
| 213 int qp = qp_sample_.Avg(1); |
| 214 |
| 215 bool prev_fps_bad = !fps_threshold_.IsHigh().value_or(true); |
| 216 bool prev_qp_bad = qp_threshold_.IsHigh().value_or(false); |
| 217 bool prev_variance_bad = variance_threshold_.IsHigh().value_or(false); |
| 218 bool prev_any_bad = prev_fps_bad || prev_qp_bad || prev_variance_bad; |
| 219 |
| 220 fps_threshold_.AddMeasurement(static_cast<int>(fps)); |
| 221 if (qp != -1) |
| 222 qp_threshold_.AddMeasurement(qp); |
| 223 rtc::Optional<double> fps_variance_opt = fps_threshold_.CalculateVariance(); |
| 224 double fps_variance = fps_variance_opt.value_or(0); |
| 225 if (fps_variance_opt) { |
| 226 variance_threshold_.AddMeasurement(static_cast<int>(fps_variance)); |
| 227 } |
| 228 |
| 229 bool fps_bad = !fps_threshold_.IsHigh().value_or(true); |
| 230 bool qp_bad = qp_threshold_.IsHigh().value_or(false); |
| 231 bool variance_bad = variance_threshold_.IsHigh().value_or(false); |
| 232 bool any_bad = fps_bad || qp_bad || variance_bad; |
| 233 |
| 234 if (!prev_any_bad && any_bad) { |
| 235 LOG(LS_WARNING) << "Bad call (any) start: " << now; |
| 236 } else if (prev_any_bad && !any_bad) { |
| 237 LOG(LS_WARNING) << "Bad call (any) end: " << now; |
| 238 } |
| 239 |
| 240 if (!prev_fps_bad && fps_bad) { |
| 241 LOG(LS_WARNING) << "Bad call (fps) start: " << now; |
| 242 } else if (prev_fps_bad && !fps_bad) { |
| 243 LOG(LS_WARNING) << "Bad call (fps) end: " << now; |
| 244 } |
| 245 |
| 246 if (!prev_qp_bad && qp_bad) { |
| 247 LOG(LS_WARNING) << "Bad call (qp) start: " << now; |
| 248 } else if (prev_qp_bad && !qp_bad) { |
| 249 LOG(LS_WARNING) << "Bad call (qp) end: " << now; |
| 250 } |
| 251 |
| 252 if (!prev_variance_bad && variance_bad) { |
| 253 LOG(LS_WARNING) << "Bad call (variance) start: " << now; |
| 254 } else if (prev_variance_bad && !variance_bad) { |
| 255 LOG(LS_WARNING) << "Bad call (variance) end: " << now; |
| 256 } |
| 257 |
| 258 LOG(LS_INFO) << "SAMPLE: sample_length: " << (now - last_sample_time_) |
| 259 << " fps: " << fps << " fps_bad: " << fps_bad << " qp: " << qp |
| 260 << " qp_bad: " << qp_bad << " variance_bad: " << variance_bad |
| 261 << " fps_variance: " << fps_variance; |
| 262 |
| 263 last_sample_time_ = now; |
| 264 qp_sample_.Reset(); |
| 265 } |
| 266 |
176 VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const { | 267 VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const { |
177 rtc::CritScope lock(&crit_); | 268 rtc::CritScope lock(&crit_); |
178 return stats_; | 269 return stats_; |
179 } | 270 } |
180 | 271 |
181 void ReceiveStatisticsProxy::OnIncomingPayloadType(int payload_type) { | 272 void ReceiveStatisticsProxy::OnIncomingPayloadType(int payload_type) { |
182 rtc::CritScope lock(&crit_); | 273 rtc::CritScope lock(&crit_); |
183 stats_.current_payload_type = payload_type; | 274 stats_.current_payload_type = payload_type; |
184 } | 275 } |
185 | 276 |
186 void ReceiveStatisticsProxy::OnDecoderImplementationName( | 277 void ReceiveStatisticsProxy::OnDecoderImplementationName( |
187 const char* implementation_name) { | 278 const char* implementation_name) { |
188 rtc::CritScope lock(&crit_); | 279 rtc::CritScope lock(&crit_); |
189 stats_.decoder_implementation_name = implementation_name; | 280 stats_.decoder_implementation_name = implementation_name; |
190 } | 281 } |
191 void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate, | 282 void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate, |
192 unsigned int bitrate_bps) { | 283 unsigned int bitrate_bps) { |
193 rtc::CritScope lock(&crit_); | 284 rtc::CritScope lock(&crit_); |
| 285 QualitySample(); |
194 stats_.network_frame_rate = framerate; | 286 stats_.network_frame_rate = framerate; |
195 stats_.total_bitrate_bps = bitrate_bps; | 287 stats_.total_bitrate_bps = bitrate_bps; |
196 } | 288 } |
197 | 289 |
198 void ReceiveStatisticsProxy::OnDecoderTiming(int decode_ms, | 290 void ReceiveStatisticsProxy::OnDecoderTiming(int decode_ms, |
199 int max_decode_ms, | 291 int max_decode_ms, |
200 int current_delay_ms, | 292 int current_delay_ms, |
201 int target_delay_ms, | 293 int target_delay_ms, |
202 int jitter_buffer_ms, | 294 int jitter_buffer_ms, |
203 int min_playout_delay_ms, | 295 int min_playout_delay_ms, |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 } | 425 } |
334 | 426 |
335 void ReceiveStatisticsProxy::OnPreDecode( | 427 void ReceiveStatisticsProxy::OnPreDecode( |
336 const EncodedImage& encoded_image, | 428 const EncodedImage& encoded_image, |
337 const CodecSpecificInfo* codec_specific_info) { | 429 const CodecSpecificInfo* codec_specific_info) { |
338 if (!codec_specific_info || encoded_image.qp_ == -1) { | 430 if (!codec_specific_info || encoded_image.qp_ == -1) { |
339 return; | 431 return; |
340 } | 432 } |
341 if (codec_specific_info->codecType == kVideoCodecVP8) { | 433 if (codec_specific_info->codecType == kVideoCodecVP8) { |
342 qp_counters_.vp8.Add(encoded_image.qp_); | 434 qp_counters_.vp8.Add(encoded_image.qp_); |
| 435 rtc::CritScope lock(&crit_); |
| 436 qp_sample_.Add(encoded_image.qp_); |
343 } | 437 } |
344 } | 438 } |
345 | 439 |
346 void ReceiveStatisticsProxy::SampleCounter::Add(int sample) { | 440 void ReceiveStatisticsProxy::SampleCounter::Add(int sample) { |
347 sum += sample; | 441 sum += sample; |
348 ++num_samples; | 442 ++num_samples; |
349 } | 443 } |
350 | 444 |
351 int ReceiveStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { | 445 int ReceiveStatisticsProxy::SampleCounter::Avg(int min_required_samples) const { |
352 if (num_samples < min_required_samples || num_samples == 0) | 446 if (num_samples < min_required_samples || num_samples == 0) |
353 return -1; | 447 return -1; |
354 return sum / num_samples; | 448 return sum / num_samples; |
355 } | 449 } |
356 | 450 |
| 451 void ReceiveStatisticsProxy::SampleCounter::Reset() { |
| 452 num_samples = 0; |
| 453 sum = 0; |
| 454 } |
| 455 |
357 } // namespace webrtc | 456 } // namespace webrtc |
OLD | NEW |