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