blob: a4f17547bdb3b4a9cff07ba2a4fd11e185229982 [file] [log] [blame]
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "video/send_statistics_proxy.h"
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000012
asaperssond89920b2015-07-22 06:52:00 -070013#include <algorithm>
Evan Shrubsolecc62b162019-09-09 11:26:45 +020014#include <array>
Tim Psiakiad13d2f2015-11-10 16:34:50 -080015#include <cmath>
Åsa Persson20317f92018-08-15 08:57:54 +020016#include <limits>
Åsa Perssonaa329e72017-12-15 15:54:44 +010017#include <utility>
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000018
Steve Antonbd631a02019-03-28 10:51:27 -070019#include "absl/algorithm/container.h"
Evan Shrubsolecc62b162019-09-09 11:26:45 +020020#include "api/video/video_codec_constants.h"
21#include "api/video/video_codec_type.h"
22#include "api/video_codecs/video_codec.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "modules/video_coding/include/video_codec_interface.h"
24#include "rtc_base/checks.h"
25#include "rtc_base/logging.h"
Åsa Persson20317f92018-08-15 08:57:54 +020026#include "rtc_base/numerics/mod_ops.h"
Tommifef05002018-02-27 13:51:08 +010027#include "rtc_base/strings/string_builder.h"
asapersson8d75ac72017-09-15 06:41:15 -070028#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "system_wrappers/include/metrics.h"
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000030
31namespace webrtc {
asapersson2a0a2a42015-10-27 01:32:00 -070032namespace {
asapersson1aa420b2015-12-07 03:12:22 -080033const float kEncodeTimeWeigthFactor = 0.5f;
Åsa Persson0122e842017-10-16 12:19:23 +020034const size_t kMaxEncodedFrameMapSize = 150;
35const int64_t kMaxEncodedFrameWindowMs = 800;
Åsa Persson20317f92018-08-15 08:57:54 +020036const uint32_t kMaxEncodedFrameTimestampDiff = 900000; // 10 sec.
Åsa Persson0122e842017-10-16 12:19:23 +020037const int64_t kBucketSizeMs = 100;
38const size_t kBucketCount = 10;
asapersson1aa420b2015-12-07 03:12:22 -080039
asapersson8d75ac72017-09-15 06:41:15 -070040const char kVp8ForcedFallbackEncoderFieldTrial[] =
Åsa Persson45bbc8a2017-11-13 10:16:47 +010041 "WebRTC-VP8-Forced-Fallback-Encoder-v2";
asapersson8d75ac72017-09-15 06:41:15 -070042const char kVp8SwCodecName[] = "libvpx";
43
asapersson2a0a2a42015-10-27 01:32:00 -070044// Used by histograms. Values of entries should not be changed.
45enum HistogramCodecType {
46 kVideoUnknown = 0,
47 kVideoVp8 = 1,
48 kVideoVp9 = 2,
49 kVideoH264 = 3,
50 kVideoMax = 64,
51};
52
asaperssonc2148a52016-02-04 00:33:21 -080053const char* kRealtimePrefix = "WebRTC.Video.";
54const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
55
sprangb4a1ae52015-12-03 08:10:08 -080056const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
57 switch (content_type) {
58 case VideoEncoderConfig::ContentType::kRealtimeVideo:
asaperssonc2148a52016-02-04 00:33:21 -080059 return kRealtimePrefix;
sprangb4a1ae52015-12-03 08:10:08 -080060 case VideoEncoderConfig::ContentType::kScreen:
asaperssonc2148a52016-02-04 00:33:21 -080061 return kScreenPrefix;
sprangb4a1ae52015-12-03 08:10:08 -080062 }
63 RTC_NOTREACHED();
64 return nullptr;
65}
66
asapersson2a0a2a42015-10-27 01:32:00 -070067HistogramCodecType PayloadNameToHistogramCodecType(
68 const std::string& payload_name) {
kthelgason1cdddc92017-08-24 03:52:48 -070069 VideoCodecType codecType = PayloadStringToCodecType(payload_name);
70 switch (codecType) {
deadbeefc964d0b2017-04-03 10:03:35 -070071 case kVideoCodecVP8:
72 return kVideoVp8;
73 case kVideoCodecVP9:
74 return kVideoVp9;
75 case kVideoCodecH264:
76 return kVideoH264;
77 default:
78 return kVideoUnknown;
79 }
asapersson2a0a2a42015-10-27 01:32:00 -070080}
81
82void UpdateCodecTypeHistogram(const std::string& payload_name) {
asapersson28ba9272016-01-25 05:58:23 -080083 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
84 PayloadNameToHistogramCodecType(payload_name),
85 kVideoMax);
asapersson2a0a2a42015-10-27 01:32:00 -070086}
asapersson8d75ac72017-09-15 06:41:15 -070087
Niels Möllerd3b8c632018-08-27 15:33:42 +020088bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info,
89 int simulcast_index) {
90 return codec_info->codecType == kVideoCodecVP8 && simulcast_index == 0 &&
asapersson8d75ac72017-09-15 06:41:15 -070091 (codec_info->codecSpecific.VP8.temporalIdx == 0 ||
92 codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx);
93}
94
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020095absl::optional<int> GetFallbackMaxPixels(const std::string& group) {
asapersson8d75ac72017-09-15 06:41:15 -070096 if (group.empty())
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020097 return absl::nullopt;
asapersson8d75ac72017-09-15 06:41:15 -070098
asapersson8d75ac72017-09-15 06:41:15 -070099 int min_pixels;
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100100 int max_pixels;
101 int min_bps;
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100102 if (sscanf(group.c_str(), "-%d,%d,%d", &min_pixels, &max_pixels, &min_bps) !=
103 3) {
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200104 return absl::optional<int>();
asapersson8d75ac72017-09-15 06:41:15 -0700105 }
106
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100107 if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels)
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200108 return absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100109
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200110 return absl::optional<int>(max_pixels);
asapersson8d75ac72017-09-15 06:41:15 -0700111}
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100112
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200113absl::optional<int> GetFallbackMaxPixelsIfFieldTrialEnabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100114 std::string group =
115 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
116 return (group.find("Enabled") == 0) ? GetFallbackMaxPixels(group.substr(7))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200117 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100118}
119
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200120absl::optional<int> GetFallbackMaxPixelsIfFieldTrialDisabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100121 std::string group =
122 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
123 return (group.find("Disabled") == 0) ? GetFallbackMaxPixels(group.substr(8))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200124 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100125}
asapersson2a0a2a42015-10-27 01:32:00 -0700126} // namespace
127
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000128const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
129
sprangb4a1ae52015-12-03 08:10:08 -0800130SendStatisticsProxy::SendStatisticsProxy(
131 Clock* clock,
132 const VideoSendStream::Config& config,
133 VideoEncoderConfig::ContentType content_type)
asaperssond89920b2015-07-22 06:52:00 -0700134 : clock_(clock),
Niels Möller259a4972018-04-05 15:36:51 +0200135 payload_name_(config.rtp.payload_name),
perkj26091b12016-09-01 01:17:40 -0700136 rtp_config_(config.rtp),
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100137 fallback_max_pixels_(GetFallbackMaxPixelsIfFieldTrialEnabled()),
138 fallback_max_pixels_disabled_(GetFallbackMaxPixelsIfFieldTrialDisabled()),
sprangb4a1ae52015-12-03 08:10:08 -0800139 content_type_(content_type),
asapersson4374a092016-07-27 00:39:09 -0700140 start_ms_(clock->TimeInMilliseconds()),
asapersson1aa420b2015-12-07 03:12:22 -0800141 encode_time_(kEncodeTimeWeigthFactor),
asapersson0944a802017-04-07 00:57:58 -0700142 quality_downscales_(-1),
143 cpu_downscales_(-1),
Henrik Boströmce33b6a2019-05-28 17:42:38 +0200144 quality_limitation_reason_tracker_(clock_),
Åsa Persson0122e842017-10-16 12:19:23 +0200145 media_byte_rate_tracker_(kBucketSizeMs, kBucketCount),
146 encoded_frame_rate_tracker_(kBucketSizeMs, kBucketCount),
Evan Shrubsolecc62b162019-09-09 11:26:45 +0200147 last_num_spatial_layers_(0),
148 last_num_simulcast_streams_(0),
149 last_spatial_layer_use_{},
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +0200150 bw_limited_layers_(false),
sprang07fb9be2016-02-24 07:55:00 -0800151 uma_container_(
152 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000153}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000154
sprang07fb9be2016-02-24 07:55:00 -0800155SendStatisticsProxy::~SendStatisticsProxy() {
156 rtc::CritScope lock(&crit_);
perkj26091b12016-09-01 01:17:40 -0700157 uma_container_->UpdateHistograms(rtp_config_, stats_);
asapersson4374a092016-07-27 00:39:09 -0700158
159 int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
asapersson1d02d3e2016-09-09 22:40:25 -0700160 RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
161 elapsed_sec);
asapersson4374a092016-07-27 00:39:09 -0700162
163 if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
perkj26091b12016-09-01 01:17:40 -0700164 UpdateCodecTypeHistogram(payload_name_);
sprang07fb9be2016-02-24 07:55:00 -0800165}
sprangb4a1ae52015-12-03 08:10:08 -0800166
Mirko Bonadei8fdcac32018-08-28 16:30:18 +0200167SendStatisticsProxy::FallbackEncoderInfo::FallbackEncoderInfo() = default;
168
sprangb4a1ae52015-12-03 08:10:08 -0800169SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
sprang07fb9be2016-02-24 07:55:00 -0800170 const char* prefix,
171 const VideoSendStream::Stats& stats,
172 Clock* const clock)
sprangb4a1ae52015-12-03 08:10:08 -0800173 : uma_prefix_(prefix),
sprang07fb9be2016-02-24 07:55:00 -0800174 clock_(clock),
Honghai Zhang82d78622016-05-06 11:29:15 -0700175 input_frame_rate_tracker_(100, 10u),
asapersson320e45a2016-11-29 01:40:35 -0800176 input_fps_counter_(clock, nullptr, true),
177 sent_fps_counter_(clock, nullptr, true),
asapersson93e1e232017-02-06 05:18:35 -0800178 total_byte_counter_(clock, nullptr, true),
179 media_byte_counter_(clock, nullptr, true),
180 rtx_byte_counter_(clock, nullptr, true),
181 padding_byte_counter_(clock, nullptr, true),
182 retransmit_byte_counter_(clock, nullptr, true),
183 fec_byte_counter_(clock, nullptr, true),
sprang07fb9be2016-02-24 07:55:00 -0800184 first_rtcp_stats_time_ms_(-1),
Erik Språng22c2b482016-03-01 09:40:42 +0100185 first_rtp_stats_time_ms_(-1),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100186 start_stats_(stats),
187 num_streams_(0),
188 num_pixels_highest_stream_(0) {
asapersson93e1e232017-02-06 05:18:35 -0800189 InitializeBitrateCounters(stats);
Åsa Persson20317f92018-08-15 08:57:54 +0200190 static_assert(
191 kMaxEncodedFrameTimestampDiff < std::numeric_limits<uint32_t>::max() / 2,
192 "has to be smaller than half range");
asapersson93e1e232017-02-06 05:18:35 -0800193}
sprangb4a1ae52015-12-03 08:10:08 -0800194
sprang07fb9be2016-02-24 07:55:00 -0800195SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
Åsa Persson24b4eda2015-06-16 10:17:01 +0200196
asapersson93e1e232017-02-06 05:18:35 -0800197void SendStatisticsProxy::UmaSamplesContainer::InitializeBitrateCounters(
198 const VideoSendStream::Stats& stats) {
199 for (const auto& it : stats.substreams) {
200 uint32_t ssrc = it.first;
201 total_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
202 ssrc);
203 padding_byte_counter_.SetLast(it.second.rtp_stats.transmitted.padding_bytes,
204 ssrc);
205 retransmit_byte_counter_.SetLast(
206 it.second.rtp_stats.retransmitted.TotalBytes(), ssrc);
207 fec_byte_counter_.SetLast(it.second.rtp_stats.fec.TotalBytes(), ssrc);
208 if (it.second.is_rtx) {
209 rtx_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
210 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100211 } else {
asapersson93e1e232017-02-06 05:18:35 -0800212 media_byte_counter_.SetLast(it.second.rtp_stats.MediaPayloadBytes(),
213 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100214 }
215 }
216}
217
Ilya Nikolaevskiy840394c2019-11-26 17:20:50 +0100218void SendStatisticsProxy::UmaSamplesContainer::RemoveOld(int64_t now_ms) {
Åsa Persson0122e842017-10-16 12:19:23 +0200219 while (!encoded_frames_.empty()) {
220 auto it = encoded_frames_.begin();
221 if (now_ms - it->second.send_ms < kMaxEncodedFrameWindowMs)
222 break;
223
224 // Use max per timestamp.
225 sent_width_counter_.Add(it->second.max_width);
226 sent_height_counter_.Add(it->second.max_height);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100227
228 // Check number of encoded streams per timestamp.
Niels Möllerd3b8c632018-08-27 15:33:42 +0200229 if (num_streams_ > static_cast<size_t>(it->second.max_simulcast_idx)) {
Åsa Perssonaa329e72017-12-15 15:54:44 +0100230 if (num_streams_ > 1) {
231 int disabled_streams =
232 static_cast<int>(num_streams_ - 1 - it->second.max_simulcast_idx);
233 // Can be limited in resolution or framerate.
234 uint32_t pixels = it->second.max_width * it->second.max_height;
235 bool bw_limited_resolution =
236 disabled_streams > 0 && pixels < num_pixels_highest_stream_;
237 bw_limited_frame_counter_.Add(bw_limited_resolution);
238 if (bw_limited_resolution) {
239 bw_resolutions_disabled_counter_.Add(disabled_streams);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100240 }
241 }
242 }
Åsa Persson0122e842017-10-16 12:19:23 +0200243 encoded_frames_.erase(it);
244 }
245}
246
247bool SendStatisticsProxy::UmaSamplesContainer::InsertEncodedFrame(
Åsa Perssonaa329e72017-12-15 15:54:44 +0100248 const EncodedImage& encoded_frame,
Ilya Nikolaevskiy840394c2019-11-26 17:20:50 +0100249 int simulcast_idx) {
Åsa Persson0122e842017-10-16 12:19:23 +0200250 int64_t now_ms = clock_->TimeInMilliseconds();
Ilya Nikolaevskiy840394c2019-11-26 17:20:50 +0100251 RemoveOld(now_ms);
Åsa Persson0122e842017-10-16 12:19:23 +0200252 if (encoded_frames_.size() > kMaxEncodedFrameMapSize) {
253 encoded_frames_.clear();
254 }
255
Åsa Persson20317f92018-08-15 08:57:54 +0200256 // Check for jump in timestamp.
257 if (!encoded_frames_.empty()) {
258 uint32_t oldest_timestamp = encoded_frames_.begin()->first;
Niels Möller23775882018-08-16 10:24:12 +0200259 if (ForwardDiff(oldest_timestamp, encoded_frame.Timestamp()) >
Åsa Persson20317f92018-08-15 08:57:54 +0200260 kMaxEncodedFrameTimestampDiff) {
261 // Gap detected, clear frames to have a sequence where newest timestamp
262 // is not too far away from oldest in order to distinguish old and new.
263 encoded_frames_.clear();
264 }
265 }
266
Niels Möller72bc8d62018-09-12 10:03:51 +0200267 auto it = encoded_frames_.find(encoded_frame.Timestamp());
Åsa Persson0122e842017-10-16 12:19:23 +0200268 if (it == encoded_frames_.end()) {
269 // First frame with this timestamp.
Åsa Perssonaa329e72017-12-15 15:54:44 +0100270 encoded_frames_.insert(
Niels Möller23775882018-08-16 10:24:12 +0200271 std::make_pair(encoded_frame.Timestamp(),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100272 Frame(now_ms, encoded_frame._encodedWidth,
273 encoded_frame._encodedHeight, simulcast_idx)));
Åsa Persson0122e842017-10-16 12:19:23 +0200274 sent_fps_counter_.Add(1);
275 return true;
276 }
277
278 it->second.max_width =
279 std::max(it->second.max_width, encoded_frame._encodedWidth);
280 it->second.max_height =
281 std::max(it->second.max_height, encoded_frame._encodedHeight);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100282 it->second.max_simulcast_idx =
283 std::max(it->second.max_simulcast_idx, simulcast_idx);
Åsa Persson0122e842017-10-16 12:19:23 +0200284 return false;
285}
286
sprang07fb9be2016-02-24 07:55:00 -0800287void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
Stefan Holmerdbdb3a02018-07-17 16:03:46 +0200288 const RtpConfig& rtp_config,
sprang07fb9be2016-02-24 07:55:00 -0800289 const VideoSendStream::Stats& current_stats) {
asaperssonc2148a52016-02-04 00:33:21 -0800290 RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
291 const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
asapersson320e45a2016-11-29 01:40:35 -0800292 const int kMinRequiredPeriodicSamples = 6;
Karl Wiberg881f1682018-03-08 15:03:23 +0100293 char log_stream_buf[8 * 1024];
294 rtc::SimpleStringBuilder log_stream(log_stream_buf);
perkj803d97f2016-11-01 11:45:46 -0700295 int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
296 int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700297 if (in_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700298 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
299 in_width);
300 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
301 in_height);
Tommifef05002018-02-27 13:51:08 +0100302 log_stream << uma_prefix_ << "InputWidthInPixels " << in_width << "\n"
303 << uma_prefix_ << "InputHeightInPixels " << in_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700304 }
asapersson320e45a2016-11-29 01:40:35 -0800305 AggregatedStats in_fps = input_fps_counter_.GetStats();
306 if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
307 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
308 in_fps.average);
Tommifef05002018-02-27 13:51:08 +0100309 log_stream << uma_prefix_ << "InputFramesPerSecond " << in_fps.ToString()
310 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800311 }
312
perkj803d97f2016-11-01 11:45:46 -0700313 int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
314 int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700315 if (sent_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700316 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
317 sent_width);
318 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
319 sent_height);
Tommifef05002018-02-27 13:51:08 +0100320 log_stream << uma_prefix_ << "SentWidthInPixels " << sent_width << "\n"
321 << uma_prefix_ << "SentHeightInPixels " << sent_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700322 }
asapersson320e45a2016-11-29 01:40:35 -0800323 AggregatedStats sent_fps = sent_fps_counter_.GetStats();
324 if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
325 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
326 sent_fps.average);
Tommifef05002018-02-27 13:51:08 +0100327 log_stream << uma_prefix_ << "SentFramesPerSecond " << sent_fps.ToString()
328 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800329 }
330
ilnikf4ded682017-08-30 02:30:20 -0700331 if (in_fps.num_samples > kMinRequiredPeriodicSamples &&
332 sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
333 int in_fps_avg = in_fps.average;
334 if (in_fps_avg > 0) {
335 int sent_fps_avg = sent_fps.average;
336 int sent_to_in_fps_ratio_percent =
337 (100 * sent_fps_avg + in_fps_avg / 2) / in_fps_avg;
338 // If reported period is small, it may happen that sent_fps is larger than
339 // input_fps briefly on average. This should be treated as 100% sent to
340 // input ratio.
341 if (sent_to_in_fps_ratio_percent > 100)
342 sent_to_in_fps_ratio_percent = 100;
343 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
344 uma_prefix_ + "SentToInputFpsRatioPercent",
345 sent_to_in_fps_ratio_percent);
Tommifef05002018-02-27 13:51:08 +0100346 log_stream << uma_prefix_ << "SentToInputFpsRatioPercent "
347 << sent_to_in_fps_ratio_percent << "\n";
ilnikf4ded682017-08-30 02:30:20 -0700348 }
349 }
350
perkj803d97f2016-11-01 11:45:46 -0700351 int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonc2148a52016-02-04 00:33:21 -0800352 if (encode_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700353 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
354 encode_ms);
Tommifef05002018-02-27 13:51:08 +0100355 log_stream << uma_prefix_ << "EncodeTimeInMs " << encode_ms << "\n";
asaperssonc2148a52016-02-04 00:33:21 -0800356 }
perkj803d97f2016-11-01 11:45:46 -0700357 int key_frames_permille =
358 key_frame_counter_.Permille(kMinRequiredMetricsSamples);
asaperssondec5ebf2015-10-05 02:36:17 -0700359 if (key_frames_permille != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700360 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
361 key_frames_permille);
Tommifef05002018-02-27 13:51:08 +0100362 log_stream << uma_prefix_ << "KeyFramesSentInPermille "
363 << key_frames_permille << "\n";
asaperssondec5ebf2015-10-05 02:36:17 -0700364 }
asapersson4306fc72015-10-19 00:35:21 -0700365 int quality_limited =
perkj803d97f2016-11-01 11:45:46 -0700366 quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700367 if (quality_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700368 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
369 uma_prefix_ + "QualityLimitedResolutionInPercent",
370 quality_limited);
Tommifef05002018-02-27 13:51:08 +0100371 log_stream << uma_prefix_ << "QualityLimitedResolutionInPercent "
372 << quality_limited << "\n";
asapersson4306fc72015-10-19 00:35:21 -0700373 }
perkj803d97f2016-11-01 11:45:46 -0700374 int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700375 if (downscales != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700376 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800377 kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
378 20);
asapersson4306fc72015-10-19 00:35:21 -0700379 }
perkj803d97f2016-11-01 11:45:46 -0700380 int cpu_limited =
381 cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
382 if (cpu_limited != -1) {
383 RTC_HISTOGRAMS_PERCENTAGE(
384 kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
385 }
386 int bw_limited =
387 bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700388 if (bw_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700389 RTC_HISTOGRAMS_PERCENTAGE(
asaperssonc2148a52016-02-04 00:33:21 -0800390 kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
391 bw_limited);
asaperssonda535c42015-10-19 23:32:41 -0700392 }
perkj803d97f2016-11-01 11:45:46 -0700393 int num_disabled =
394 bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700395 if (num_disabled != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700396 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800397 kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
398 num_disabled, 10);
asaperssonda535c42015-10-19 23:32:41 -0700399 }
perkj803d97f2016-11-01 11:45:46 -0700400 int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800401 if (delay_ms != -1)
asapersson1d02d3e2016-09-09 22:40:25 -0700402 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
403 delay_ms);
asaperssonf040b232015-11-04 00:59:03 -0800404
perkj803d97f2016-11-01 11:45:46 -0700405 int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800406 if (max_delay_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700407 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
408 max_delay_ms);
sprangb4a1ae52015-12-03 08:10:08 -0800409 }
sprang07fb9be2016-02-24 07:55:00 -0800410
asapersson118ef002016-03-31 00:00:19 -0700411 for (const auto& it : qp_counters_) {
perkj803d97f2016-11-01 11:45:46 -0700412 int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700413 if (qp_vp8 != -1) {
asapersson118ef002016-03-31 00:00:19 -0700414 int spatial_idx = it.first;
415 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700416 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
417 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700418 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700419 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
420 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700421 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700422 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
423 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700424 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700425 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
426 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700427 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100428 RTC_LOG(LS_WARNING)
429 << "QP stats not recorded for VP8 spatial idx " << spatial_idx;
asapersson118ef002016-03-31 00:00:19 -0700430 }
431 }
perkj803d97f2016-11-01 11:45:46 -0700432 int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700433 if (qp_vp9 != -1) {
434 int spatial_idx = it.first;
435 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700436 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
437 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700438 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700439 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
440 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700441 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700442 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
443 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700444 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700445 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
446 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700447 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100448 RTC_LOG(LS_WARNING)
449 << "QP stats not recorded for VP9 spatial layer " << spatial_idx;
asapersson5265fed2016-04-18 02:58:47 -0700450 }
451 }
asapersson827cab32016-11-02 09:08:47 -0700452 int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
453 if (qp_h264 != -1) {
454 int spatial_idx = it.first;
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200455 if (spatial_idx == -1) {
456 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264",
457 qp_h264);
458 } else if (spatial_idx == 0) {
459 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S0",
460 qp_h264);
461 } else if (spatial_idx == 1) {
462 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S1",
463 qp_h264);
464 } else if (spatial_idx == 2) {
465 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S2",
466 qp_h264);
467 } else {
468 RTC_LOG(LS_WARNING)
469 << "QP stats not recorded for H264 spatial idx " << spatial_idx;
470 }
asapersson827cab32016-11-02 09:08:47 -0700471 }
asapersson118ef002016-03-31 00:00:19 -0700472 }
473
asapersson0944a802017-04-07 00:57:58 -0700474 if (first_rtp_stats_time_ms_ != -1) {
asapersson09f05612017-05-15 23:40:18 -0700475 quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
476 int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700477 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
478 int quality_changes = current_stats.number_of_quality_adapt_changes -
479 start_stats_.number_of_quality_adapt_changes;
Åsa Persson875841d2018-01-08 08:49:53 +0100480 // Only base stats on changes during a call, discard initial changes.
481 int initial_changes =
482 initial_quality_changes_.down + initial_quality_changes_.up;
483 if (initial_changes <= quality_changes)
484 quality_changes -= initial_changes;
asapersson0944a802017-04-07 00:57:58 -0700485 RTC_HISTOGRAMS_COUNTS_100(kIndex,
486 uma_prefix_ + "AdaptChangesPerMinute.Quality",
487 quality_changes * 60 / elapsed_sec);
488 }
asapersson09f05612017-05-15 23:40:18 -0700489 cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
490 elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700491 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
492 int cpu_changes = current_stats.number_of_cpu_adapt_changes -
493 start_stats_.number_of_cpu_adapt_changes;
494 RTC_HISTOGRAMS_COUNTS_100(kIndex,
495 uma_prefix_ + "AdaptChangesPerMinute.Cpu",
496 cpu_changes * 60 / elapsed_sec);
497 }
asapersson6eca98b2017-04-04 23:40:50 -0700498 }
499
sprange2d83d62016-02-19 09:03:26 -0800500 if (first_rtcp_stats_time_ms_ != -1) {
sprang07fb9be2016-02-24 07:55:00 -0800501 int64_t elapsed_sec =
502 (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
503 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
504 int fraction_lost = report_block_stats_.FractionLostInPercent();
505 if (fraction_lost != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700506 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800507 kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
Tommifef05002018-02-27 13:51:08 +0100508 log_stream << uma_prefix_ << "SentPacketsLostInPercent "
Stefan Holmer0a5792e2018-10-05 13:47:12 +0200509 << fraction_lost << "\n";
sprang07fb9be2016-02-24 07:55:00 -0800510 }
511
512 // The RTCP packet type counters, delivered via the
513 // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
514 // life of the send stream and are not reset when switching content type.
515 // For the purpose of these statistics though, we want new counts when
516 // switching since we switch histogram name. On every reset of the
517 // UmaSamplesContainer, we save the initial state of the counters, so that
518 // we can calculate the delta here and aggregate over all ssrcs.
519 RtcpPacketTypeCounter counters;
perkj26091b12016-09-01 01:17:40 -0700520 for (uint32_t ssrc : rtp_config.ssrcs) {
sprang07fb9be2016-02-24 07:55:00 -0800521 auto kv = current_stats.substreams.find(ssrc);
522 if (kv == current_stats.substreams.end())
523 continue;
524
525 RtcpPacketTypeCounter stream_counters =
526 kv->second.rtcp_packet_type_counts;
527 kv = start_stats_.substreams.find(ssrc);
528 if (kv != start_stats_.substreams.end())
529 stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
530
531 counters.Add(stream_counters);
532 }
asapersson1d02d3e2016-09-09 22:40:25 -0700533 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
534 uma_prefix_ + "NackPacketsReceivedPerMinute",
535 counters.nack_packets * 60 / elapsed_sec);
536 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
537 uma_prefix_ + "FirPacketsReceivedPerMinute",
538 counters.fir_packets * 60 / elapsed_sec);
539 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
540 uma_prefix_ + "PliPacketsReceivedPerMinute",
541 counters.pli_packets * 60 / elapsed_sec);
sprang07fb9be2016-02-24 07:55:00 -0800542 if (counters.nack_requests > 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700543 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800544 kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
545 counters.UniqueNackRequestsInPercent());
546 }
sprange2d83d62016-02-19 09:03:26 -0800547 }
548 }
Erik Språng22c2b482016-03-01 09:40:42 +0100549
550 if (first_rtp_stats_time_ms_ != -1) {
551 int64_t elapsed_sec =
552 (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
553 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
asapersson66d4b372016-12-19 06:50:53 -0800554 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
555 target_rate_updates_.pause_resume_events);
Tommifef05002018-02-27 13:51:08 +0100556 log_stream << uma_prefix_ << "NumberOfPauseEvents "
557 << target_rate_updates_.pause_resume_events << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800558
559 int paused_time_percent =
560 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
561 if (paused_time_percent != -1) {
562 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
563 paused_time_percent);
Tommifef05002018-02-27 13:51:08 +0100564 log_stream << uma_prefix_ << "PausedTimeInPercent "
565 << paused_time_percent << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800566 }
asapersson93e1e232017-02-06 05:18:35 -0800567 }
568 }
asapersson66d4b372016-12-19 06:50:53 -0800569
asapersson8d75ac72017-09-15 06:41:15 -0700570 if (fallback_info_.is_possible) {
571 // Double interval since there is some time before fallback may occur.
572 const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000;
573 int64_t elapsed_ms = fallback_info_.elapsed_ms;
574 int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs);
575 if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) {
576 RTC_HISTOGRAMS_PERCENTAGE(
577 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8",
578 fallback_time_percent);
579 RTC_HISTOGRAMS_COUNTS_100(
580 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8",
581 fallback_info_.on_off_events * 60 / (elapsed_ms / 1000));
582 }
583 }
584
asapersson93e1e232017-02-06 05:18:35 -0800585 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats();
586 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
587 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps",
588 total_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100589 log_stream << uma_prefix_ << "BitrateSentInBps "
590 << total_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800591 }
592 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats();
593 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
594 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
595 media_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100596 log_stream << uma_prefix_ << "MediaBitrateSentInBps "
597 << media_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800598 }
599 AggregatedStats padding_bytes_per_sec = padding_byte_counter_.GetStats();
600 if (padding_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
601 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
602 uma_prefix_ + "PaddingBitrateSentInKbps",
603 padding_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100604 log_stream << uma_prefix_ << "PaddingBitrateSentInBps "
605 << padding_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800606 }
607 AggregatedStats retransmit_bytes_per_sec =
608 retransmit_byte_counter_.GetStats();
609 if (retransmit_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
610 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
611 uma_prefix_ + "RetransmittedBitrateSentInKbps",
612 retransmit_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100613 log_stream << uma_prefix_ << "RetransmittedBitrateSentInBps "
614 << retransmit_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800615 }
616 if (!rtp_config.rtx.ssrcs.empty()) {
617 AggregatedStats rtx_bytes_per_sec = rtx_byte_counter_.GetStats();
618 int rtx_bytes_per_sec_avg = -1;
619 if (rtx_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
620 rtx_bytes_per_sec_avg = rtx_bytes_per_sec.average;
Tommifef05002018-02-27 13:51:08 +0100621 log_stream << uma_prefix_ << "RtxBitrateSentInBps "
622 << rtx_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800623 } else if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
624 rtx_bytes_per_sec_avg = 0; // RTX enabled but no RTX data sent, record 0.
625 }
626 if (rtx_bytes_per_sec_avg != -1) {
627 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
628 rtx_bytes_per_sec_avg * 8 / 1000);
629 }
630 }
631 if (rtp_config.flexfec.payload_type != -1 ||
632 rtp_config.ulpfec.red_payload_type != -1) {
633 AggregatedStats fec_bytes_per_sec = fec_byte_counter_.GetStats();
634 if (fec_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
635 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "FecBitrateSentInKbps",
636 fec_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100637 log_stream << uma_prefix_ << "FecBitrateSentInBps "
638 << fec_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
Erik Språng22c2b482016-03-01 09:40:42 +0100639 }
640 }
Tommifef05002018-02-27 13:51:08 +0100641 log_stream << "Frames encoded " << current_stats.frames_encoded << "\n"
642 << uma_prefix_ << "DroppedFrames.Capturer "
643 << current_stats.frames_dropped_by_capturer << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200644 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
645 current_stats.frames_dropped_by_capturer);
Tommifef05002018-02-27 13:51:08 +0100646 log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
647 << current_stats.frames_dropped_by_encoder_queue << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200648 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
649 current_stats.frames_dropped_by_encoder_queue);
Tommifef05002018-02-27 13:51:08 +0100650 log_stream << uma_prefix_ << "DroppedFrames.Encoder "
651 << current_stats.frames_dropped_by_encoder << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200652 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Encoder",
653 current_stats.frames_dropped_by_encoder);
Tommifef05002018-02-27 13:51:08 +0100654 log_stream << uma_prefix_ << "DroppedFrames.Ratelimiter "
655 << current_stats.frames_dropped_by_rate_limiter;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200656 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Ratelimiter",
657 current_stats.frames_dropped_by_rate_limiter);
Jonas Olsson694a36f2018-02-16 13:09:41 +0100658
Tommifef05002018-02-27 13:51:08 +0100659 RTC_LOG(LS_INFO) << log_stream.str();
sprangb4a1ae52015-12-03 08:10:08 -0800660}
661
Pera48ddb72016-09-29 11:48:50 +0200662void SendStatisticsProxy::OnEncoderReconfigured(
663 const VideoEncoderConfig& config,
Niels Möller97e04882018-05-25 09:43:26 +0200664 const std::vector<VideoStream>& streams) {
sprangb4a1ae52015-12-03 08:10:08 -0800665 rtc::CritScope lock(&crit_);
Pera48ddb72016-09-29 11:48:50 +0200666
667 if (content_type_ != config.content_type) {
perkj26091b12016-09-01 01:17:40 -0700668 uma_container_->UpdateHistograms(rtp_config_, stats_);
Pera48ddb72016-09-29 11:48:50 +0200669 uma_container_.reset(new UmaSamplesContainer(
670 GetUmaPrefix(config.content_type), stats_, clock_));
671 content_type_ = config.content_type;
asaperssonf040b232015-11-04 00:59:03 -0800672 }
Åsa Perssonaa329e72017-12-15 15:54:44 +0100673 uma_container_->encoded_frames_.clear();
674 uma_container_->num_streams_ = streams.size();
675 uma_container_->num_pixels_highest_stream_ =
676 streams.empty() ? 0 : (streams.back().width * streams.back().height);
Åsa Persson24b4eda2015-06-16 10:17:01 +0200677}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000678
Niels Möller213618e2018-07-24 09:29:58 +0200679void SendStatisticsProxy::OnEncodedFrameTimeMeasured(int encode_time_ms,
680 int encode_usage_percent) {
Henrik Boström5684af52019-04-02 15:05:21 +0200681 RTC_DCHECK_GE(encode_time_ms, 0);
Peter Boströmf2f82832015-05-01 13:00:41 +0200682 rtc::CritScope lock(&crit_);
Peter Boströme4499152016-02-05 11:13:28 +0100683 uma_container_->encode_time_counter_.Add(encode_time_ms);
684 encode_time_.Apply(1.0f, encode_time_ms);
Oleh Prypin19929582019-04-23 08:50:04 +0200685 stats_.avg_encode_time_ms = std::round(encode_time_.filtered());
Henrik Boström5684af52019-04-02 15:05:21 +0200686 stats_.total_encode_time_ms += encode_time_ms;
Niels Möller213618e2018-07-24 09:29:58 +0200687 stats_.encode_usage_percent = encode_usage_percent;
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000688}
689
Peter Boström7083e112015-09-22 16:28:51 +0200690void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
asapersson0944a802017-04-07 00:57:58 -0700691 int64_t now_ms = clock_->TimeInMilliseconds();
Peter Boströmf2f82832015-05-01 13:00:41 +0200692 rtc::CritScope lock(&crit_);
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000693 stats_.suspended = is_suspended;
asapersson320e45a2016-11-29 01:40:35 -0800694 if (is_suspended) {
asapersson93e1e232017-02-06 05:18:35 -0800695 // Pause framerate (add min pause time since there may be frames/packets
696 // that are not yet sent).
697 const int64_t kMinMs = 500;
698 uma_container_->input_fps_counter_.ProcessAndPauseForDuration(kMinMs);
699 uma_container_->sent_fps_counter_.ProcessAndPauseForDuration(kMinMs);
700 // Pause bitrate stats.
701 uma_container_->total_byte_counter_.ProcessAndPauseForDuration(kMinMs);
702 uma_container_->media_byte_counter_.ProcessAndPauseForDuration(kMinMs);
703 uma_container_->rtx_byte_counter_.ProcessAndPauseForDuration(kMinMs);
704 uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
705 uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
706 uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
asapersson0944a802017-04-07 00:57:58 -0700707 // Stop adaptation stats.
asapersson09f05612017-05-15 23:40:18 -0700708 uma_container_->cpu_adapt_timer_.Stop(now_ms);
709 uma_container_->quality_adapt_timer_.Stop(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800710 } else {
asapersson0944a802017-04-07 00:57:58 -0700711 // Start adaptation stats if scaling is enabled.
712 if (cpu_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700713 uma_container_->cpu_adapt_timer_.Start(now_ms);
asapersson0944a802017-04-07 00:57:58 -0700714 if (quality_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700715 uma_container_->quality_adapt_timer_.Start(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800716 // Stop pause explicitly for stats that may be zero/not updated for some
717 // time.
718 uma_container_->rtx_byte_counter_.ProcessAndStopPause();
719 uma_container_->padding_byte_counter_.ProcessAndStopPause();
720 uma_container_->retransmit_byte_counter_.ProcessAndStopPause();
721 uma_container_->fec_byte_counter_.ProcessAndStopPause();
asapersson320e45a2016-11-29 01:40:35 -0800722 }
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000723}
724
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000725VideoSendStream::Stats SendStatisticsProxy::GetStats() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200726 rtc::CritScope lock(&crit_);
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000727 PurgeOldStats();
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000728 stats_.input_frame_rate =
sprangb4a1ae52015-12-03 08:10:08 -0800729 round(uma_container_->input_frame_rate_tracker_.ComputeRate());
ilnik50864a82017-09-06 12:32:35 -0700730 stats_.content_type =
731 content_type_ == VideoEncoderConfig::ContentType::kRealtimeVideo
732 ? VideoContentType::UNSPECIFIED
733 : VideoContentType::SCREENSHARE;
Åsa Persson0122e842017-10-16 12:19:23 +0200734 stats_.encode_frame_rate = round(encoded_frame_rate_tracker_.ComputeRate());
735 stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
Henrik Boströmce33b6a2019-05-28 17:42:38 +0200736 stats_.quality_limitation_durations_ms =
737 quality_limitation_reason_tracker_.DurationsMs();
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000738 return stats_;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000739}
740
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000741void SendStatisticsProxy::PurgeOldStats() {
Peter Boström20f3f942015-05-15 11:33:39 +0200742 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000743 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
744 stats_.substreams.begin();
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000745 it != stats_.substreams.end(); ++it) {
746 uint32_t ssrc = it->first;
Peter Boström20f3f942015-05-15 11:33:39 +0200747 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
748 it->second.width = 0;
749 it->second.height = 0;
750 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000751 }
752}
753
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000754VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
755 uint32_t ssrc) {
756 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
757 stats_.substreams.find(ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000758 if (it != stats_.substreams.end())
759 return &it->second;
760
Steve Antonbd631a02019-03-28 10:51:27 -0700761 bool is_media = absl::c_linear_search(rtp_config_.ssrcs, ssrc);
brandtr3d200bd2017-01-16 06:59:19 -0800762 bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
763 ssrc == rtp_config_.flexfec.ssrc;
Steve Antonbd631a02019-03-28 10:51:27 -0700764 bool is_rtx = absl::c_linear_search(rtp_config_.rtx.ssrcs, ssrc);
brandtrcd188f62016-11-15 08:21:52 -0800765 if (!is_media && !is_flexfec && !is_rtx)
766 return nullptr;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000767
asapersson2e5cfcd2016-08-11 08:41:18 -0700768 // Insert new entry and return ptr.
769 VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
770 entry->is_rtx = is_rtx;
asaperssona6a699a2016-11-25 03:52:46 -0800771 entry->is_flexfec = is_flexfec;
asapersson2e5cfcd2016-08-11 08:41:18 -0700772
773 return entry;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000774}
775
Peter Boström20f3f942015-05-15 11:33:39 +0200776void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
777 rtc::CritScope lock(&crit_);
778 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200779 if (!stats)
Peter Boström20f3f942015-05-15 11:33:39 +0200780 return;
781
782 stats->total_bitrate_bps = 0;
783 stats->retransmit_bitrate_bps = 0;
784 stats->height = 0;
785 stats->width = 0;
786}
787
perkjf5b2e512016-07-05 08:34:04 -0700788void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200789 rtc::CritScope lock(&crit_);
asapersson66d4b372016-12-19 06:50:53 -0800790 if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
791 return; // Start on first non-zero bitrate, may initially be zero.
792
793 int64_t now = clock_->TimeInMilliseconds();
794 if (uma_container_->target_rate_updates_.last_ms != -1) {
795 bool was_paused = stats_.target_media_bitrate_bps == 0;
796 int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
797 uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
798
799 // Use last to not include update when stream is stopped and video disabled.
800 if (uma_container_->target_rate_updates_.last_paused_or_resumed)
801 ++uma_container_->target_rate_updates_.pause_resume_events;
802
803 // Check if video is paused/resumed.
804 uma_container_->target_rate_updates_.last_paused_or_resumed =
805 (bitrate_bps == 0) != was_paused;
806 }
807 uma_container_->target_rate_updates_.last_ms = now;
808
pbos@webrtc.org891d4832015-02-26 13:15:22 +0000809 stats_.target_media_bitrate_bps = bitrate_bps;
810}
811
asapersson8d75ac72017-09-15 06:41:15 -0700812void SendStatisticsProxy::UpdateEncoderFallbackStats(
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100813 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200814 int pixels,
815 int simulcast_index) {
816 UpdateFallbackDisabledStats(codec_info, pixels, simulcast_index);
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100817
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100818 if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
asapersson8d75ac72017-09-15 06:41:15 -0700819 return;
820 }
821
Niels Möllerd3b8c632018-08-27 15:33:42 +0200822 if (!IsForcedFallbackPossible(codec_info, simulcast_index)) {
asapersson8d75ac72017-09-15 06:41:15 -0700823 uma_container_->fallback_info_.is_possible = false;
824 return;
825 }
826
827 FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_;
828
829 const int64_t now_ms = clock_->TimeInMilliseconds();
830 bool is_active = fallback_info->is_active;
Erik Språnge2fd86a2018-10-24 11:32:39 +0200831 if (encoder_changed_) {
asapersson8d75ac72017-09-15 06:41:15 -0700832 // Implementation changed.
Erik Språnge2fd86a2018-10-24 11:32:39 +0200833 const bool last_was_vp8_software =
834 encoder_changed_->previous_encoder_implementation == kVp8SwCodecName;
835 is_active = encoder_changed_->new_encoder_implementation == kVp8SwCodecName;
836 encoder_changed_.reset();
837 if (!is_active && !last_was_vp8_software) {
asapersson8d75ac72017-09-15 06:41:15 -0700838 // First or not a VP8 SW change, update stats on next call.
839 return;
840 }
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100841 if (is_active && (pixels > *fallback_max_pixels_)) {
842 // Pixels should not be above |fallback_max_pixels_|. If above skip to
843 // avoid fallbacks due to failure.
844 fallback_info->is_possible = false;
845 return;
asapersson8d75ac72017-09-15 06:41:15 -0700846 }
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100847 stats_.has_entered_low_resolution = true;
asapersson8d75ac72017-09-15 06:41:15 -0700848 ++fallback_info->on_off_events;
849 }
850
851 if (fallback_info->last_update_ms) {
852 int64_t diff_ms = now_ms - *(fallback_info->last_update_ms);
853 // If the time diff since last update is greater than |max_frame_diff_ms|,
854 // video is considered paused/muted and the change is not included.
855 if (diff_ms < fallback_info->max_frame_diff_ms) {
856 uma_container_->fallback_active_counter_.Add(fallback_info->is_active,
857 diff_ms);
858 fallback_info->elapsed_ms += diff_ms;
859 }
860 }
861 fallback_info->is_active = is_active;
862 fallback_info->last_update_ms.emplace(now_ms);
863}
864
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100865void SendStatisticsProxy::UpdateFallbackDisabledStats(
866 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200867 int pixels,
868 int simulcast_index) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100869 if (!fallback_max_pixels_disabled_ ||
870 !uma_container_->fallback_info_disabled_.is_possible ||
871 stats_.has_entered_low_resolution) {
872 return;
873 }
874
Niels Möllerd3b8c632018-08-27 15:33:42 +0200875 if (!IsForcedFallbackPossible(codec_info, simulcast_index) ||
Erik Språnge2fd86a2018-10-24 11:32:39 +0200876 stats_.encoder_implementation_name == kVp8SwCodecName) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100877 uma_container_->fallback_info_disabled_.is_possible = false;
878 return;
879 }
880
881 if (pixels <= *fallback_max_pixels_disabled_ ||
882 uma_container_->fallback_info_disabled_.min_pixel_limit_reached) {
883 stats_.has_entered_low_resolution = true;
884 }
885}
886
887void SendStatisticsProxy::OnMinPixelLimitReached() {
888 rtc::CritScope lock(&crit_);
889 uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true;
890}
891
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000892void SendStatisticsProxy::OnSendEncodedImage(
893 const EncodedImage& encoded_image,
kjellander02b3d272016-04-20 05:05:54 -0700894 const CodecSpecificInfo* codec_info) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200895 // Simulcast is used for VP8, H264 and Generic.
896 int simulcast_idx =
897 (codec_info && (codec_info->codecType == kVideoCodecVP8 ||
898 codec_info->codecType == kVideoCodecH264 ||
899 codec_info->codecType == kVideoCodecGeneric))
900 ? encoded_image.SpatialIndex().value_or(0)
901 : 0;
kjellander02b3d272016-04-20 05:05:54 -0700902
perkj275afc52016-09-01 00:21:16 -0700903 rtc::CritScope lock(&crit_);
sakal43536c32016-10-24 01:46:43 -0700904 ++stats_.frames_encoded;
Henrik Boström23aff9b2019-05-20 15:15:38 +0200905 // The current encode frame rate is based on previously encoded frames.
906 double encode_frame_rate = encoded_frame_rate_tracker_.ComputeRate();
907 // We assume that less than 1 FPS is not a trustworthy estimate - perhaps we
908 // just started encoding for the first time or after a pause. Assuming frame
909 // rate is at least 1 FPS is conservative to avoid too large increments.
910 if (encode_frame_rate < 1.0)
911 encode_frame_rate = 1.0;
912 double target_frame_size_bytes =
913 stats_.target_media_bitrate_bps / (8.0 * encode_frame_rate);
914 // |stats_.target_media_bitrate_bps| is set in
915 // SendStatisticsProxy::OnSetEncoderTargetRate.
916 stats_.total_encoded_bytes_target += round(target_frame_size_bytes);
kjellander02b3d272016-04-20 05:05:54 -0700917 if (codec_info) {
Erik Språnge2fd86a2018-10-24 11:32:39 +0200918 UpdateEncoderFallbackStats(
919 codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
920 simulcast_idx);
kjellander02b3d272016-04-20 05:05:54 -0700921 }
922
Niels Möllerd3b8c632018-08-27 15:33:42 +0200923 if (static_cast<size_t>(simulcast_idx) >= rtp_config_.ssrcs.size()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100924 RTC_LOG(LS_ERROR) << "Encoded image outside simulcast range ("
925 << simulcast_idx << " >= " << rtp_config_.ssrcs.size()
926 << ").";
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000927 return;
928 }
perkj26091b12016-09-01 01:17:40 -0700929 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000930
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000931 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200932 if (!stats)
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000933 return;
934
Sergey Silkinbb081a62018-09-04 18:34:22 +0200935 // Report resolution of top spatial layer in case of VP9 SVC.
936 bool is_svc_low_spatial_layer =
937 (codec_info && codec_info->codecType == kVideoCodecVP9)
938 ? !codec_info->codecSpecific.VP9.end_of_picture
939 : false;
940
941 if (!stats->width || !stats->height || !is_svc_low_spatial_layer) {
942 stats->width = encoded_image._encodedWidth;
943 stats->height = encoded_image._encodedHeight;
944 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
945 }
asaperssond89920b2015-07-22 06:52:00 -0700946
sprangb4a1ae52015-12-03 08:10:08 -0800947 uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
Niels Möller8f7ce222019-03-21 15:43:58 +0100948 VideoFrameType::kVideoFrameKey);
asapersson4306fc72015-10-19 00:35:21 -0700949
sakal87da4042016-10-31 06:53:47 -0700950 if (encoded_image.qp_ != -1) {
951 if (!stats_.qp_sum)
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100952 stats_.qp_sum = 0;
sakal87da4042016-10-31 06:53:47 -0700953 *stats_.qp_sum += encoded_image.qp_;
954
955 if (codec_info) {
956 if (codec_info->codecType == kVideoCodecVP8) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200957 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
sakal87da4042016-10-31 06:53:47 -0700958 uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
959 } else if (codec_info->codecType == kVideoCodecVP9) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200960 int spatial_idx = encoded_image.SpatialIndex().value_or(-1);
sakal87da4042016-10-31 06:53:47 -0700961 uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
asapersson827cab32016-11-02 09:08:47 -0700962 } else if (codec_info->codecType == kVideoCodecH264) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200963 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
asapersson827cab32016-11-02 09:08:47 -0700964 uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
sakal87da4042016-10-31 06:53:47 -0700965 }
asapersson5265fed2016-04-18 02:58:47 -0700966 }
asapersson118ef002016-03-31 00:00:19 -0700967 }
968
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100969 // If any of the simulcast streams have a huge frame, it should be counted
970 // as a single difficult input frame.
971 // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200972 if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) {
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100973 if (!last_outlier_timestamp_ ||
974 *last_outlier_timestamp_ < encoded_image.capture_time_ms_) {
975 last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_);
976 ++stats_.huge_frames_sent;
977 }
978 }
979
Niels Möller77536a22019-01-15 08:50:01 +0100980 media_byte_rate_tracker_.AddSamples(encoded_image.size());
Åsa Perssonaa329e72017-12-15 15:54:44 +0100981
Ilya Nikolaevskiy840394c2019-11-26 17:20:50 +0100982 if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx)) {
Åsa Persson0122e842017-10-16 12:19:23 +0200983 encoded_frame_rate_tracker_.AddSamples(1);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100984 }
985
Ilya Nikolaevskiy840394c2019-11-26 17:20:50 +0100986 stats_.bw_limited_resolution |= quality_downscales_ > 0;
Åsa Perssonaa329e72017-12-15 15:54:44 +0100987
988 if (quality_downscales_ != -1) {
989 uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
990 if (quality_downscales_ > 0)
991 uma_container_->quality_downscales_counter_.Add(quality_downscales_);
992 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000993}
994
Erik Språnge2fd86a2018-10-24 11:32:39 +0200995void SendStatisticsProxy::OnEncoderImplementationChanged(
996 const std::string& implementation_name) {
997 rtc::CritScope lock(&crit_);
998 encoder_changed_ = EncoderChangeEvent{stats_.encoder_implementation_name,
999 implementation_name};
1000 stats_.encoder_implementation_name = implementation_name;
1001}
1002
Niels Möller213618e2018-07-24 09:29:58 +02001003int SendStatisticsProxy::GetInputFrameRate() const {
1004 rtc::CritScope lock(&crit_);
1005 return round(uma_container_->input_frame_rate_tracker_.ComputeRate());
1006}
1007
Per69b332d2016-06-02 15:45:42 +02001008int SendStatisticsProxy::GetSendFrameRate() const {
1009 rtc::CritScope lock(&crit_);
Åsa Persson0122e842017-10-16 12:19:23 +02001010 return round(encoded_frame_rate_tracker_.ComputeRate());
Per69b332d2016-06-02 15:45:42 +02001011}
1012
asaperssond89920b2015-07-22 06:52:00 -07001013void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001014 rtc::CritScope lock(&crit_);
sprangb4a1ae52015-12-03 08:10:08 -08001015 uma_container_->input_frame_rate_tracker_.AddSamples(1);
asapersson320e45a2016-11-29 01:40:35 -08001016 uma_container_->input_fps_counter_.Add(1);
sprangb4a1ae52015-12-03 08:10:08 -08001017 uma_container_->input_width_counter_.Add(width);
1018 uma_container_->input_height_counter_.Add(height);
asaperssonf4e44af2017-04-19 02:01:06 -07001019 if (cpu_downscales_ >= 0) {
1020 uma_container_->cpu_limited_frame_counter_.Add(
1021 stats_.cpu_limited_resolution);
1022 }
Åsa Perssond29b54c2017-10-19 17:32:17 +02001023 if (encoded_frame_rate_tracker_.TotalSampleCount() == 0) {
1024 // Set start time now instead of when first key frame is encoded to avoid a
1025 // too high initial estimate.
1026 encoded_frame_rate_tracker_.AddSamples(0);
1027 }
perkj803d97f2016-11-01 11:45:46 -07001028}
1029
Niels Möller213618e2018-07-24 09:29:58 +02001030void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001031 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001032 switch (reason) {
1033 case DropReason::kSource:
1034 ++stats_.frames_dropped_by_capturer;
1035 break;
1036 case DropReason::kEncoderQueue:
1037 ++stats_.frames_dropped_by_encoder_queue;
1038 break;
1039 case DropReason::kEncoder:
1040 ++stats_.frames_dropped_by_encoder;
1041 break;
1042 case DropReason::kMediaOptimization:
1043 ++stats_.frames_dropped_by_rate_limiter;
1044 break;
1045 }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001046}
1047
Niels Möller213618e2018-07-24 09:29:58 +02001048void SendStatisticsProxy::OnAdaptationChanged(
1049 AdaptationReason reason,
1050 const AdaptationSteps& cpu_counts,
1051 const AdaptationSteps& quality_counts) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001052 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001053 switch (reason) {
1054 case AdaptationReason::kNone:
1055 SetAdaptTimer(cpu_counts, &uma_container_->cpu_adapt_timer_);
1056 SetAdaptTimer(quality_counts, &uma_container_->quality_adapt_timer_);
1057 break;
1058 case AdaptationReason::kCpu:
1059 ++stats_.number_of_cpu_adapt_changes;
1060 break;
1061 case AdaptationReason::kQuality:
1062 TryUpdateInitialQualityResolutionAdaptUp(quality_counts);
1063 ++stats_.number_of_quality_adapt_changes;
1064 break;
1065 }
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001066
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001067 cpu_downscales_ = cpu_counts.num_resolution_reductions.value_or(-1);
1068 quality_downscales_ = quality_counts.num_resolution_reductions.value_or(-1);
1069
1070 cpu_counts_ = cpu_counts;
1071 quality_counts_ = quality_counts;
1072
1073 UpdateAdaptationStats();
1074}
1075
1076void SendStatisticsProxy::UpdateAdaptationStats() {
1077 bool is_cpu_limited = cpu_counts_.num_resolution_reductions > 0 ||
1078 cpu_counts_.num_framerate_reductions > 0;
1079 bool is_bandwidth_limited = quality_counts_.num_resolution_reductions > 0 ||
1080 quality_counts_.num_framerate_reductions > 0 ||
1081 bw_limited_layers_;
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001082 if (is_bandwidth_limited) {
1083 // We may be both CPU limited and bandwidth limited at the same time but
1084 // there is no way to express this in standardized stats. Heuristically,
1085 // bandwidth is more likely to be a limiting factor than CPU, and more
1086 // likely to vary over time, so only when we aren't bandwidth limited do we
1087 // want to know about our CPU being the bottleneck.
1088 quality_limitation_reason_tracker_.SetReason(
1089 QualityLimitationReason::kBandwidth);
1090 } else if (is_cpu_limited) {
1091 quality_limitation_reason_tracker_.SetReason(QualityLimitationReason::kCpu);
1092 } else {
1093 quality_limitation_reason_tracker_.SetReason(
1094 QualityLimitationReason::kNone);
1095 }
1096
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001097 stats_.cpu_limited_resolution = cpu_counts_.num_resolution_reductions > 0;
1098 stats_.cpu_limited_framerate = cpu_counts_.num_framerate_reductions > 0;
1099 stats_.bw_limited_resolution = quality_counts_.num_resolution_reductions > 0;
1100 stats_.bw_limited_framerate = quality_counts_.num_framerate_reductions > 0;
1101 // If bitrate allocator has disabled some layers frame-rate or resolution are
1102 // limited depending on the encoder configuration.
1103 if (bw_limited_layers_) {
1104 switch (content_type_) {
1105 case VideoEncoderConfig::ContentType::kRealtimeVideo: {
1106 stats_.bw_limited_resolution = true;
1107 break;
1108 }
1109 case VideoEncoderConfig::ContentType::kScreen: {
1110 stats_.bw_limited_framerate = true;
1111 break;
1112 }
1113 }
1114 }
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001115 stats_.quality_limitation_reason =
1116 quality_limitation_reason_tracker_.current_reason();
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001117
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001118 // |stats_.quality_limitation_durations_ms| depends on the current time
1119 // when it is polled; it is updated in SendStatisticsProxy::GetStats().
asapersson09f05612017-05-15 23:40:18 -07001120}
1121
Evan Shrubsolecc62b162019-09-09 11:26:45 +02001122void SendStatisticsProxy::OnBitrateAllocationUpdated(
1123 const VideoCodec& codec,
1124 const VideoBitrateAllocation& allocation) {
1125 int num_spatial_layers = 0;
1126 for (int i = 0; i < kMaxSpatialLayers; i++) {
1127 if (codec.spatialLayers[i].active) {
1128 num_spatial_layers++;
1129 }
1130 }
1131 int num_simulcast_streams = 0;
1132 for (int i = 0; i < kMaxSimulcastStreams; i++) {
1133 if (codec.simulcastStream[i].active) {
1134 num_simulcast_streams++;
1135 }
1136 }
1137
1138 std::array<bool, kMaxSpatialLayers> spatial_layers;
1139 for (int i = 0; i < kMaxSpatialLayers; i++) {
1140 spatial_layers[i] = (allocation.GetSpatialLayerSum(i) > 0);
1141 }
1142
1143 rtc::CritScope lock(&crit_);
1144
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001145 bw_limited_layers_ = allocation.is_bw_limited();
1146 UpdateAdaptationStats();
1147
Evan Shrubsolecc62b162019-09-09 11:26:45 +02001148 if (spatial_layers != last_spatial_layer_use_) {
1149 // If the number of spatial layers has changed, the resolution change is
1150 // not due to quality limitations, it is because the configuration
1151 // changed.
1152 if (last_num_spatial_layers_ == num_spatial_layers &&
1153 last_num_simulcast_streams_ == num_simulcast_streams) {
1154 ++stats_.quality_limitation_resolution_changes;
1155 }
1156 last_spatial_layer_use_ = spatial_layers;
1157 }
1158 last_num_spatial_layers_ = num_spatial_layers;
1159 last_num_simulcast_streams_ = num_simulcast_streams;
1160}
1161
Åsa Persson875841d2018-01-08 08:49:53 +01001162// TODO(asapersson): Include fps changes.
1163void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
1164 rtc::CritScope lock(&crit_);
1165 ++uma_container_->initial_quality_changes_.down;
1166}
1167
1168void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
Niels Möller213618e2018-07-24 09:29:58 +02001169 const AdaptationSteps& quality_counts) {
Åsa Persson875841d2018-01-08 08:49:53 +01001170 if (uma_container_->initial_quality_changes_.down == 0)
1171 return;
1172
1173 if (quality_downscales_ > 0 &&
Niels Möller213618e2018-07-24 09:29:58 +02001174 quality_counts.num_resolution_reductions.value_or(-1) <
1175 quality_downscales_) {
Åsa Persson875841d2018-01-08 08:49:53 +01001176 // Adapting up in quality.
1177 if (uma_container_->initial_quality_changes_.down >
1178 uma_container_->initial_quality_changes_.up) {
1179 ++uma_container_->initial_quality_changes_.up;
1180 }
1181 }
1182}
1183
Niels Möller213618e2018-07-24 09:29:58 +02001184void SendStatisticsProxy::SetAdaptTimer(const AdaptationSteps& counts,
1185 StatsTimer* timer) {
1186 if (counts.num_resolution_reductions || counts.num_framerate_reductions) {
asapersson09f05612017-05-15 23:40:18 -07001187 // Adaptation enabled.
1188 if (!stats_.suspended)
1189 timer->Start(clock_->TimeInMilliseconds());
1190 return;
1191 }
1192 timer->Stop(clock_->TimeInMilliseconds());
kthelgason876222f2016-11-29 01:44:11 -08001193}
1194
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001195void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
1196 uint32_t ssrc,
1197 const RtcpPacketTypeCounter& packet_counter) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001198 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001199 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001200 if (!stats)
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001201 return;
1202
1203 stats->rtcp_packet_type_counts = packet_counter;
sprang07fb9be2016-02-24 07:55:00 -08001204 if (uma_container_->first_rtcp_stats_time_ms_ == -1)
1205 uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001206}
1207
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001208void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
1209 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001210 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001211 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001212 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001213 return;
1214
1215 stats->rtcp_stats = statistics;
Niels Möller77d3efc2019-08-01 15:09:51 +02001216 uma_container_->report_block_stats_.Store(ssrc, statistics);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001217}
1218
Henrik Boström87e3f9d2019-05-27 10:44:24 +02001219void SendStatisticsProxy::OnReportBlockDataUpdated(
1220 ReportBlockData report_block_data) {
1221 rtc::CritScope lock(&crit_);
1222 VideoSendStream::StreamStats* stats =
1223 GetStatsEntry(report_block_data.report_block().source_ssrc);
1224 if (!stats)
1225 return;
1226 stats->report_block_data = std::move(report_block_data);
1227}
1228
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001229void SendStatisticsProxy::DataCountersUpdated(
1230 const StreamDataCounters& counters,
1231 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001232 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001233 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
asapersson93e1e232017-02-06 05:18:35 -08001234 RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc " << ssrc;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001235
asaperssona6a699a2016-11-25 03:52:46 -08001236 if (stats->is_flexfec) {
1237 // The same counters are reported for both the media ssrc and flexfec ssrc.
1238 // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
1239 return;
1240 }
1241
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001242 stats->rtp_stats = counters;
asapersson6eca98b2017-04-04 23:40:50 -07001243 if (uma_container_->first_rtp_stats_time_ms_ == -1) {
1244 int64_t now_ms = clock_->TimeInMilliseconds();
1245 uma_container_->first_rtp_stats_time_ms_ = now_ms;
asapersson09f05612017-05-15 23:40:18 -07001246 uma_container_->cpu_adapt_timer_.Restart(now_ms);
1247 uma_container_->quality_adapt_timer_.Restart(now_ms);
asapersson6eca98b2017-04-04 23:40:50 -07001248 }
asapersson93e1e232017-02-06 05:18:35 -08001249
1250 uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),
1251 ssrc);
1252 uma_container_->padding_byte_counter_.Set(counters.transmitted.padding_bytes,
1253 ssrc);
1254 uma_container_->retransmit_byte_counter_.Set(
1255 counters.retransmitted.TotalBytes(), ssrc);
1256 uma_container_->fec_byte_counter_.Set(counters.fec.TotalBytes(), ssrc);
1257 if (stats->is_rtx) {
1258 uma_container_->rtx_byte_counter_.Set(counters.transmitted.TotalBytes(),
1259 ssrc);
1260 } else {
1261 uma_container_->media_byte_counter_.Set(counters.MediaPayloadBytes(), ssrc);
1262 }
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001263}
1264
sprangcd349d92016-07-13 09:11:28 -07001265void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
1266 uint32_t retransmit_bitrate_bps,
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001267 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001268 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001269 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001270 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001271 return;
1272
sprangcd349d92016-07-13 09:11:28 -07001273 stats->total_bitrate_bps = total_bitrate_bps;
1274 stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001275}
1276
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001277void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
1278 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001279 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001280 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001281 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001282 return;
1283
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001284 stats->frame_counts = frame_counts;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001285}
1286
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001287void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
1288 int max_delay_ms,
Henrik Boström9fe18342019-05-16 18:38:20 +02001289 uint64_t total_delay_ms,
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001290 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001291 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001292 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001293 if (!stats)
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001294 return;
1295 stats->avg_delay_ms = avg_delay_ms;
1296 stats->max_delay_ms = max_delay_ms;
Henrik Boström9fe18342019-05-16 18:38:20 +02001297 stats->total_packet_send_delay_ms = total_delay_ms;
asaperssonf040b232015-11-04 00:59:03 -08001298
sprangb4a1ae52015-12-03 08:10:08 -08001299 uma_container_->delay_counter_.Add(avg_delay_ms);
1300 uma_container_->max_delay_counter_.Add(max_delay_ms);
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001301}
1302
asapersson6eca98b2017-04-04 23:40:50 -07001303void SendStatisticsProxy::StatsTimer::Start(int64_t now_ms) {
1304 if (start_ms == -1)
1305 start_ms = now_ms;
1306}
1307
1308void SendStatisticsProxy::StatsTimer::Stop(int64_t now_ms) {
1309 if (start_ms != -1) {
1310 total_ms += now_ms - start_ms;
1311 start_ms = -1;
1312 }
1313}
1314
1315void SendStatisticsProxy::StatsTimer::Restart(int64_t now_ms) {
1316 total_ms = 0;
1317 if (start_ms != -1)
1318 start_ms = now_ms;
1319}
1320
asaperssond89920b2015-07-22 06:52:00 -07001321void SendStatisticsProxy::SampleCounter::Add(int sample) {
1322 sum += sample;
1323 ++num_samples;
1324}
1325
asapersson66d4b372016-12-19 06:50:53 -08001326int SendStatisticsProxy::SampleCounter::Avg(
1327 int64_t min_required_samples) const {
asaperssond89920b2015-07-22 06:52:00 -07001328 if (num_samples < min_required_samples || num_samples == 0)
1329 return -1;
asapersson66d4b372016-12-19 06:50:53 -08001330 return static_cast<int>((sum + (num_samples / 2)) / num_samples);
asaperssond89920b2015-07-22 06:52:00 -07001331}
1332
asaperssondec5ebf2015-10-05 02:36:17 -07001333void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
1334 if (sample)
1335 ++sum;
1336 ++num_samples;
1337}
1338
asapersson66d4b372016-12-19 06:50:53 -08001339void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
1340 if (sample)
1341 sum += count;
1342 num_samples += count;
1343}
asaperssondec5ebf2015-10-05 02:36:17 -07001344int SendStatisticsProxy::BoolSampleCounter::Percent(
asapersson66d4b372016-12-19 06:50:53 -08001345 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001346 return Fraction(min_required_samples, 100.0f);
1347}
1348
1349int SendStatisticsProxy::BoolSampleCounter::Permille(
asapersson66d4b372016-12-19 06:50:53 -08001350 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001351 return Fraction(min_required_samples, 1000.0f);
1352}
1353
1354int SendStatisticsProxy::BoolSampleCounter::Fraction(
asapersson66d4b372016-12-19 06:50:53 -08001355 int64_t min_required_samples,
1356 float multiplier) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001357 if (num_samples < min_required_samples || num_samples == 0)
1358 return -1;
1359 return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
1360}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001361} // namespace webrtc