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/send_statistics_proxy.h"
12
13 #include <algorithm>
14 #include <cmath>
15 #include <map>
16 #include <vector>
17
18 #include "webrtc/base/checks.h"
19 #include "webrtc/base/logging.h"
20 #include "webrtc/modules/video_coding/include/video_codec_interface.h"
21 #include "webrtc/system_wrappers/include/metrics.h"
22
23 namespace webrtc {
24 namespace {
25 const float kEncodeTimeWeigthFactor = 0.5f;
26
27 // Used by histograms. Values of entries should not be changed.
28 enum HistogramCodecType {
29 kVideoUnknown = 0,
30 kVideoVp8 = 1,
31 kVideoVp9 = 2,
32 kVideoH264 = 3,
33 kVideoMax = 64,
34 };
35
36 const char* kRealtimePrefix = "WebRTC.Video.";
37 const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
38
GetUmaPrefix(VideoEncoderConfig::ContentType content_type)39 const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
40 switch (content_type) {
41 case VideoEncoderConfig::ContentType::kRealtimeVideo:
42 return kRealtimePrefix;
43 case VideoEncoderConfig::ContentType::kScreen:
44 return kScreenPrefix;
45 }
46 RTC_NOTREACHED();
47 return nullptr;
48 }
49
PayloadNameToHistogramCodecType(const std::string & payload_name)50 HistogramCodecType PayloadNameToHistogramCodecType(
51 const std::string& payload_name) {
52 if (payload_name == "VP8") {
53 return kVideoVp8;
54 } else if (payload_name == "VP9") {
55 return kVideoVp9;
56 } else if (payload_name == "H264") {
57 return kVideoH264;
58 } else {
59 return kVideoUnknown;
60 }
61 }
62
UpdateCodecTypeHistogram(const std::string & payload_name)63 void UpdateCodecTypeHistogram(const std::string& payload_name) {
64 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
65 PayloadNameToHistogramCodecType(payload_name),
66 kVideoMax);
67 }
68 } // namespace
69
70
71 const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
72
SendStatisticsProxy(Clock * clock,const VideoSendStream::Config & config,VideoEncoderConfig::ContentType content_type)73 SendStatisticsProxy::SendStatisticsProxy(
74 Clock* clock,
75 const VideoSendStream::Config& config,
76 VideoEncoderConfig::ContentType content_type)
77 : clock_(clock),
78 payload_name_(config.encoder_settings.payload_name),
79 rtp_config_(config.rtp),
80 content_type_(content_type),
81 start_ms_(clock->TimeInMilliseconds()),
82 last_sent_frame_timestamp_(0),
83 encode_time_(kEncodeTimeWeigthFactor),
84 uma_container_(
85 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
86 }
87
~SendStatisticsProxy()88 SendStatisticsProxy::~SendStatisticsProxy() {
89 rtc::CritScope lock(&crit_);
90 uma_container_->UpdateHistograms(rtp_config_, stats_);
91
92 int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
93 RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
94 elapsed_sec);
95
96 if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
97 UpdateCodecTypeHistogram(payload_name_);
98 }
99
UmaSamplesContainer(const char * prefix,const VideoSendStream::Stats & stats,Clock * const clock)100 SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
101 const char* prefix,
102 const VideoSendStream::Stats& stats,
103 Clock* const clock)
104 : uma_prefix_(prefix),
105 clock_(clock),
106 max_sent_width_per_timestamp_(0),
107 max_sent_height_per_timestamp_(0),
108 input_frame_rate_tracker_(100, 10u),
109 input_fps_counter_(clock, nullptr, true),
110 sent_fps_counter_(clock, nullptr, true),
111 first_rtcp_stats_time_ms_(-1),
112 first_rtp_stats_time_ms_(-1),
113 start_stats_(stats) {}
114
~UmaSamplesContainer()115 SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
116
AccumulateRtxStats(const VideoSendStream::Stats & stats,const std::vector<uint32_t> & rtx_ssrcs,StreamDataCounters * total_rtp_stats,StreamDataCounters * rtx_stats)117 void AccumulateRtxStats(const VideoSendStream::Stats& stats,
118 const std::vector<uint32_t>& rtx_ssrcs,
119 StreamDataCounters* total_rtp_stats,
120 StreamDataCounters* rtx_stats) {
121 for (auto it : stats.substreams) {
122 if (std::find(rtx_ssrcs.begin(), rtx_ssrcs.end(), it.first) !=
123 rtx_ssrcs.end()) {
124 rtx_stats->Add(it.second.rtp_stats);
125 } else {
126 total_rtp_stats->Add(it.second.rtp_stats);
127 }
128 }
129 }
130
UpdateHistograms(const VideoSendStream::Config::Rtp & rtp_config,const VideoSendStream::Stats & current_stats)131 void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
132 const VideoSendStream::Config::Rtp& rtp_config,
133 const VideoSendStream::Stats& current_stats) {
134 RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
135 const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
136 const int kMinRequiredPeriodicSamples = 6;
137 int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
138 int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
139 if (in_width != -1) {
140 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
141 in_width);
142 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
143 in_height);
144 }
145 AggregatedStats in_fps = input_fps_counter_.GetStats();
146 if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
147 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
148 in_fps.average);
149 LOG(LS_INFO) << uma_prefix_ + "InputFramesPerSecond, " << in_fps.ToString();
150 }
151
152 int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
153 int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
154 if (sent_width != -1) {
155 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
156 sent_width);
157 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
158 sent_height);
159 }
160 AggregatedStats sent_fps = sent_fps_counter_.GetStats();
161 if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
162 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
163 sent_fps.average);
164 LOG(LS_INFO) << uma_prefix_ + "SentFramesPerSecond, "
165 << sent_fps.ToString();
166 }
167
168 int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
169 if (encode_ms != -1) {
170 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
171 encode_ms);
172 }
173 int key_frames_permille =
174 key_frame_counter_.Permille(kMinRequiredMetricsSamples);
175 if (key_frames_permille != -1) {
176 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
177 key_frames_permille);
178 }
179 int quality_limited =
180 quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
181 if (quality_limited != -1) {
182 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
183 uma_prefix_ + "QualityLimitedResolutionInPercent",
184 quality_limited);
185 }
186 int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
187 if (downscales != -1) {
188 RTC_HISTOGRAMS_ENUMERATION(
189 kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
190 20);
191 }
192 int cpu_limited =
193 cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
194 if (cpu_limited != -1) {
195 RTC_HISTOGRAMS_PERCENTAGE(
196 kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
197 }
198 int bw_limited =
199 bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
200 if (bw_limited != -1) {
201 RTC_HISTOGRAMS_PERCENTAGE(
202 kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
203 bw_limited);
204 }
205 int num_disabled =
206 bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
207 if (num_disabled != -1) {
208 RTC_HISTOGRAMS_ENUMERATION(
209 kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
210 num_disabled, 10);
211 }
212 int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
213 if (delay_ms != -1)
214 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
215 delay_ms);
216
217 int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
218 if (max_delay_ms != -1) {
219 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
220 max_delay_ms);
221 }
222
223 for (const auto& it : qp_counters_) {
224 int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
225 if (qp_vp8 != -1) {
226 int spatial_idx = it.first;
227 if (spatial_idx == -1) {
228 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
229 qp_vp8);
230 } else if (spatial_idx == 0) {
231 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
232 qp_vp8);
233 } else if (spatial_idx == 1) {
234 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
235 qp_vp8);
236 } else if (spatial_idx == 2) {
237 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
238 qp_vp8);
239 } else {
240 LOG(LS_WARNING) << "QP stats not recorded for VP8 spatial idx "
241 << spatial_idx;
242 }
243 }
244 int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
245 if (qp_vp9 != -1) {
246 int spatial_idx = it.first;
247 if (spatial_idx == -1) {
248 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
249 qp_vp9);
250 } else if (spatial_idx == 0) {
251 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
252 qp_vp9);
253 } else if (spatial_idx == 1) {
254 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
255 qp_vp9);
256 } else if (spatial_idx == 2) {
257 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
258 qp_vp9);
259 } else {
260 LOG(LS_WARNING) << "QP stats not recorded for VP9 spatial layer "
261 << spatial_idx;
262 }
263 }
264 int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
265 if (qp_h264 != -1) {
266 int spatial_idx = it.first;
267 RTC_DCHECK_EQ(-1, spatial_idx);
268 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "Encoded.Qp.H264",
269 qp_h264);
270 }
271 }
272
273 if (first_rtcp_stats_time_ms_ != -1) {
274 int64_t elapsed_sec =
275 (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
276 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
277 int fraction_lost = report_block_stats_.FractionLostInPercent();
278 if (fraction_lost != -1) {
279 RTC_HISTOGRAMS_PERCENTAGE(
280 kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
281 }
282
283 // The RTCP packet type counters, delivered via the
284 // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
285 // life of the send stream and are not reset when switching content type.
286 // For the purpose of these statistics though, we want new counts when
287 // switching since we switch histogram name. On every reset of the
288 // UmaSamplesContainer, we save the initial state of the counters, so that
289 // we can calculate the delta here and aggregate over all ssrcs.
290 RtcpPacketTypeCounter counters;
291 for (uint32_t ssrc : rtp_config.ssrcs) {
292 auto kv = current_stats.substreams.find(ssrc);
293 if (kv == current_stats.substreams.end())
294 continue;
295
296 RtcpPacketTypeCounter stream_counters =
297 kv->second.rtcp_packet_type_counts;
298 kv = start_stats_.substreams.find(ssrc);
299 if (kv != start_stats_.substreams.end())
300 stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
301
302 counters.Add(stream_counters);
303 }
304 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
305 uma_prefix_ + "NackPacketsReceivedPerMinute",
306 counters.nack_packets * 60 / elapsed_sec);
307 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
308 uma_prefix_ + "FirPacketsReceivedPerMinute",
309 counters.fir_packets * 60 / elapsed_sec);
310 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
311 uma_prefix_ + "PliPacketsReceivedPerMinute",
312 counters.pli_packets * 60 / elapsed_sec);
313 if (counters.nack_requests > 0) {
314 RTC_HISTOGRAMS_PERCENTAGE(
315 kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
316 counters.UniqueNackRequestsInPercent());
317 }
318 }
319 }
320
321 if (first_rtp_stats_time_ms_ != -1) {
322 int64_t elapsed_sec =
323 (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
324 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
325 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
326 target_rate_updates_.pause_resume_events);
327
328 int paused_time_percent =
329 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
330 if (paused_time_percent != -1) {
331 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
332 paused_time_percent);
333 }
334
335 StreamDataCounters rtp;
336 StreamDataCounters rtx;
337 AccumulateRtxStats(current_stats, rtp_config.rtx.ssrcs, &rtp, &rtx);
338 StreamDataCounters start_rtp;
339 StreamDataCounters start_rtx;
340 AccumulateRtxStats(start_stats_, rtp_config.rtx.ssrcs, &start_rtp,
341 &start_rtx);
342 rtp.Subtract(start_rtp);
343 rtx.Subtract(start_rtx);
344 StreamDataCounters rtp_rtx = rtp;
345 rtp_rtx.Add(rtx);
346
347 RTC_HISTOGRAMS_COUNTS_10000(
348 kIndex, uma_prefix_ + "BitrateSentInKbps",
349 static_cast<int>(rtp_rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
350 1000));
351 RTC_HISTOGRAMS_COUNTS_10000(
352 kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
353 static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000));
354 RTC_HISTOGRAMS_COUNTS_10000(
355 kIndex, uma_prefix_ + "PaddingBitrateSentInKbps",
356 static_cast<int>(rtp_rtx.transmitted.padding_bytes * 8 / elapsed_sec /
357 1000));
358 RTC_HISTOGRAMS_COUNTS_10000(
359 kIndex, uma_prefix_ + "RetransmittedBitrateSentInKbps",
360 static_cast<int>(rtp_rtx.retransmitted.TotalBytes() * 8 /
361 elapsed_sec / 1000));
362 if (!rtp_config.rtx.ssrcs.empty()) {
363 RTC_HISTOGRAMS_COUNTS_10000(
364 kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
365 static_cast<int>(rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
366 1000));
367 }
368 if (rtp_config.flexfec.payload_type != -1 ||
369 rtp_config.ulpfec.red_payload_type != -1) {
370 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
371 uma_prefix_ + "FecBitrateSentInKbps",
372 static_cast<int>(rtp_rtx.fec.TotalBytes() *
373 8 / elapsed_sec / 1000));
374 }
375 }
376 }
377 }
378
OnEncoderReconfigured(const VideoEncoderConfig & config,uint32_t preferred_bitrate_bps)379 void SendStatisticsProxy::OnEncoderReconfigured(
380 const VideoEncoderConfig& config,
381 uint32_t preferred_bitrate_bps) {
382 rtc::CritScope lock(&crit_);
383 stats_.preferred_media_bitrate_bps = preferred_bitrate_bps;
384
385 if (content_type_ != config.content_type) {
386 uma_container_->UpdateHistograms(rtp_config_, stats_);
387 uma_container_.reset(new UmaSamplesContainer(
388 GetUmaPrefix(config.content_type), stats_, clock_));
389 content_type_ = config.content_type;
390 }
391 }
392
OnEncoderStatsUpdate(uint32_t framerate,uint32_t bitrate)393 void SendStatisticsProxy::OnEncoderStatsUpdate(uint32_t framerate,
394 uint32_t bitrate) {
395 rtc::CritScope lock(&crit_);
396 stats_.encode_frame_rate = framerate;
397 stats_.media_bitrate_bps = bitrate;
398 }
399
OnEncodedFrameTimeMeasured(int encode_time_ms,const CpuOveruseMetrics & metrics)400 void SendStatisticsProxy::OnEncodedFrameTimeMeasured(
401 int encode_time_ms,
402 const CpuOveruseMetrics& metrics) {
403 rtc::CritScope lock(&crit_);
404 uma_container_->encode_time_counter_.Add(encode_time_ms);
405 encode_time_.Apply(1.0f, encode_time_ms);
406 stats_.avg_encode_time_ms = round(encode_time_.filtered());
407 stats_.encode_usage_percent = metrics.encode_usage_percent;
408 }
409
OnSuspendChange(bool is_suspended)410 void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
411 rtc::CritScope lock(&crit_);
412 stats_.suspended = is_suspended;
413 // Pause framerate stats.
414 if (is_suspended) {
415 uma_container_->input_fps_counter_.ProcessAndPause();
416 uma_container_->sent_fps_counter_.ProcessAndPause();
417 }
418 }
419
GetStats()420 VideoSendStream::Stats SendStatisticsProxy::GetStats() {
421 rtc::CritScope lock(&crit_);
422 PurgeOldStats();
423 stats_.input_frame_rate =
424 round(uma_container_->input_frame_rate_tracker_.ComputeRate());
425 return stats_;
426 }
427
PurgeOldStats()428 void SendStatisticsProxy::PurgeOldStats() {
429 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
430 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
431 stats_.substreams.begin();
432 it != stats_.substreams.end(); ++it) {
433 uint32_t ssrc = it->first;
434 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
435 it->second.width = 0;
436 it->second.height = 0;
437 }
438 }
439 }
440
GetStatsEntry(uint32_t ssrc)441 VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
442 uint32_t ssrc) {
443 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
444 stats_.substreams.find(ssrc);
445 if (it != stats_.substreams.end())
446 return &it->second;
447
448 bool is_media = std::find(rtp_config_.ssrcs.begin(), rtp_config_.ssrcs.end(),
449 ssrc) != rtp_config_.ssrcs.end();
450 bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
451 ssrc == rtp_config_.flexfec.ssrc;
452 bool is_rtx =
453 std::find(rtp_config_.rtx.ssrcs.begin(), rtp_config_.rtx.ssrcs.end(),
454 ssrc) != rtp_config_.rtx.ssrcs.end();
455 if (!is_media && !is_flexfec && !is_rtx)
456 return nullptr;
457
458 // Insert new entry and return ptr.
459 VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
460 entry->is_rtx = is_rtx;
461 entry->is_flexfec = is_flexfec;
462
463 return entry;
464 }
465
OnInactiveSsrc(uint32_t ssrc)466 void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
467 rtc::CritScope lock(&crit_);
468 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
469 if (!stats)
470 return;
471
472 stats->total_bitrate_bps = 0;
473 stats->retransmit_bitrate_bps = 0;
474 stats->height = 0;
475 stats->width = 0;
476 }
477
OnSetEncoderTargetRate(uint32_t bitrate_bps)478 void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
479 rtc::CritScope lock(&crit_);
480 if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
481 return; // Start on first non-zero bitrate, may initially be zero.
482
483 int64_t now = clock_->TimeInMilliseconds();
484 if (uma_container_->target_rate_updates_.last_ms != -1) {
485 bool was_paused = stats_.target_media_bitrate_bps == 0;
486 int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
487 uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
488
489 // Use last to not include update when stream is stopped and video disabled.
490 if (uma_container_->target_rate_updates_.last_paused_or_resumed)
491 ++uma_container_->target_rate_updates_.pause_resume_events;
492
493 // Check if video is paused/resumed.
494 uma_container_->target_rate_updates_.last_paused_or_resumed =
495 (bitrate_bps == 0) != was_paused;
496 }
497 uma_container_->target_rate_updates_.last_ms = now;
498
499 stats_.target_media_bitrate_bps = bitrate_bps;
500 }
501
OnSendEncodedImage(const EncodedImage & encoded_image,const CodecSpecificInfo * codec_info)502 void SendStatisticsProxy::OnSendEncodedImage(
503 const EncodedImage& encoded_image,
504 const CodecSpecificInfo* codec_info) {
505 size_t simulcast_idx = 0;
506
507 rtc::CritScope lock(&crit_);
508 ++stats_.frames_encoded;
509 if (codec_info) {
510 if (codec_info->codecType == kVideoCodecVP8) {
511 simulcast_idx = codec_info->codecSpecific.VP8.simulcastIdx;
512 } else if (codec_info->codecType == kVideoCodecGeneric) {
513 simulcast_idx = codec_info->codecSpecific.generic.simulcast_idx;
514 }
515 if (codec_info->codec_name) {
516 stats_.encoder_implementation_name = codec_info->codec_name;
517 }
518 }
519
520 if (simulcast_idx >= rtp_config_.ssrcs.size()) {
521 LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
522 << " >= " << rtp_config_.ssrcs.size() << ").";
523 return;
524 }
525 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
526
527 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
528 if (!stats)
529 return;
530
531 stats->width = encoded_image._encodedWidth;
532 stats->height = encoded_image._encodedHeight;
533 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
534
535 uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
536 kVideoFrameKey);
537 stats_.bw_limited_resolution =
538 encoded_image.adapt_reason_.bw_resolutions_disabled > 0 ||
539 quality_downscales_ > 0;
540
541 if (quality_downscales_ != -1) {
542 uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
543 if (quality_downscales_ > 0)
544 uma_container_->quality_downscales_counter_.Add(quality_downscales_);
545 }
546 if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) {
547 bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0;
548 uma_container_->bw_limited_frame_counter_.Add(bw_limited);
549 if (bw_limited) {
550 uma_container_->bw_resolutions_disabled_counter_.Add(
551 encoded_image.adapt_reason_.bw_resolutions_disabled);
552 }
553 }
554
555 if (encoded_image.qp_ != -1) {
556 if (!stats_.qp_sum)
557 stats_.qp_sum = rtc::Optional<uint64_t>(0);
558 *stats_.qp_sum += encoded_image.qp_;
559
560 if (codec_info) {
561 if (codec_info->codecType == kVideoCodecVP8) {
562 int spatial_idx = (rtp_config_.ssrcs.size() == 1)
563 ? -1
564 : static_cast<int>(simulcast_idx);
565 uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
566 } else if (codec_info->codecType == kVideoCodecVP9) {
567 int spatial_idx =
568 (codec_info->codecSpecific.VP9.num_spatial_layers == 1)
569 ? -1
570 : codec_info->codecSpecific.VP9.spatial_idx;
571 uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
572 } else if (codec_info->codecType == kVideoCodecH264) {
573 int spatial_idx = -1;
574 uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
575 }
576 }
577 }
578
579 // TODO(asapersson): This is incorrect if simulcast layers are encoded on
580 // different threads and there is no guarantee that one frame of all layers
581 // are encoded before the next start.
582 if (last_sent_frame_timestamp_ > 0 &&
583 encoded_image._timeStamp != last_sent_frame_timestamp_) {
584 uma_container_->sent_fps_counter_.Add(1);
585 uma_container_->sent_width_counter_.Add(
586 uma_container_->max_sent_width_per_timestamp_);
587 uma_container_->sent_height_counter_.Add(
588 uma_container_->max_sent_height_per_timestamp_);
589 uma_container_->max_sent_width_per_timestamp_ = 0;
590 uma_container_->max_sent_height_per_timestamp_ = 0;
591 }
592 last_sent_frame_timestamp_ = encoded_image._timeStamp;
593 uma_container_->max_sent_width_per_timestamp_ =
594 std::max(uma_container_->max_sent_width_per_timestamp_,
595 static_cast<int>(encoded_image._encodedWidth));
596 uma_container_->max_sent_height_per_timestamp_ =
597 std::max(uma_container_->max_sent_height_per_timestamp_,
598 static_cast<int>(encoded_image._encodedHeight));
599 }
600
GetSendFrameRate() const601 int SendStatisticsProxy::GetSendFrameRate() const {
602 rtc::CritScope lock(&crit_);
603 return stats_.encode_frame_rate;
604 }
605
OnIncomingFrame(int width,int height)606 void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
607 rtc::CritScope lock(&crit_);
608 uma_container_->input_frame_rate_tracker_.AddSamples(1);
609 uma_container_->input_fps_counter_.Add(1);
610 uma_container_->input_width_counter_.Add(width);
611 uma_container_->input_height_counter_.Add(height);
612 uma_container_->cpu_limited_frame_counter_.Add(stats_.cpu_limited_resolution);
613 }
614
SetResolutionRestrictionStats(bool scaling_enabled,bool cpu_restricted,int num_quality_downscales)615 void SendStatisticsProxy::SetResolutionRestrictionStats(
616 bool scaling_enabled,
617 bool cpu_restricted,
618 int num_quality_downscales) {
619 rtc::CritScope lock(&crit_);
620 if (scaling_enabled) {
621 quality_downscales_ = num_quality_downscales;
622 stats_.bw_limited_resolution = quality_downscales_ > 0;
623 stats_.cpu_limited_resolution = cpu_restricted;
624 } else {
625 stats_.bw_limited_resolution = false;
626 stats_.cpu_limited_resolution = false;
627 quality_downscales_ = -1;
628 }
629 }
630
OnCpuRestrictedResolutionChanged(bool cpu_restricted_resolution)631 void SendStatisticsProxy::OnCpuRestrictedResolutionChanged(
632 bool cpu_restricted_resolution) {
633 rtc::CritScope lock(&crit_);
634 stats_.cpu_limited_resolution = cpu_restricted_resolution;
635 ++stats_.number_of_cpu_adapt_changes;
636 }
637
OnQualityRestrictedResolutionChanged(int num_quality_downscales)638 void SendStatisticsProxy::OnQualityRestrictedResolutionChanged(
639 int num_quality_downscales) {
640 rtc::CritScope lock(&crit_);
641 quality_downscales_ = num_quality_downscales;
642 stats_.bw_limited_resolution = quality_downscales_ > 0;
643 }
644
RtcpPacketTypesCounterUpdated(uint32_t ssrc,const RtcpPacketTypeCounter & packet_counter)645 void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
646 uint32_t ssrc,
647 const RtcpPacketTypeCounter& packet_counter) {
648 rtc::CritScope lock(&crit_);
649 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
650 if (!stats)
651 return;
652
653 stats->rtcp_packet_type_counts = packet_counter;
654 if (uma_container_->first_rtcp_stats_time_ms_ == -1)
655 uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
656 }
657
StatisticsUpdated(const RtcpStatistics & statistics,uint32_t ssrc)658 void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
659 uint32_t ssrc) {
660 rtc::CritScope lock(&crit_);
661 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
662 if (!stats)
663 return;
664
665 stats->rtcp_stats = statistics;
666 uma_container_->report_block_stats_.Store(statistics, 0, ssrc);
667 }
668
CNameChanged(const char * cname,uint32_t ssrc)669 void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {}
670
DataCountersUpdated(const StreamDataCounters & counters,uint32_t ssrc)671 void SendStatisticsProxy::DataCountersUpdated(
672 const StreamDataCounters& counters,
673 uint32_t ssrc) {
674 rtc::CritScope lock(&crit_);
675 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
676 RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc: "
677 << ssrc;
678
679 if (stats->is_flexfec) {
680 // The same counters are reported for both the media ssrc and flexfec ssrc.
681 // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
682 return;
683 }
684
685 stats->rtp_stats = counters;
686 if (uma_container_->first_rtp_stats_time_ms_ == -1)
687 uma_container_->first_rtp_stats_time_ms_ = clock_->TimeInMilliseconds();
688 }
689
Notify(uint32_t total_bitrate_bps,uint32_t retransmit_bitrate_bps,uint32_t ssrc)690 void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
691 uint32_t retransmit_bitrate_bps,
692 uint32_t ssrc) {
693 rtc::CritScope lock(&crit_);
694 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
695 if (!stats)
696 return;
697
698 stats->total_bitrate_bps = total_bitrate_bps;
699 stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
700 }
701
FrameCountUpdated(const FrameCounts & frame_counts,uint32_t ssrc)702 void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
703 uint32_t ssrc) {
704 rtc::CritScope lock(&crit_);
705 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
706 if (!stats)
707 return;
708
709 stats->frame_counts = frame_counts;
710 }
711
SendSideDelayUpdated(int avg_delay_ms,int max_delay_ms,uint32_t ssrc)712 void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
713 int max_delay_ms,
714 uint32_t ssrc) {
715 rtc::CritScope lock(&crit_);
716 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
717 if (!stats)
718 return;
719 stats->avg_delay_ms = avg_delay_ms;
720 stats->max_delay_ms = max_delay_ms;
721
722 uma_container_->delay_counter_.Add(avg_delay_ms);
723 uma_container_->max_delay_counter_.Add(max_delay_ms);
724 }
725
Add(int sample)726 void SendStatisticsProxy::SampleCounter::Add(int sample) {
727 sum += sample;
728 ++num_samples;
729 }
730
Avg(int64_t min_required_samples) const731 int SendStatisticsProxy::SampleCounter::Avg(
732 int64_t min_required_samples) const {
733 if (num_samples < min_required_samples || num_samples == 0)
734 return -1;
735 return static_cast<int>((sum + (num_samples / 2)) / num_samples);
736 }
737
Add(bool sample)738 void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
739 if (sample)
740 ++sum;
741 ++num_samples;
742 }
743
Add(bool sample,int64_t count)744 void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
745 if (sample)
746 sum += count;
747 num_samples += count;
748 }
Percent(int64_t min_required_samples) const749 int SendStatisticsProxy::BoolSampleCounter::Percent(
750 int64_t min_required_samples) const {
751 return Fraction(min_required_samples, 100.0f);
752 }
753
Permille(int64_t min_required_samples) const754 int SendStatisticsProxy::BoolSampleCounter::Permille(
755 int64_t min_required_samples) const {
756 return Fraction(min_required_samples, 1000.0f);
757 }
758
Fraction(int64_t min_required_samples,float multiplier) const759 int SendStatisticsProxy::BoolSampleCounter::Fraction(
760 int64_t min_required_samples,
761 float multiplier) const {
762 if (num_samples < min_required_samples || num_samples == 0)
763 return -1;
764 return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
765 }
766 } // namespace webrtc
767