Line data Source code
1 : /*
2 : * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 : *
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
6 : * tree. An additional intellectual property rights grant can be found
7 : * in the file PATENTS. All contributing project authors may
8 : * be found in the AUTHORS file in the root of the source tree.
9 : */
10 :
11 : #include "webrtc/video/receive_statistics_proxy.h"
12 :
13 : #include <cmath>
14 :
15 : #include "webrtc/base/checks.h"
16 : #include "webrtc/base/logging.h"
17 : #include "webrtc/modules/video_coding/include/video_codec_interface.h"
18 : #include "webrtc/system_wrappers/include/clock.h"
19 : #include "webrtc/system_wrappers/include/field_trial.h"
20 : #include "webrtc/system_wrappers/include/metrics.h"
21 :
22 : namespace webrtc {
23 : namespace {
24 : // Periodic time interval for processing samples for |freq_offset_counter_|.
25 : const int64_t kFreqOffsetProcessIntervalMs = 40000;
26 :
27 : // Configuration for bad call detection.
28 : const int kBadCallMinRequiredSamples = 10;
29 : const int kMinSampleLengthMs = 990;
30 : const int kNumMeasurements = 10;
31 : const int kNumMeasurementsVariance = kNumMeasurements * 1.5;
32 : const float kBadFraction = 0.8f;
33 : // For fps:
34 : // Low means low enough to be bad, high means high enough to be good
35 : const int kLowFpsThreshold = 12;
36 : const int kHighFpsThreshold = 14;
37 : // For qp and fps variance:
38 : // Low means low enough to be good, high means high enough to be bad
39 : const int kLowQpThresholdVp8 = 60;
40 : const int kHighQpThresholdVp8 = 70;
41 : const int kLowVarianceThreshold = 1;
42 : const int kHighVarianceThreshold = 2;
43 : } // namespace
44 :
45 0 : ReceiveStatisticsProxy::ReceiveStatisticsProxy(
46 : const VideoReceiveStream::Config* config,
47 0 : Clock* clock)
48 : : clock_(clock),
49 : config_(*config),
50 0 : start_ms_(clock->TimeInMilliseconds()),
51 0 : last_sample_time_(clock->TimeInMilliseconds()),
52 : fps_threshold_(kLowFpsThreshold,
53 : kHighFpsThreshold,
54 : kBadFraction,
55 : kNumMeasurements),
56 : qp_threshold_(kLowQpThresholdVp8,
57 : kHighQpThresholdVp8,
58 : kBadFraction,
59 : kNumMeasurements),
60 : variance_threshold_(kLowVarianceThreshold,
61 : kHighVarianceThreshold,
62 : kBadFraction,
63 : kNumMeasurementsVariance),
64 : num_bad_states_(0),
65 : num_certain_states_(0),
66 : // 1000ms window, scale 1000 for ms to s.
67 : decode_fps_estimator_(1000, 1000),
68 : renders_fps_estimator_(1000, 1000),
69 : render_fps_tracker_(100, 10u),
70 : render_pixel_tracker_(100u, 10u),
71 : freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs),
72 : first_report_block_time_ms_(-1),
73 0 : receive_state_(kReceiveStateInitial) {
74 0 : stats_.ssrc = config_.rtp.remote_ssrc;
75 0 : for (auto it : config_.rtp.rtx)
76 0 : rtx_stats_[it.second.ssrc] = StreamDataCounters();
77 0 : }
78 :
79 0 : ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {
80 0 : UpdateHistograms();
81 0 : }
82 :
83 0 : void ReceiveStatisticsProxy::UpdateHistograms() {
84 0 : RTC_HISTOGRAM_COUNTS_100000(
85 : "WebRTC.Video.ReceiveStreamLifetimeInSeconds",
86 : (clock_->TimeInMilliseconds() - start_ms_) / 1000);
87 :
88 0 : if (first_report_block_time_ms_ != -1 &&
89 0 : ((clock_->TimeInMilliseconds() - first_report_block_time_ms_) / 1000) >=
90 : metrics::kMinRunTimeInSeconds) {
91 0 : int fraction_lost = report_block_stats_.FractionLostInPercent();
92 0 : if (fraction_lost != -1) {
93 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.ReceivedPacketsLostInPercent",
94 : fraction_lost);
95 : }
96 : }
97 :
98 0 : const int kMinRequiredSamples = 200;
99 0 : int samples = static_cast<int>(render_fps_tracker_.TotalSampleCount());
100 0 : if (samples > kMinRequiredSamples) {
101 0 : RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.RenderFramesPerSecond",
102 : round(render_fps_tracker_.ComputeTotalRate()));
103 0 : RTC_HISTOGRAM_COUNTS_100000(
104 : "WebRTC.Video.RenderSqrtPixelsPerSecond",
105 : round(render_pixel_tracker_.ComputeTotalRate()));
106 : }
107 0 : int width = render_width_counter_.Avg(kMinRequiredSamples);
108 0 : int height = render_height_counter_.Avg(kMinRequiredSamples);
109 0 : if (width != -1) {
110 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedWidthInPixels", width);
111 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedHeightInPixels", height);
112 : }
113 0 : int sync_offset_ms = sync_offset_counter_.Avg(kMinRequiredSamples);
114 0 : if (sync_offset_ms != -1) {
115 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.AVSyncOffsetInMs", sync_offset_ms);
116 : }
117 0 : AggregatedStats freq_offset_stats = freq_offset_counter_.GetStats();
118 0 : if (freq_offset_stats.num_samples > 0) {
119 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtpToNtpFreqOffsetInKhz",
120 : freq_offset_stats.average);
121 0 : LOG(LS_INFO) << "WebRTC.Video.RtpToNtpFreqOffsetInKhz, "
122 0 : << freq_offset_stats.ToString();
123 : }
124 :
125 0 : int qp = qp_counters_.vp8.Avg(kMinRequiredSamples);
126 0 : if (qp != -1)
127 0 : RTC_HISTOGRAM_COUNTS_200("WebRTC.Video.Decoded.Vp8.Qp", qp);
128 :
129 : // TODO(asapersson): DecoderTiming() is call periodically (each 1000ms) and
130 : // not per frame. Change decode time to include every frame.
131 0 : const int kMinRequiredDecodeSamples = 5;
132 0 : int decode_ms = decode_time_counter_.Avg(kMinRequiredDecodeSamples);
133 0 : if (decode_ms != -1)
134 0 : RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.DecodeTimeInMs", decode_ms);
135 :
136 0 : if (field_trial::FindFullName("WebRTC-NewVideoJitterBuffer") !=
137 : "Enabled") {
138 : int jb_delay_ms =
139 0 : jitter_buffer_delay_counter_.Avg(kMinRequiredDecodeSamples);
140 0 : if (jb_delay_ms != -1) {
141 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.JitterBufferDelayInMs",
142 : jb_delay_ms);
143 : }
144 : }
145 0 : int target_delay_ms = target_delay_counter_.Avg(kMinRequiredDecodeSamples);
146 0 : if (target_delay_ms != -1) {
147 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.TargetDelayInMs", target_delay_ms);
148 : }
149 0 : int current_delay_ms = current_delay_counter_.Avg(kMinRequiredDecodeSamples);
150 0 : if (current_delay_ms != -1) {
151 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.CurrentDelayInMs",
152 : current_delay_ms);
153 : }
154 0 : int delay_ms = delay_counter_.Avg(kMinRequiredDecodeSamples);
155 0 : if (delay_ms != -1)
156 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.OnewayDelayInMs", delay_ms);
157 :
158 0 : int e2e_delay_ms = e2e_delay_counter_.Avg(kMinRequiredSamples);
159 0 : if (e2e_delay_ms != -1)
160 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.EndToEndDelayInMs", e2e_delay_ms);
161 :
162 0 : StreamDataCounters rtp = stats_.rtp_stats;
163 0 : StreamDataCounters rtx;
164 0 : for (auto it : rtx_stats_)
165 0 : rtx.Add(it.second);
166 0 : StreamDataCounters rtp_rtx = rtp;
167 0 : rtp_rtx.Add(rtx);
168 : int64_t elapsed_sec =
169 0 : rtp_rtx.TimeSinceFirstPacketInMs(clock_->TimeInMilliseconds()) / 1000;
170 0 : if (elapsed_sec > metrics::kMinRunTimeInSeconds) {
171 0 : RTC_HISTOGRAM_COUNTS_10000(
172 : "WebRTC.Video.BitrateReceivedInKbps",
173 : static_cast<int>(rtp_rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
174 : 1000));
175 0 : RTC_HISTOGRAM_COUNTS_10000(
176 : "WebRTC.Video.MediaBitrateReceivedInKbps",
177 : static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000));
178 0 : RTC_HISTOGRAM_COUNTS_10000(
179 : "WebRTC.Video.PaddingBitrateReceivedInKbps",
180 : static_cast<int>(rtp_rtx.transmitted.padding_bytes * 8 / elapsed_sec /
181 : 1000));
182 0 : RTC_HISTOGRAM_COUNTS_10000(
183 : "WebRTC.Video.RetransmittedBitrateReceivedInKbps",
184 : static_cast<int>(rtp_rtx.retransmitted.TotalBytes() * 8 / elapsed_sec /
185 : 1000));
186 0 : if (!rtx_stats_.empty()) {
187 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtxBitrateReceivedInKbps",
188 : static_cast<int>(rtx.transmitted.TotalBytes() *
189 : 8 / elapsed_sec / 1000));
190 : }
191 0 : if (config_.rtp.ulpfec.ulpfec_payload_type != -1) {
192 0 : RTC_HISTOGRAM_COUNTS_10000(
193 : "WebRTC.Video.FecBitrateReceivedInKbps",
194 : static_cast<int>(rtp_rtx.fec.TotalBytes() * 8 / elapsed_sec / 1000));
195 : }
196 0 : const RtcpPacketTypeCounter& counters = stats_.rtcp_packet_type_counts;
197 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute",
198 : counters.nack_packets * 60 / elapsed_sec);
199 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute",
200 : counters.fir_packets * 60 / elapsed_sec);
201 0 : RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute",
202 : counters.pli_packets * 60 / elapsed_sec);
203 0 : if (counters.nack_requests > 0) {
204 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent",
205 : counters.UniqueNackRequestsInPercent());
206 : }
207 : }
208 :
209 0 : if (num_certain_states_ >= kBadCallMinRequiredSamples) {
210 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Any",
211 : 100 * num_bad_states_ / num_certain_states_);
212 : }
213 : rtc::Optional<double> fps_fraction =
214 0 : fps_threshold_.FractionHigh(kBadCallMinRequiredSamples);
215 0 : if (fps_fraction) {
216 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRate",
217 : static_cast<int>(100 * (1 - *fps_fraction)));
218 : }
219 : rtc::Optional<double> variance_fraction =
220 0 : variance_threshold_.FractionHigh(kBadCallMinRequiredSamples);
221 0 : if (variance_fraction) {
222 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRateVariance",
223 : static_cast<int>(100 * *variance_fraction));
224 : }
225 : rtc::Optional<double> qp_fraction =
226 0 : qp_threshold_.FractionHigh(kBadCallMinRequiredSamples);
227 0 : if (qp_fraction) {
228 0 : RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Qp",
229 : static_cast<int>(100 * *qp_fraction));
230 : }
231 0 : }
232 :
233 0 : void ReceiveStatisticsProxy::QualitySample() {
234 0 : int64_t now = clock_->TimeInMilliseconds();
235 0 : if (last_sample_time_ + kMinSampleLengthMs > now)
236 0 : return;
237 :
238 : double fps =
239 0 : render_fps_tracker_.ComputeRateForInterval(now - last_sample_time_);
240 0 : int qp = qp_sample_.Avg(1);
241 :
242 0 : bool prev_fps_bad = !fps_threshold_.IsHigh().value_or(true);
243 0 : bool prev_qp_bad = qp_threshold_.IsHigh().value_or(false);
244 0 : bool prev_variance_bad = variance_threshold_.IsHigh().value_or(false);
245 0 : bool prev_any_bad = prev_fps_bad || prev_qp_bad || prev_variance_bad;
246 :
247 0 : fps_threshold_.AddMeasurement(static_cast<int>(fps));
248 0 : if (qp != -1)
249 0 : qp_threshold_.AddMeasurement(qp);
250 0 : rtc::Optional<double> fps_variance_opt = fps_threshold_.CalculateVariance();
251 0 : double fps_variance = fps_variance_opt.value_or(0);
252 0 : if (fps_variance_opt) {
253 0 : variance_threshold_.AddMeasurement(static_cast<int>(fps_variance));
254 : }
255 :
256 0 : bool fps_bad = !fps_threshold_.IsHigh().value_or(true);
257 0 : bool qp_bad = qp_threshold_.IsHigh().value_or(false);
258 0 : bool variance_bad = variance_threshold_.IsHigh().value_or(false);
259 0 : bool any_bad = fps_bad || qp_bad || variance_bad;
260 :
261 0 : if (!prev_any_bad && any_bad) {
262 0 : LOG(LS_INFO) << "Bad call (any) start: " << now;
263 0 : } else if (prev_any_bad && !any_bad) {
264 0 : LOG(LS_INFO) << "Bad call (any) end: " << now;
265 : }
266 :
267 0 : if (!prev_fps_bad && fps_bad) {
268 0 : LOG(LS_INFO) << "Bad call (fps) start: " << now;
269 0 : } else if (prev_fps_bad && !fps_bad) {
270 0 : LOG(LS_INFO) << "Bad call (fps) end: " << now;
271 : }
272 :
273 0 : if (!prev_qp_bad && qp_bad) {
274 0 : LOG(LS_INFO) << "Bad call (qp) start: " << now;
275 0 : } else if (prev_qp_bad && !qp_bad) {
276 0 : LOG(LS_INFO) << "Bad call (qp) end: " << now;
277 : }
278 :
279 0 : if (!prev_variance_bad && variance_bad) {
280 0 : LOG(LS_INFO) << "Bad call (variance) start: " << now;
281 0 : } else if (prev_variance_bad && !variance_bad) {
282 0 : LOG(LS_INFO) << "Bad call (variance) end: " << now;
283 : }
284 :
285 0 : LOG(LS_VERBOSE) << "SAMPLE: sample_length: " << (now - last_sample_time_)
286 0 : << " fps: " << fps << " fps_bad: " << fps_bad << " qp: " << qp
287 0 : << " qp_bad: " << qp_bad << " variance_bad: " << variance_bad
288 0 : << " fps_variance: " << fps_variance;
289 :
290 0 : last_sample_time_ = now;
291 0 : qp_sample_.Reset();
292 :
293 0 : if (fps_threshold_.IsHigh() || variance_threshold_.IsHigh() ||
294 0 : qp_threshold_.IsHigh()) {
295 0 : if (any_bad)
296 0 : ++num_bad_states_;
297 0 : ++num_certain_states_;
298 : }
299 : }
300 :
301 0 : VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
302 0 : rtc::CritScope lock(&crit_);
303 0 : return stats_;
304 : }
305 :
306 0 : void ReceiveStatisticsProxy::OnIncomingPayloadType(int payload_type) {
307 0 : rtc::CritScope lock(&crit_);
308 0 : stats_.current_payload_type = payload_type;
309 0 : }
310 :
311 0 : void ReceiveStatisticsProxy::OnDecoderImplementationName(
312 : const char* implementation_name) {
313 0 : rtc::CritScope lock(&crit_);
314 0 : stats_.decoder_implementation_name = implementation_name;
315 0 : }
316 0 : void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate,
317 : unsigned int bitrate_bps) {
318 0 : rtc::CritScope lock(&crit_);
319 0 : if (stats_.rtp_stats.first_packet_time_ms != -1)
320 0 : QualitySample();
321 0 : stats_.network_frame_rate = framerate;
322 0 : stats_.total_bitrate_bps = bitrate_bps;
323 0 : }
324 :
325 0 : void ReceiveStatisticsProxy::ReceiveStateChange(VideoReceiveState state) {
326 0 : rtc::CritScope lock(&crit_);
327 0 : receive_state_ = state;
328 0 : }
329 :
330 0 : void ReceiveStatisticsProxy::OnDecoderTiming(int decode_ms,
331 : int max_decode_ms,
332 : int current_delay_ms,
333 : int target_delay_ms,
334 : int jitter_buffer_ms,
335 : int min_playout_delay_ms,
336 : int render_delay_ms,
337 : int64_t rtt_ms) {
338 0 : rtc::CritScope lock(&crit_);
339 0 : stats_.decode_ms = decode_ms;
340 0 : stats_.max_decode_ms = max_decode_ms;
341 0 : stats_.current_delay_ms = current_delay_ms;
342 0 : stats_.target_delay_ms = target_delay_ms;
343 0 : stats_.jitter_buffer_ms = jitter_buffer_ms;
344 0 : stats_.min_playout_delay_ms = min_playout_delay_ms;
345 0 : stats_.render_delay_ms = render_delay_ms;
346 0 : decode_time_counter_.Add(decode_ms);
347 0 : jitter_buffer_delay_counter_.Add(jitter_buffer_ms);
348 0 : target_delay_counter_.Add(target_delay_ms);
349 0 : current_delay_counter_.Add(current_delay_ms);
350 : // Network delay (rtt/2) + target_delay_ms (jitter delay + decode time +
351 : // render delay).
352 0 : delay_counter_.Add(target_delay_ms + rtt_ms / 2);
353 0 : }
354 :
355 0 : void ReceiveStatisticsProxy::RtcpPacketTypesCounterUpdated(
356 : uint32_t ssrc,
357 : const RtcpPacketTypeCounter& packet_counter) {
358 0 : rtc::CritScope lock(&crit_);
359 0 : if (stats_.ssrc != ssrc)
360 0 : return;
361 0 : stats_.rtcp_packet_type_counts = packet_counter;
362 : }
363 :
364 0 : void ReceiveStatisticsProxy::StatisticsUpdated(
365 : const webrtc::RtcpStatistics& statistics,
366 : uint32_t ssrc) {
367 0 : rtc::CritScope lock(&crit_);
368 : // TODO(pbos): Handle both local and remote ssrcs here and RTC_DCHECK that we
369 : // receive stats from one of them.
370 0 : if (stats_.ssrc != ssrc)
371 0 : return;
372 0 : stats_.rtcp_stats = statistics;
373 0 : report_block_stats_.Store(statistics, ssrc, 0);
374 :
375 0 : if (first_report_block_time_ms_ == -1)
376 0 : first_report_block_time_ms_ = clock_->TimeInMilliseconds();
377 : }
378 :
379 0 : void ReceiveStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
380 0 : rtc::CritScope lock(&crit_);
381 : // TODO(pbos): Handle both local and remote ssrcs here and RTC_DCHECK that we
382 : // receive stats from one of them.
383 0 : if (stats_.ssrc != ssrc)
384 0 : return;
385 0 : stats_.c_name = cname;
386 : }
387 :
388 0 : void ReceiveStatisticsProxy::DataCountersUpdated(
389 : const webrtc::StreamDataCounters& counters,
390 : uint32_t ssrc) {
391 0 : rtc::CritScope lock(&crit_);
392 0 : if (ssrc == stats_.ssrc) {
393 0 : stats_.rtp_stats = counters;
394 : } else {
395 0 : auto it = rtx_stats_.find(ssrc);
396 0 : if (it != rtx_stats_.end()) {
397 0 : it->second = counters;
398 : } else {
399 0 : RTC_NOTREACHED() << "Unexpected stream ssrc: " << ssrc;
400 : }
401 : }
402 0 : }
403 :
404 0 : void ReceiveStatisticsProxy::OnDecodedFrame() {
405 0 : uint64_t now = clock_->TimeInMilliseconds();
406 :
407 0 : rtc::CritScope lock(&crit_);
408 0 : ++stats_.frames_decoded;
409 0 : decode_fps_estimator_.Update(1, now);
410 0 : stats_.decode_frame_rate = decode_fps_estimator_.Rate(now).value_or(0);
411 0 : }
412 :
413 0 : void ReceiveStatisticsProxy::OnRenderedFrame(const VideoFrame& frame) {
414 0 : int width = frame.width();
415 0 : int height = frame.height();
416 0 : RTC_DCHECK_GT(width, 0);
417 0 : RTC_DCHECK_GT(height, 0);
418 0 : uint64_t now = clock_->TimeInMilliseconds();
419 :
420 0 : rtc::CritScope lock(&crit_);
421 0 : renders_fps_estimator_.Update(1, now);
422 0 : stats_.render_frame_rate = renders_fps_estimator_.Rate(now).value_or(0);
423 0 : stats_.width = width;
424 0 : stats_.height = height;
425 0 : render_width_counter_.Add(width);
426 0 : render_height_counter_.Add(height);
427 0 : render_fps_tracker_.AddSamples(1);
428 0 : render_pixel_tracker_.AddSamples(sqrt(width * height));
429 :
430 0 : if (frame.ntp_time_ms() > 0) {
431 0 : int64_t delay_ms = clock_->CurrentNtpInMilliseconds() - frame.ntp_time_ms();
432 0 : if (delay_ms >= 0)
433 0 : e2e_delay_counter_.Add(delay_ms);
434 : }
435 0 : }
436 :
437 0 : void ReceiveStatisticsProxy::OnSyncOffsetUpdated(int64_t sync_offset_ms,
438 : double estimated_freq_khz) {
439 0 : rtc::CritScope lock(&crit_);
440 0 : sync_offset_counter_.Add(std::abs(sync_offset_ms));
441 0 : stats_.sync_offset_ms = sync_offset_ms;
442 :
443 0 : const double kMaxFreqKhz = 10000.0;
444 0 : int offset_khz = kMaxFreqKhz;
445 : // Should not be zero or negative. If so, report max.
446 0 : if (estimated_freq_khz < kMaxFreqKhz && estimated_freq_khz > 0.0)
447 0 : offset_khz = static_cast<int>(std::fabs(estimated_freq_khz - 90.0) + 0.5);
448 :
449 0 : freq_offset_counter_.Add(offset_khz);
450 0 : }
451 :
452 0 : void ReceiveStatisticsProxy::OnReceiveRatesUpdated(uint32_t bitRate,
453 : uint32_t frameRate) {
454 0 : }
455 :
456 0 : void ReceiveStatisticsProxy::OnFrameCountsUpdated(
457 : const FrameCounts& frame_counts) {
458 0 : rtc::CritScope lock(&crit_);
459 0 : stats_.frame_counts = frame_counts;
460 0 : }
461 :
462 0 : void ReceiveStatisticsProxy::OnDiscardedPacketsUpdated(int discarded_packets) {
463 0 : rtc::CritScope lock(&crit_);
464 0 : stats_.discarded_packets = discarded_packets;
465 0 : }
466 :
467 0 : void ReceiveStatisticsProxy::OnPreDecode(
468 : const EncodedImage& encoded_image,
469 : const CodecSpecificInfo* codec_specific_info) {
470 0 : if (!codec_specific_info || encoded_image.qp_ == -1) {
471 0 : return;
472 : }
473 0 : if (codec_specific_info->codecType == kVideoCodecVP8) {
474 0 : qp_counters_.vp8.Add(encoded_image.qp_);
475 0 : rtc::CritScope lock(&crit_);
476 0 : qp_sample_.Add(encoded_image.qp_);
477 : }
478 : }
479 :
480 0 : void ReceiveStatisticsProxy::SampleCounter::Add(int sample) {
481 0 : sum += sample;
482 0 : ++num_samples;
483 0 : }
484 :
485 0 : int ReceiveStatisticsProxy::SampleCounter::Avg(
486 : int64_t min_required_samples) const {
487 0 : if (num_samples < min_required_samples || num_samples == 0)
488 0 : return -1;
489 0 : return static_cast<int>(sum / num_samples);
490 : }
491 :
492 0 : void ReceiveStatisticsProxy::SampleCounter::Reset() {
493 0 : num_samples = 0;
494 0 : sum = 0;
495 0 : }
496 :
497 : } // namespace webrtc
|