blob: 58fb82e8245ee7a86478a8a760e505292fa5b78c [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
Åsa Perssonaa329e72017-12-15 15:54:44 +0100218void SendStatisticsProxy::UmaSamplesContainer::RemoveOld(
219 int64_t now_ms,
220 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200221 while (!encoded_frames_.empty()) {
222 auto it = encoded_frames_.begin();
223 if (now_ms - it->second.send_ms < kMaxEncodedFrameWindowMs)
224 break;
225
226 // Use max per timestamp.
227 sent_width_counter_.Add(it->second.max_width);
228 sent_height_counter_.Add(it->second.max_height);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100229
230 // Check number of encoded streams per timestamp.
Niels Möllerd3b8c632018-08-27 15:33:42 +0200231 if (num_streams_ > static_cast<size_t>(it->second.max_simulcast_idx)) {
Åsa Perssonaa329e72017-12-15 15:54:44 +0100232 *is_limited_in_resolution = false;
233 if (num_streams_ > 1) {
234 int disabled_streams =
235 static_cast<int>(num_streams_ - 1 - it->second.max_simulcast_idx);
236 // Can be limited in resolution or framerate.
237 uint32_t pixels = it->second.max_width * it->second.max_height;
238 bool bw_limited_resolution =
239 disabled_streams > 0 && pixels < num_pixels_highest_stream_;
240 bw_limited_frame_counter_.Add(bw_limited_resolution);
241 if (bw_limited_resolution) {
242 bw_resolutions_disabled_counter_.Add(disabled_streams);
243 *is_limited_in_resolution = true;
244 }
245 }
246 }
Åsa Persson0122e842017-10-16 12:19:23 +0200247 encoded_frames_.erase(it);
248 }
249}
250
251bool SendStatisticsProxy::UmaSamplesContainer::InsertEncodedFrame(
Åsa Perssonaa329e72017-12-15 15:54:44 +0100252 const EncodedImage& encoded_frame,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200253 int simulcast_idx,
Åsa Perssonaa329e72017-12-15 15:54:44 +0100254 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200255 int64_t now_ms = clock_->TimeInMilliseconds();
Åsa Perssonaa329e72017-12-15 15:54:44 +0100256 RemoveOld(now_ms, is_limited_in_resolution);
Åsa Persson0122e842017-10-16 12:19:23 +0200257 if (encoded_frames_.size() > kMaxEncodedFrameMapSize) {
258 encoded_frames_.clear();
259 }
260
Åsa Persson20317f92018-08-15 08:57:54 +0200261 // Check for jump in timestamp.
262 if (!encoded_frames_.empty()) {
263 uint32_t oldest_timestamp = encoded_frames_.begin()->first;
Niels Möller23775882018-08-16 10:24:12 +0200264 if (ForwardDiff(oldest_timestamp, encoded_frame.Timestamp()) >
Åsa Persson20317f92018-08-15 08:57:54 +0200265 kMaxEncodedFrameTimestampDiff) {
266 // Gap detected, clear frames to have a sequence where newest timestamp
267 // is not too far away from oldest in order to distinguish old and new.
268 encoded_frames_.clear();
269 }
270 }
271
Niels Möller72bc8d62018-09-12 10:03:51 +0200272 auto it = encoded_frames_.find(encoded_frame.Timestamp());
Åsa Persson0122e842017-10-16 12:19:23 +0200273 if (it == encoded_frames_.end()) {
274 // First frame with this timestamp.
Åsa Perssonaa329e72017-12-15 15:54:44 +0100275 encoded_frames_.insert(
Niels Möller23775882018-08-16 10:24:12 +0200276 std::make_pair(encoded_frame.Timestamp(),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100277 Frame(now_ms, encoded_frame._encodedWidth,
278 encoded_frame._encodedHeight, simulcast_idx)));
Åsa Persson0122e842017-10-16 12:19:23 +0200279 sent_fps_counter_.Add(1);
280 return true;
281 }
282
283 it->second.max_width =
284 std::max(it->second.max_width, encoded_frame._encodedWidth);
285 it->second.max_height =
286 std::max(it->second.max_height, encoded_frame._encodedHeight);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100287 it->second.max_simulcast_idx =
288 std::max(it->second.max_simulcast_idx, simulcast_idx);
Åsa Persson0122e842017-10-16 12:19:23 +0200289 return false;
290}
291
sprang07fb9be2016-02-24 07:55:00 -0800292void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
Stefan Holmerdbdb3a02018-07-17 16:03:46 +0200293 const RtpConfig& rtp_config,
sprang07fb9be2016-02-24 07:55:00 -0800294 const VideoSendStream::Stats& current_stats) {
asaperssonc2148a52016-02-04 00:33:21 -0800295 RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
296 const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
asapersson320e45a2016-11-29 01:40:35 -0800297 const int kMinRequiredPeriodicSamples = 6;
Karl Wiberg881f1682018-03-08 15:03:23 +0100298 char log_stream_buf[8 * 1024];
299 rtc::SimpleStringBuilder log_stream(log_stream_buf);
perkj803d97f2016-11-01 11:45:46 -0700300 int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
301 int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700302 if (in_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700303 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
304 in_width);
305 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
306 in_height);
Tommifef05002018-02-27 13:51:08 +0100307 log_stream << uma_prefix_ << "InputWidthInPixels " << in_width << "\n"
308 << uma_prefix_ << "InputHeightInPixels " << in_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700309 }
asapersson320e45a2016-11-29 01:40:35 -0800310 AggregatedStats in_fps = input_fps_counter_.GetStats();
311 if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
312 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
313 in_fps.average);
Tommifef05002018-02-27 13:51:08 +0100314 log_stream << uma_prefix_ << "InputFramesPerSecond " << in_fps.ToString()
315 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800316 }
317
perkj803d97f2016-11-01 11:45:46 -0700318 int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
319 int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700320 if (sent_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700321 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
322 sent_width);
323 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
324 sent_height);
Tommifef05002018-02-27 13:51:08 +0100325 log_stream << uma_prefix_ << "SentWidthInPixels " << sent_width << "\n"
326 << uma_prefix_ << "SentHeightInPixels " << sent_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700327 }
asapersson320e45a2016-11-29 01:40:35 -0800328 AggregatedStats sent_fps = sent_fps_counter_.GetStats();
329 if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
330 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
331 sent_fps.average);
Tommifef05002018-02-27 13:51:08 +0100332 log_stream << uma_prefix_ << "SentFramesPerSecond " << sent_fps.ToString()
333 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800334 }
335
ilnikf4ded682017-08-30 02:30:20 -0700336 if (in_fps.num_samples > kMinRequiredPeriodicSamples &&
337 sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
338 int in_fps_avg = in_fps.average;
339 if (in_fps_avg > 0) {
340 int sent_fps_avg = sent_fps.average;
341 int sent_to_in_fps_ratio_percent =
342 (100 * sent_fps_avg + in_fps_avg / 2) / in_fps_avg;
343 // If reported period is small, it may happen that sent_fps is larger than
344 // input_fps briefly on average. This should be treated as 100% sent to
345 // input ratio.
346 if (sent_to_in_fps_ratio_percent > 100)
347 sent_to_in_fps_ratio_percent = 100;
348 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
349 uma_prefix_ + "SentToInputFpsRatioPercent",
350 sent_to_in_fps_ratio_percent);
Tommifef05002018-02-27 13:51:08 +0100351 log_stream << uma_prefix_ << "SentToInputFpsRatioPercent "
352 << sent_to_in_fps_ratio_percent << "\n";
ilnikf4ded682017-08-30 02:30:20 -0700353 }
354 }
355
perkj803d97f2016-11-01 11:45:46 -0700356 int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonc2148a52016-02-04 00:33:21 -0800357 if (encode_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700358 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
359 encode_ms);
Tommifef05002018-02-27 13:51:08 +0100360 log_stream << uma_prefix_ << "EncodeTimeInMs " << encode_ms << "\n";
asaperssonc2148a52016-02-04 00:33:21 -0800361 }
perkj803d97f2016-11-01 11:45:46 -0700362 int key_frames_permille =
363 key_frame_counter_.Permille(kMinRequiredMetricsSamples);
asaperssondec5ebf2015-10-05 02:36:17 -0700364 if (key_frames_permille != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700365 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
366 key_frames_permille);
Tommifef05002018-02-27 13:51:08 +0100367 log_stream << uma_prefix_ << "KeyFramesSentInPermille "
368 << key_frames_permille << "\n";
asaperssondec5ebf2015-10-05 02:36:17 -0700369 }
asapersson4306fc72015-10-19 00:35:21 -0700370 int quality_limited =
perkj803d97f2016-11-01 11:45:46 -0700371 quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700372 if (quality_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700373 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
374 uma_prefix_ + "QualityLimitedResolutionInPercent",
375 quality_limited);
Tommifef05002018-02-27 13:51:08 +0100376 log_stream << uma_prefix_ << "QualityLimitedResolutionInPercent "
377 << quality_limited << "\n";
asapersson4306fc72015-10-19 00:35:21 -0700378 }
perkj803d97f2016-11-01 11:45:46 -0700379 int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700380 if (downscales != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700381 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800382 kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
383 20);
asapersson4306fc72015-10-19 00:35:21 -0700384 }
perkj803d97f2016-11-01 11:45:46 -0700385 int cpu_limited =
386 cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
387 if (cpu_limited != -1) {
388 RTC_HISTOGRAMS_PERCENTAGE(
389 kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
390 }
391 int bw_limited =
392 bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700393 if (bw_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700394 RTC_HISTOGRAMS_PERCENTAGE(
asaperssonc2148a52016-02-04 00:33:21 -0800395 kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
396 bw_limited);
asaperssonda535c42015-10-19 23:32:41 -0700397 }
perkj803d97f2016-11-01 11:45:46 -0700398 int num_disabled =
399 bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700400 if (num_disabled != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700401 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800402 kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
403 num_disabled, 10);
asaperssonda535c42015-10-19 23:32:41 -0700404 }
perkj803d97f2016-11-01 11:45:46 -0700405 int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800406 if (delay_ms != -1)
asapersson1d02d3e2016-09-09 22:40:25 -0700407 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
408 delay_ms);
asaperssonf040b232015-11-04 00:59:03 -0800409
perkj803d97f2016-11-01 11:45:46 -0700410 int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800411 if (max_delay_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700412 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
413 max_delay_ms);
sprangb4a1ae52015-12-03 08:10:08 -0800414 }
sprang07fb9be2016-02-24 07:55:00 -0800415
asapersson118ef002016-03-31 00:00:19 -0700416 for (const auto& it : qp_counters_) {
perkj803d97f2016-11-01 11:45:46 -0700417 int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700418 if (qp_vp8 != -1) {
asapersson118ef002016-03-31 00:00:19 -0700419 int spatial_idx = it.first;
420 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700421 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
422 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700423 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700424 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
425 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700426 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700427 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
428 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700429 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700430 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
431 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700432 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100433 RTC_LOG(LS_WARNING)
434 << "QP stats not recorded for VP8 spatial idx " << spatial_idx;
asapersson118ef002016-03-31 00:00:19 -0700435 }
436 }
perkj803d97f2016-11-01 11:45:46 -0700437 int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700438 if (qp_vp9 != -1) {
439 int spatial_idx = it.first;
440 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700441 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
442 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700443 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700444 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
445 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700446 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700447 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
448 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700449 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700450 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
451 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700452 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100453 RTC_LOG(LS_WARNING)
454 << "QP stats not recorded for VP9 spatial layer " << spatial_idx;
asapersson5265fed2016-04-18 02:58:47 -0700455 }
456 }
asapersson827cab32016-11-02 09:08:47 -0700457 int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
458 if (qp_h264 != -1) {
459 int spatial_idx = it.first;
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200460 if (spatial_idx == -1) {
461 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264",
462 qp_h264);
463 } else if (spatial_idx == 0) {
464 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S0",
465 qp_h264);
466 } else if (spatial_idx == 1) {
467 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S1",
468 qp_h264);
469 } else if (spatial_idx == 2) {
470 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S2",
471 qp_h264);
472 } else {
473 RTC_LOG(LS_WARNING)
474 << "QP stats not recorded for H264 spatial idx " << spatial_idx;
475 }
asapersson827cab32016-11-02 09:08:47 -0700476 }
asapersson118ef002016-03-31 00:00:19 -0700477 }
478
asapersson0944a802017-04-07 00:57:58 -0700479 if (first_rtp_stats_time_ms_ != -1) {
asapersson09f05612017-05-15 23:40:18 -0700480 quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
481 int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700482 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
483 int quality_changes = current_stats.number_of_quality_adapt_changes -
484 start_stats_.number_of_quality_adapt_changes;
Åsa Persson875841d2018-01-08 08:49:53 +0100485 // Only base stats on changes during a call, discard initial changes.
486 int initial_changes =
487 initial_quality_changes_.down + initial_quality_changes_.up;
488 if (initial_changes <= quality_changes)
489 quality_changes -= initial_changes;
asapersson0944a802017-04-07 00:57:58 -0700490 RTC_HISTOGRAMS_COUNTS_100(kIndex,
491 uma_prefix_ + "AdaptChangesPerMinute.Quality",
492 quality_changes * 60 / elapsed_sec);
493 }
asapersson09f05612017-05-15 23:40:18 -0700494 cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
495 elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700496 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
497 int cpu_changes = current_stats.number_of_cpu_adapt_changes -
498 start_stats_.number_of_cpu_adapt_changes;
499 RTC_HISTOGRAMS_COUNTS_100(kIndex,
500 uma_prefix_ + "AdaptChangesPerMinute.Cpu",
501 cpu_changes * 60 / elapsed_sec);
502 }
asapersson6eca98b2017-04-04 23:40:50 -0700503 }
504
sprange2d83d62016-02-19 09:03:26 -0800505 if (first_rtcp_stats_time_ms_ != -1) {
sprang07fb9be2016-02-24 07:55:00 -0800506 int64_t elapsed_sec =
507 (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
508 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
509 int fraction_lost = report_block_stats_.FractionLostInPercent();
510 if (fraction_lost != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700511 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800512 kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
Tommifef05002018-02-27 13:51:08 +0100513 log_stream << uma_prefix_ << "SentPacketsLostInPercent "
Stefan Holmer0a5792e2018-10-05 13:47:12 +0200514 << fraction_lost << "\n";
sprang07fb9be2016-02-24 07:55:00 -0800515 }
516
517 // The RTCP packet type counters, delivered via the
518 // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
519 // life of the send stream and are not reset when switching content type.
520 // For the purpose of these statistics though, we want new counts when
521 // switching since we switch histogram name. On every reset of the
522 // UmaSamplesContainer, we save the initial state of the counters, so that
523 // we can calculate the delta here and aggregate over all ssrcs.
524 RtcpPacketTypeCounter counters;
perkj26091b12016-09-01 01:17:40 -0700525 for (uint32_t ssrc : rtp_config.ssrcs) {
sprang07fb9be2016-02-24 07:55:00 -0800526 auto kv = current_stats.substreams.find(ssrc);
527 if (kv == current_stats.substreams.end())
528 continue;
529
530 RtcpPacketTypeCounter stream_counters =
531 kv->second.rtcp_packet_type_counts;
532 kv = start_stats_.substreams.find(ssrc);
533 if (kv != start_stats_.substreams.end())
534 stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
535
536 counters.Add(stream_counters);
537 }
asapersson1d02d3e2016-09-09 22:40:25 -0700538 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
539 uma_prefix_ + "NackPacketsReceivedPerMinute",
540 counters.nack_packets * 60 / elapsed_sec);
541 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
542 uma_prefix_ + "FirPacketsReceivedPerMinute",
543 counters.fir_packets * 60 / elapsed_sec);
544 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
545 uma_prefix_ + "PliPacketsReceivedPerMinute",
546 counters.pli_packets * 60 / elapsed_sec);
sprang07fb9be2016-02-24 07:55:00 -0800547 if (counters.nack_requests > 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700548 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800549 kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
550 counters.UniqueNackRequestsInPercent());
551 }
sprange2d83d62016-02-19 09:03:26 -0800552 }
553 }
Erik Språng22c2b482016-03-01 09:40:42 +0100554
555 if (first_rtp_stats_time_ms_ != -1) {
556 int64_t elapsed_sec =
557 (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
558 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
asapersson66d4b372016-12-19 06:50:53 -0800559 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
560 target_rate_updates_.pause_resume_events);
Tommifef05002018-02-27 13:51:08 +0100561 log_stream << uma_prefix_ << "NumberOfPauseEvents "
562 << target_rate_updates_.pause_resume_events << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800563
564 int paused_time_percent =
565 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
566 if (paused_time_percent != -1) {
567 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
568 paused_time_percent);
Tommifef05002018-02-27 13:51:08 +0100569 log_stream << uma_prefix_ << "PausedTimeInPercent "
570 << paused_time_percent << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800571 }
asapersson93e1e232017-02-06 05:18:35 -0800572 }
573 }
asapersson66d4b372016-12-19 06:50:53 -0800574
asapersson8d75ac72017-09-15 06:41:15 -0700575 if (fallback_info_.is_possible) {
576 // Double interval since there is some time before fallback may occur.
577 const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000;
578 int64_t elapsed_ms = fallback_info_.elapsed_ms;
579 int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs);
580 if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) {
581 RTC_HISTOGRAMS_PERCENTAGE(
582 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8",
583 fallback_time_percent);
584 RTC_HISTOGRAMS_COUNTS_100(
585 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8",
586 fallback_info_.on_off_events * 60 / (elapsed_ms / 1000));
587 }
588 }
589
asapersson93e1e232017-02-06 05:18:35 -0800590 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats();
591 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
592 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps",
593 total_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100594 log_stream << uma_prefix_ << "BitrateSentInBps "
595 << total_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800596 }
597 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats();
598 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
599 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
600 media_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100601 log_stream << uma_prefix_ << "MediaBitrateSentInBps "
602 << media_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800603 }
604 AggregatedStats padding_bytes_per_sec = padding_byte_counter_.GetStats();
605 if (padding_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
606 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
607 uma_prefix_ + "PaddingBitrateSentInKbps",
608 padding_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100609 log_stream << uma_prefix_ << "PaddingBitrateSentInBps "
610 << padding_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800611 }
612 AggregatedStats retransmit_bytes_per_sec =
613 retransmit_byte_counter_.GetStats();
614 if (retransmit_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
615 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
616 uma_prefix_ + "RetransmittedBitrateSentInKbps",
617 retransmit_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100618 log_stream << uma_prefix_ << "RetransmittedBitrateSentInBps "
619 << retransmit_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800620 }
621 if (!rtp_config.rtx.ssrcs.empty()) {
622 AggregatedStats rtx_bytes_per_sec = rtx_byte_counter_.GetStats();
623 int rtx_bytes_per_sec_avg = -1;
624 if (rtx_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
625 rtx_bytes_per_sec_avg = rtx_bytes_per_sec.average;
Tommifef05002018-02-27 13:51:08 +0100626 log_stream << uma_prefix_ << "RtxBitrateSentInBps "
627 << rtx_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800628 } else if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
629 rtx_bytes_per_sec_avg = 0; // RTX enabled but no RTX data sent, record 0.
630 }
631 if (rtx_bytes_per_sec_avg != -1) {
632 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
633 rtx_bytes_per_sec_avg * 8 / 1000);
634 }
635 }
636 if (rtp_config.flexfec.payload_type != -1 ||
637 rtp_config.ulpfec.red_payload_type != -1) {
638 AggregatedStats fec_bytes_per_sec = fec_byte_counter_.GetStats();
639 if (fec_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
640 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "FecBitrateSentInKbps",
641 fec_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100642 log_stream << uma_prefix_ << "FecBitrateSentInBps "
643 << fec_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
Erik Språng22c2b482016-03-01 09:40:42 +0100644 }
645 }
Tommifef05002018-02-27 13:51:08 +0100646 log_stream << "Frames encoded " << current_stats.frames_encoded << "\n"
647 << uma_prefix_ << "DroppedFrames.Capturer "
648 << current_stats.frames_dropped_by_capturer << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200649 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
650 current_stats.frames_dropped_by_capturer);
Tommifef05002018-02-27 13:51:08 +0100651 log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
652 << current_stats.frames_dropped_by_encoder_queue << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200653 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
654 current_stats.frames_dropped_by_encoder_queue);
Tommifef05002018-02-27 13:51:08 +0100655 log_stream << uma_prefix_ << "DroppedFrames.Encoder "
656 << current_stats.frames_dropped_by_encoder << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200657 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Encoder",
658 current_stats.frames_dropped_by_encoder);
Tommifef05002018-02-27 13:51:08 +0100659 log_stream << uma_prefix_ << "DroppedFrames.Ratelimiter "
660 << current_stats.frames_dropped_by_rate_limiter;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200661 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Ratelimiter",
662 current_stats.frames_dropped_by_rate_limiter);
Jonas Olsson694a36f2018-02-16 13:09:41 +0100663
Tommifef05002018-02-27 13:51:08 +0100664 RTC_LOG(LS_INFO) << log_stream.str();
sprangb4a1ae52015-12-03 08:10:08 -0800665}
666
Pera48ddb72016-09-29 11:48:50 +0200667void SendStatisticsProxy::OnEncoderReconfigured(
668 const VideoEncoderConfig& config,
Niels Möller97e04882018-05-25 09:43:26 +0200669 const std::vector<VideoStream>& streams) {
sprangb4a1ae52015-12-03 08:10:08 -0800670 rtc::CritScope lock(&crit_);
Pera48ddb72016-09-29 11:48:50 +0200671
672 if (content_type_ != config.content_type) {
perkj26091b12016-09-01 01:17:40 -0700673 uma_container_->UpdateHistograms(rtp_config_, stats_);
Pera48ddb72016-09-29 11:48:50 +0200674 uma_container_.reset(new UmaSamplesContainer(
675 GetUmaPrefix(config.content_type), stats_, clock_));
676 content_type_ = config.content_type;
asaperssonf040b232015-11-04 00:59:03 -0800677 }
Åsa Perssonaa329e72017-12-15 15:54:44 +0100678 uma_container_->encoded_frames_.clear();
679 uma_container_->num_streams_ = streams.size();
680 uma_container_->num_pixels_highest_stream_ =
681 streams.empty() ? 0 : (streams.back().width * streams.back().height);
Åsa Persson24b4eda2015-06-16 10:17:01 +0200682}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000683
Niels Möller213618e2018-07-24 09:29:58 +0200684void SendStatisticsProxy::OnEncodedFrameTimeMeasured(int encode_time_ms,
685 int encode_usage_percent) {
Henrik Boström5684af52019-04-02 15:05:21 +0200686 RTC_DCHECK_GE(encode_time_ms, 0);
Peter Boströmf2f82832015-05-01 13:00:41 +0200687 rtc::CritScope lock(&crit_);
Peter Boströme4499152016-02-05 11:13:28 +0100688 uma_container_->encode_time_counter_.Add(encode_time_ms);
689 encode_time_.Apply(1.0f, encode_time_ms);
Oleh Prypin19929582019-04-23 08:50:04 +0200690 stats_.avg_encode_time_ms = std::round(encode_time_.filtered());
Henrik Boström5684af52019-04-02 15:05:21 +0200691 stats_.total_encode_time_ms += encode_time_ms;
Niels Möller213618e2018-07-24 09:29:58 +0200692 stats_.encode_usage_percent = encode_usage_percent;
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000693}
694
Peter Boström7083e112015-09-22 16:28:51 +0200695void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
asapersson0944a802017-04-07 00:57:58 -0700696 int64_t now_ms = clock_->TimeInMilliseconds();
Peter Boströmf2f82832015-05-01 13:00:41 +0200697 rtc::CritScope lock(&crit_);
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000698 stats_.suspended = is_suspended;
asapersson320e45a2016-11-29 01:40:35 -0800699 if (is_suspended) {
asapersson93e1e232017-02-06 05:18:35 -0800700 // Pause framerate (add min pause time since there may be frames/packets
701 // that are not yet sent).
702 const int64_t kMinMs = 500;
703 uma_container_->input_fps_counter_.ProcessAndPauseForDuration(kMinMs);
704 uma_container_->sent_fps_counter_.ProcessAndPauseForDuration(kMinMs);
705 // Pause bitrate stats.
706 uma_container_->total_byte_counter_.ProcessAndPauseForDuration(kMinMs);
707 uma_container_->media_byte_counter_.ProcessAndPauseForDuration(kMinMs);
708 uma_container_->rtx_byte_counter_.ProcessAndPauseForDuration(kMinMs);
709 uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
710 uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
711 uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
asapersson0944a802017-04-07 00:57:58 -0700712 // Stop adaptation stats.
asapersson09f05612017-05-15 23:40:18 -0700713 uma_container_->cpu_adapt_timer_.Stop(now_ms);
714 uma_container_->quality_adapt_timer_.Stop(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800715 } else {
asapersson0944a802017-04-07 00:57:58 -0700716 // Start adaptation stats if scaling is enabled.
717 if (cpu_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700718 uma_container_->cpu_adapt_timer_.Start(now_ms);
asapersson0944a802017-04-07 00:57:58 -0700719 if (quality_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700720 uma_container_->quality_adapt_timer_.Start(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800721 // Stop pause explicitly for stats that may be zero/not updated for some
722 // time.
723 uma_container_->rtx_byte_counter_.ProcessAndStopPause();
724 uma_container_->padding_byte_counter_.ProcessAndStopPause();
725 uma_container_->retransmit_byte_counter_.ProcessAndStopPause();
726 uma_container_->fec_byte_counter_.ProcessAndStopPause();
asapersson320e45a2016-11-29 01:40:35 -0800727 }
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000728}
729
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000730VideoSendStream::Stats SendStatisticsProxy::GetStats() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200731 rtc::CritScope lock(&crit_);
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000732 PurgeOldStats();
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000733 stats_.input_frame_rate =
sprangb4a1ae52015-12-03 08:10:08 -0800734 round(uma_container_->input_frame_rate_tracker_.ComputeRate());
ilnik50864a82017-09-06 12:32:35 -0700735 stats_.content_type =
736 content_type_ == VideoEncoderConfig::ContentType::kRealtimeVideo
737 ? VideoContentType::UNSPECIFIED
738 : VideoContentType::SCREENSHARE;
Åsa Persson0122e842017-10-16 12:19:23 +0200739 stats_.encode_frame_rate = round(encoded_frame_rate_tracker_.ComputeRate());
740 stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
Henrik Boströmce33b6a2019-05-28 17:42:38 +0200741 stats_.quality_limitation_durations_ms =
742 quality_limitation_reason_tracker_.DurationsMs();
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000743 return stats_;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000744}
745
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000746void SendStatisticsProxy::PurgeOldStats() {
Peter Boström20f3f942015-05-15 11:33:39 +0200747 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000748 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
749 stats_.substreams.begin();
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000750 it != stats_.substreams.end(); ++it) {
751 uint32_t ssrc = it->first;
Peter Boström20f3f942015-05-15 11:33:39 +0200752 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
753 it->second.width = 0;
754 it->second.height = 0;
755 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000756 }
757}
758
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000759VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
760 uint32_t ssrc) {
761 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
762 stats_.substreams.find(ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000763 if (it != stats_.substreams.end())
764 return &it->second;
765
Steve Antonbd631a02019-03-28 10:51:27 -0700766 bool is_media = absl::c_linear_search(rtp_config_.ssrcs, ssrc);
brandtr3d200bd2017-01-16 06:59:19 -0800767 bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
768 ssrc == rtp_config_.flexfec.ssrc;
Steve Antonbd631a02019-03-28 10:51:27 -0700769 bool is_rtx = absl::c_linear_search(rtp_config_.rtx.ssrcs, ssrc);
brandtrcd188f62016-11-15 08:21:52 -0800770 if (!is_media && !is_flexfec && !is_rtx)
771 return nullptr;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000772
asapersson2e5cfcd2016-08-11 08:41:18 -0700773 // Insert new entry and return ptr.
774 VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
775 entry->is_rtx = is_rtx;
asaperssona6a699a2016-11-25 03:52:46 -0800776 entry->is_flexfec = is_flexfec;
asapersson2e5cfcd2016-08-11 08:41:18 -0700777
778 return entry;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000779}
780
Peter Boström20f3f942015-05-15 11:33:39 +0200781void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
782 rtc::CritScope lock(&crit_);
783 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200784 if (!stats)
Peter Boström20f3f942015-05-15 11:33:39 +0200785 return;
786
787 stats->total_bitrate_bps = 0;
788 stats->retransmit_bitrate_bps = 0;
789 stats->height = 0;
790 stats->width = 0;
791}
792
perkjf5b2e512016-07-05 08:34:04 -0700793void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200794 rtc::CritScope lock(&crit_);
asapersson66d4b372016-12-19 06:50:53 -0800795 if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
796 return; // Start on first non-zero bitrate, may initially be zero.
797
798 int64_t now = clock_->TimeInMilliseconds();
799 if (uma_container_->target_rate_updates_.last_ms != -1) {
800 bool was_paused = stats_.target_media_bitrate_bps == 0;
801 int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
802 uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
803
804 // Use last to not include update when stream is stopped and video disabled.
805 if (uma_container_->target_rate_updates_.last_paused_or_resumed)
806 ++uma_container_->target_rate_updates_.pause_resume_events;
807
808 // Check if video is paused/resumed.
809 uma_container_->target_rate_updates_.last_paused_or_resumed =
810 (bitrate_bps == 0) != was_paused;
811 }
812 uma_container_->target_rate_updates_.last_ms = now;
813
pbos@webrtc.org891d4832015-02-26 13:15:22 +0000814 stats_.target_media_bitrate_bps = bitrate_bps;
815}
816
asapersson8d75ac72017-09-15 06:41:15 -0700817void SendStatisticsProxy::UpdateEncoderFallbackStats(
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100818 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200819 int pixels,
820 int simulcast_index) {
821 UpdateFallbackDisabledStats(codec_info, pixels, simulcast_index);
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100822
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100823 if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
asapersson8d75ac72017-09-15 06:41:15 -0700824 return;
825 }
826
Niels Möllerd3b8c632018-08-27 15:33:42 +0200827 if (!IsForcedFallbackPossible(codec_info, simulcast_index)) {
asapersson8d75ac72017-09-15 06:41:15 -0700828 uma_container_->fallback_info_.is_possible = false;
829 return;
830 }
831
832 FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_;
833
834 const int64_t now_ms = clock_->TimeInMilliseconds();
835 bool is_active = fallback_info->is_active;
Erik Språnge2fd86a2018-10-24 11:32:39 +0200836 if (encoder_changed_) {
asapersson8d75ac72017-09-15 06:41:15 -0700837 // Implementation changed.
Erik Språnge2fd86a2018-10-24 11:32:39 +0200838 const bool last_was_vp8_software =
839 encoder_changed_->previous_encoder_implementation == kVp8SwCodecName;
840 is_active = encoder_changed_->new_encoder_implementation == kVp8SwCodecName;
841 encoder_changed_.reset();
842 if (!is_active && !last_was_vp8_software) {
asapersson8d75ac72017-09-15 06:41:15 -0700843 // First or not a VP8 SW change, update stats on next call.
844 return;
845 }
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100846 if (is_active && (pixels > *fallback_max_pixels_)) {
847 // Pixels should not be above |fallback_max_pixels_|. If above skip to
848 // avoid fallbacks due to failure.
849 fallback_info->is_possible = false;
850 return;
asapersson8d75ac72017-09-15 06:41:15 -0700851 }
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100852 stats_.has_entered_low_resolution = true;
asapersson8d75ac72017-09-15 06:41:15 -0700853 ++fallback_info->on_off_events;
854 }
855
856 if (fallback_info->last_update_ms) {
857 int64_t diff_ms = now_ms - *(fallback_info->last_update_ms);
858 // If the time diff since last update is greater than |max_frame_diff_ms|,
859 // video is considered paused/muted and the change is not included.
860 if (diff_ms < fallback_info->max_frame_diff_ms) {
861 uma_container_->fallback_active_counter_.Add(fallback_info->is_active,
862 diff_ms);
863 fallback_info->elapsed_ms += diff_ms;
864 }
865 }
866 fallback_info->is_active = is_active;
867 fallback_info->last_update_ms.emplace(now_ms);
868}
869
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100870void SendStatisticsProxy::UpdateFallbackDisabledStats(
871 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200872 int pixels,
873 int simulcast_index) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100874 if (!fallback_max_pixels_disabled_ ||
875 !uma_container_->fallback_info_disabled_.is_possible ||
876 stats_.has_entered_low_resolution) {
877 return;
878 }
879
Niels Möllerd3b8c632018-08-27 15:33:42 +0200880 if (!IsForcedFallbackPossible(codec_info, simulcast_index) ||
Erik Språnge2fd86a2018-10-24 11:32:39 +0200881 stats_.encoder_implementation_name == kVp8SwCodecName) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100882 uma_container_->fallback_info_disabled_.is_possible = false;
883 return;
884 }
885
886 if (pixels <= *fallback_max_pixels_disabled_ ||
887 uma_container_->fallback_info_disabled_.min_pixel_limit_reached) {
888 stats_.has_entered_low_resolution = true;
889 }
890}
891
892void SendStatisticsProxy::OnMinPixelLimitReached() {
893 rtc::CritScope lock(&crit_);
894 uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true;
895}
896
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000897void SendStatisticsProxy::OnSendEncodedImage(
898 const EncodedImage& encoded_image,
kjellander02b3d272016-04-20 05:05:54 -0700899 const CodecSpecificInfo* codec_info) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200900 // Simulcast is used for VP8, H264 and Generic.
901 int simulcast_idx =
902 (codec_info && (codec_info->codecType == kVideoCodecVP8 ||
903 codec_info->codecType == kVideoCodecH264 ||
904 codec_info->codecType == kVideoCodecGeneric))
905 ? encoded_image.SpatialIndex().value_or(0)
906 : 0;
kjellander02b3d272016-04-20 05:05:54 -0700907
perkj275afc52016-09-01 00:21:16 -0700908 rtc::CritScope lock(&crit_);
sakal43536c32016-10-24 01:46:43 -0700909 ++stats_.frames_encoded;
Henrik Boström23aff9b2019-05-20 15:15:38 +0200910 // The current encode frame rate is based on previously encoded frames.
911 double encode_frame_rate = encoded_frame_rate_tracker_.ComputeRate();
912 // We assume that less than 1 FPS is not a trustworthy estimate - perhaps we
913 // just started encoding for the first time or after a pause. Assuming frame
914 // rate is at least 1 FPS is conservative to avoid too large increments.
915 if (encode_frame_rate < 1.0)
916 encode_frame_rate = 1.0;
917 double target_frame_size_bytes =
918 stats_.target_media_bitrate_bps / (8.0 * encode_frame_rate);
919 // |stats_.target_media_bitrate_bps| is set in
920 // SendStatisticsProxy::OnSetEncoderTargetRate.
921 stats_.total_encoded_bytes_target += round(target_frame_size_bytes);
kjellander02b3d272016-04-20 05:05:54 -0700922 if (codec_info) {
Erik Språnge2fd86a2018-10-24 11:32:39 +0200923 UpdateEncoderFallbackStats(
924 codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
925 simulcast_idx);
kjellander02b3d272016-04-20 05:05:54 -0700926 }
927
Niels Möllerd3b8c632018-08-27 15:33:42 +0200928 if (static_cast<size_t>(simulcast_idx) >= rtp_config_.ssrcs.size()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100929 RTC_LOG(LS_ERROR) << "Encoded image outside simulcast range ("
930 << simulcast_idx << " >= " << rtp_config_.ssrcs.size()
931 << ").";
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000932 return;
933 }
perkj26091b12016-09-01 01:17:40 -0700934 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000935
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000936 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200937 if (!stats)
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000938 return;
939
Sergey Silkinbb081a62018-09-04 18:34:22 +0200940 // Report resolution of top spatial layer in case of VP9 SVC.
941 bool is_svc_low_spatial_layer =
942 (codec_info && codec_info->codecType == kVideoCodecVP9)
943 ? !codec_info->codecSpecific.VP9.end_of_picture
944 : false;
945
946 if (!stats->width || !stats->height || !is_svc_low_spatial_layer) {
947 stats->width = encoded_image._encodedWidth;
948 stats->height = encoded_image._encodedHeight;
949 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
950 }
asaperssond89920b2015-07-22 06:52:00 -0700951
sprangb4a1ae52015-12-03 08:10:08 -0800952 uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
Niels Möller8f7ce222019-03-21 15:43:58 +0100953 VideoFrameType::kVideoFrameKey);
asapersson4306fc72015-10-19 00:35:21 -0700954
sakal87da4042016-10-31 06:53:47 -0700955 if (encoded_image.qp_ != -1) {
956 if (!stats_.qp_sum)
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100957 stats_.qp_sum = 0;
sakal87da4042016-10-31 06:53:47 -0700958 *stats_.qp_sum += encoded_image.qp_;
959
960 if (codec_info) {
961 if (codec_info->codecType == kVideoCodecVP8) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200962 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
sakal87da4042016-10-31 06:53:47 -0700963 uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
964 } else if (codec_info->codecType == kVideoCodecVP9) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200965 int spatial_idx = encoded_image.SpatialIndex().value_or(-1);
sakal87da4042016-10-31 06:53:47 -0700966 uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
asapersson827cab32016-11-02 09:08:47 -0700967 } else if (codec_info->codecType == kVideoCodecH264) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200968 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
asapersson827cab32016-11-02 09:08:47 -0700969 uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
sakal87da4042016-10-31 06:53:47 -0700970 }
asapersson5265fed2016-04-18 02:58:47 -0700971 }
asapersson118ef002016-03-31 00:00:19 -0700972 }
973
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100974 // If any of the simulcast streams have a huge frame, it should be counted
975 // as a single difficult input frame.
976 // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200977 if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) {
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100978 if (!last_outlier_timestamp_ ||
979 *last_outlier_timestamp_ < encoded_image.capture_time_ms_) {
980 last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_);
981 ++stats_.huge_frames_sent;
982 }
983 }
984
Niels Möller77536a22019-01-15 08:50:01 +0100985 media_byte_rate_tracker_.AddSamples(encoded_image.size());
Åsa Perssonaa329e72017-12-15 15:54:44 +0100986
987 // Initialize to current since |is_limited_in_resolution| is only updated
988 // when an encoded frame is removed from the EncodedFrameMap.
989 bool is_limited_in_resolution = stats_.bw_limited_resolution;
990 if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx,
991 &is_limited_in_resolution)) {
Åsa Persson0122e842017-10-16 12:19:23 +0200992 encoded_frame_rate_tracker_.AddSamples(1);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100993 }
994
995 stats_.bw_limited_resolution =
996 is_limited_in_resolution || quality_downscales_ > 0;
997
998 if (quality_downscales_ != -1) {
999 uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
1000 if (quality_downscales_ > 0)
1001 uma_container_->quality_downscales_counter_.Add(quality_downscales_);
1002 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +00001003}
1004
Erik Språnge2fd86a2018-10-24 11:32:39 +02001005void SendStatisticsProxy::OnEncoderImplementationChanged(
1006 const std::string& implementation_name) {
1007 rtc::CritScope lock(&crit_);
1008 encoder_changed_ = EncoderChangeEvent{stats_.encoder_implementation_name,
1009 implementation_name};
1010 stats_.encoder_implementation_name = implementation_name;
1011}
1012
Niels Möller213618e2018-07-24 09:29:58 +02001013int SendStatisticsProxy::GetInputFrameRate() const {
1014 rtc::CritScope lock(&crit_);
1015 return round(uma_container_->input_frame_rate_tracker_.ComputeRate());
1016}
1017
Per69b332d2016-06-02 15:45:42 +02001018int SendStatisticsProxy::GetSendFrameRate() const {
1019 rtc::CritScope lock(&crit_);
Åsa Persson0122e842017-10-16 12:19:23 +02001020 return round(encoded_frame_rate_tracker_.ComputeRate());
Per69b332d2016-06-02 15:45:42 +02001021}
1022
asaperssond89920b2015-07-22 06:52:00 -07001023void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001024 rtc::CritScope lock(&crit_);
sprangb4a1ae52015-12-03 08:10:08 -08001025 uma_container_->input_frame_rate_tracker_.AddSamples(1);
asapersson320e45a2016-11-29 01:40:35 -08001026 uma_container_->input_fps_counter_.Add(1);
sprangb4a1ae52015-12-03 08:10:08 -08001027 uma_container_->input_width_counter_.Add(width);
1028 uma_container_->input_height_counter_.Add(height);
asaperssonf4e44af2017-04-19 02:01:06 -07001029 if (cpu_downscales_ >= 0) {
1030 uma_container_->cpu_limited_frame_counter_.Add(
1031 stats_.cpu_limited_resolution);
1032 }
Åsa Perssond29b54c2017-10-19 17:32:17 +02001033 if (encoded_frame_rate_tracker_.TotalSampleCount() == 0) {
1034 // Set start time now instead of when first key frame is encoded to avoid a
1035 // too high initial estimate.
1036 encoded_frame_rate_tracker_.AddSamples(0);
1037 }
perkj803d97f2016-11-01 11:45:46 -07001038}
1039
Niels Möller213618e2018-07-24 09:29:58 +02001040void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001041 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001042 switch (reason) {
1043 case DropReason::kSource:
1044 ++stats_.frames_dropped_by_capturer;
1045 break;
1046 case DropReason::kEncoderQueue:
1047 ++stats_.frames_dropped_by_encoder_queue;
1048 break;
1049 case DropReason::kEncoder:
1050 ++stats_.frames_dropped_by_encoder;
1051 break;
1052 case DropReason::kMediaOptimization:
1053 ++stats_.frames_dropped_by_rate_limiter;
1054 break;
1055 }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001056}
1057
Niels Möller213618e2018-07-24 09:29:58 +02001058void SendStatisticsProxy::OnAdaptationChanged(
1059 AdaptationReason reason,
1060 const AdaptationSteps& cpu_counts,
1061 const AdaptationSteps& quality_counts) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001062 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001063 switch (reason) {
1064 case AdaptationReason::kNone:
1065 SetAdaptTimer(cpu_counts, &uma_container_->cpu_adapt_timer_);
1066 SetAdaptTimer(quality_counts, &uma_container_->quality_adapt_timer_);
1067 break;
1068 case AdaptationReason::kCpu:
1069 ++stats_.number_of_cpu_adapt_changes;
1070 break;
1071 case AdaptationReason::kQuality:
1072 TryUpdateInitialQualityResolutionAdaptUp(quality_counts);
1073 ++stats_.number_of_quality_adapt_changes;
1074 break;
1075 }
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001076
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001077 cpu_downscales_ = cpu_counts.num_resolution_reductions.value_or(-1);
1078 quality_downscales_ = quality_counts.num_resolution_reductions.value_or(-1);
1079
1080 cpu_counts_ = cpu_counts;
1081 quality_counts_ = quality_counts;
1082
1083 UpdateAdaptationStats();
1084}
1085
1086void SendStatisticsProxy::UpdateAdaptationStats() {
1087 bool is_cpu_limited = cpu_counts_.num_resolution_reductions > 0 ||
1088 cpu_counts_.num_framerate_reductions > 0;
1089 bool is_bandwidth_limited = quality_counts_.num_resolution_reductions > 0 ||
1090 quality_counts_.num_framerate_reductions > 0 ||
1091 bw_limited_layers_;
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001092 if (is_bandwidth_limited) {
1093 // We may be both CPU limited and bandwidth limited at the same time but
1094 // there is no way to express this in standardized stats. Heuristically,
1095 // bandwidth is more likely to be a limiting factor than CPU, and more
1096 // likely to vary over time, so only when we aren't bandwidth limited do we
1097 // want to know about our CPU being the bottleneck.
1098 quality_limitation_reason_tracker_.SetReason(
1099 QualityLimitationReason::kBandwidth);
1100 } else if (is_cpu_limited) {
1101 quality_limitation_reason_tracker_.SetReason(QualityLimitationReason::kCpu);
1102 } else {
1103 quality_limitation_reason_tracker_.SetReason(
1104 QualityLimitationReason::kNone);
1105 }
1106
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001107 stats_.cpu_limited_resolution = cpu_counts_.num_resolution_reductions > 0;
1108 stats_.cpu_limited_framerate = cpu_counts_.num_framerate_reductions > 0;
1109 stats_.bw_limited_resolution = quality_counts_.num_resolution_reductions > 0;
1110 stats_.bw_limited_framerate = quality_counts_.num_framerate_reductions > 0;
1111 // If bitrate allocator has disabled some layers frame-rate or resolution are
1112 // limited depending on the encoder configuration.
1113 if (bw_limited_layers_) {
1114 switch (content_type_) {
1115 case VideoEncoderConfig::ContentType::kRealtimeVideo: {
1116 stats_.bw_limited_resolution = true;
1117 break;
1118 }
1119 case VideoEncoderConfig::ContentType::kScreen: {
1120 stats_.bw_limited_framerate = true;
1121 break;
1122 }
1123 }
1124 }
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001125 stats_.quality_limitation_reason =
1126 quality_limitation_reason_tracker_.current_reason();
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001127
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001128 // |stats_.quality_limitation_durations_ms| depends on the current time
1129 // when it is polled; it is updated in SendStatisticsProxy::GetStats().
asapersson09f05612017-05-15 23:40:18 -07001130}
1131
Evan Shrubsolecc62b162019-09-09 11:26:45 +02001132void SendStatisticsProxy::OnBitrateAllocationUpdated(
1133 const VideoCodec& codec,
1134 const VideoBitrateAllocation& allocation) {
1135 int num_spatial_layers = 0;
1136 for (int i = 0; i < kMaxSpatialLayers; i++) {
1137 if (codec.spatialLayers[i].active) {
1138 num_spatial_layers++;
1139 }
1140 }
1141 int num_simulcast_streams = 0;
1142 for (int i = 0; i < kMaxSimulcastStreams; i++) {
1143 if (codec.simulcastStream[i].active) {
1144 num_simulcast_streams++;
1145 }
1146 }
1147
1148 std::array<bool, kMaxSpatialLayers> spatial_layers;
1149 for (int i = 0; i < kMaxSpatialLayers; i++) {
1150 spatial_layers[i] = (allocation.GetSpatialLayerSum(i) > 0);
1151 }
1152
1153 rtc::CritScope lock(&crit_);
1154
Ilya Nikolaevskiy5963c7c2019-10-09 18:06:58 +02001155 bw_limited_layers_ = allocation.is_bw_limited();
1156 UpdateAdaptationStats();
1157
Evan Shrubsolecc62b162019-09-09 11:26:45 +02001158 if (spatial_layers != last_spatial_layer_use_) {
1159 // If the number of spatial layers has changed, the resolution change is
1160 // not due to quality limitations, it is because the configuration
1161 // changed.
1162 if (last_num_spatial_layers_ == num_spatial_layers &&
1163 last_num_simulcast_streams_ == num_simulcast_streams) {
1164 ++stats_.quality_limitation_resolution_changes;
1165 }
1166 last_spatial_layer_use_ = spatial_layers;
1167 }
1168 last_num_spatial_layers_ = num_spatial_layers;
1169 last_num_simulcast_streams_ = num_simulcast_streams;
1170}
1171
Åsa Persson875841d2018-01-08 08:49:53 +01001172// TODO(asapersson): Include fps changes.
1173void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
1174 rtc::CritScope lock(&crit_);
1175 ++uma_container_->initial_quality_changes_.down;
1176}
1177
1178void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
Niels Möller213618e2018-07-24 09:29:58 +02001179 const AdaptationSteps& quality_counts) {
Åsa Persson875841d2018-01-08 08:49:53 +01001180 if (uma_container_->initial_quality_changes_.down == 0)
1181 return;
1182
1183 if (quality_downscales_ > 0 &&
Niels Möller213618e2018-07-24 09:29:58 +02001184 quality_counts.num_resolution_reductions.value_or(-1) <
1185 quality_downscales_) {
Åsa Persson875841d2018-01-08 08:49:53 +01001186 // Adapting up in quality.
1187 if (uma_container_->initial_quality_changes_.down >
1188 uma_container_->initial_quality_changes_.up) {
1189 ++uma_container_->initial_quality_changes_.up;
1190 }
1191 }
1192}
1193
Niels Möller213618e2018-07-24 09:29:58 +02001194void SendStatisticsProxy::SetAdaptTimer(const AdaptationSteps& counts,
1195 StatsTimer* timer) {
1196 if (counts.num_resolution_reductions || counts.num_framerate_reductions) {
asapersson09f05612017-05-15 23:40:18 -07001197 // Adaptation enabled.
1198 if (!stats_.suspended)
1199 timer->Start(clock_->TimeInMilliseconds());
1200 return;
1201 }
1202 timer->Stop(clock_->TimeInMilliseconds());
kthelgason876222f2016-11-29 01:44:11 -08001203}
1204
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001205void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
1206 uint32_t ssrc,
1207 const RtcpPacketTypeCounter& packet_counter) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001208 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001209 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001210 if (!stats)
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001211 return;
1212
1213 stats->rtcp_packet_type_counts = packet_counter;
sprang07fb9be2016-02-24 07:55:00 -08001214 if (uma_container_->first_rtcp_stats_time_ms_ == -1)
1215 uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001216}
1217
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001218void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
1219 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001220 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001221 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001222 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001223 return;
1224
1225 stats->rtcp_stats = statistics;
Niels Möller77d3efc2019-08-01 15:09:51 +02001226 uma_container_->report_block_stats_.Store(ssrc, statistics);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001227}
1228
Henrik Boström87e3f9d2019-05-27 10:44:24 +02001229void SendStatisticsProxy::OnReportBlockDataUpdated(
1230 ReportBlockData report_block_data) {
1231 rtc::CritScope lock(&crit_);
1232 VideoSendStream::StreamStats* stats =
1233 GetStatsEntry(report_block_data.report_block().source_ssrc);
1234 if (!stats)
1235 return;
1236 stats->report_block_data = std::move(report_block_data);
1237}
1238
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001239void SendStatisticsProxy::DataCountersUpdated(
1240 const StreamDataCounters& counters,
1241 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001242 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001243 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
asapersson93e1e232017-02-06 05:18:35 -08001244 RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc " << ssrc;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001245
asaperssona6a699a2016-11-25 03:52:46 -08001246 if (stats->is_flexfec) {
1247 // The same counters are reported for both the media ssrc and flexfec ssrc.
1248 // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
1249 return;
1250 }
1251
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001252 stats->rtp_stats = counters;
asapersson6eca98b2017-04-04 23:40:50 -07001253 if (uma_container_->first_rtp_stats_time_ms_ == -1) {
1254 int64_t now_ms = clock_->TimeInMilliseconds();
1255 uma_container_->first_rtp_stats_time_ms_ = now_ms;
asapersson09f05612017-05-15 23:40:18 -07001256 uma_container_->cpu_adapt_timer_.Restart(now_ms);
1257 uma_container_->quality_adapt_timer_.Restart(now_ms);
asapersson6eca98b2017-04-04 23:40:50 -07001258 }
asapersson93e1e232017-02-06 05:18:35 -08001259
1260 uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),
1261 ssrc);
1262 uma_container_->padding_byte_counter_.Set(counters.transmitted.padding_bytes,
1263 ssrc);
1264 uma_container_->retransmit_byte_counter_.Set(
1265 counters.retransmitted.TotalBytes(), ssrc);
1266 uma_container_->fec_byte_counter_.Set(counters.fec.TotalBytes(), ssrc);
1267 if (stats->is_rtx) {
1268 uma_container_->rtx_byte_counter_.Set(counters.transmitted.TotalBytes(),
1269 ssrc);
1270 } else {
1271 uma_container_->media_byte_counter_.Set(counters.MediaPayloadBytes(), ssrc);
1272 }
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001273}
1274
sprangcd349d92016-07-13 09:11:28 -07001275void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
1276 uint32_t retransmit_bitrate_bps,
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001277 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001278 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001279 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001280 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001281 return;
1282
sprangcd349d92016-07-13 09:11:28 -07001283 stats->total_bitrate_bps = total_bitrate_bps;
1284 stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001285}
1286
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001287void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
1288 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001289 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001290 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001291 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001292 return;
1293
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001294 stats->frame_counts = frame_counts;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001295}
1296
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001297void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
1298 int max_delay_ms,
Henrik Boström9fe18342019-05-16 18:38:20 +02001299 uint64_t total_delay_ms,
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001300 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001301 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001302 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001303 if (!stats)
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001304 return;
1305 stats->avg_delay_ms = avg_delay_ms;
1306 stats->max_delay_ms = max_delay_ms;
Henrik Boström9fe18342019-05-16 18:38:20 +02001307 stats->total_packet_send_delay_ms = total_delay_ms;
asaperssonf040b232015-11-04 00:59:03 -08001308
sprangb4a1ae52015-12-03 08:10:08 -08001309 uma_container_->delay_counter_.Add(avg_delay_ms);
1310 uma_container_->max_delay_counter_.Add(max_delay_ms);
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001311}
1312
asapersson6eca98b2017-04-04 23:40:50 -07001313void SendStatisticsProxy::StatsTimer::Start(int64_t now_ms) {
1314 if (start_ms == -1)
1315 start_ms = now_ms;
1316}
1317
1318void SendStatisticsProxy::StatsTimer::Stop(int64_t now_ms) {
1319 if (start_ms != -1) {
1320 total_ms += now_ms - start_ms;
1321 start_ms = -1;
1322 }
1323}
1324
1325void SendStatisticsProxy::StatsTimer::Restart(int64_t now_ms) {
1326 total_ms = 0;
1327 if (start_ms != -1)
1328 start_ms = now_ms;
1329}
1330
asaperssond89920b2015-07-22 06:52:00 -07001331void SendStatisticsProxy::SampleCounter::Add(int sample) {
1332 sum += sample;
1333 ++num_samples;
1334}
1335
asapersson66d4b372016-12-19 06:50:53 -08001336int SendStatisticsProxy::SampleCounter::Avg(
1337 int64_t min_required_samples) const {
asaperssond89920b2015-07-22 06:52:00 -07001338 if (num_samples < min_required_samples || num_samples == 0)
1339 return -1;
asapersson66d4b372016-12-19 06:50:53 -08001340 return static_cast<int>((sum + (num_samples / 2)) / num_samples);
asaperssond89920b2015-07-22 06:52:00 -07001341}
1342
asaperssondec5ebf2015-10-05 02:36:17 -07001343void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
1344 if (sample)
1345 ++sum;
1346 ++num_samples;
1347}
1348
asapersson66d4b372016-12-19 06:50:53 -08001349void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
1350 if (sample)
1351 sum += count;
1352 num_samples += count;
1353}
asaperssondec5ebf2015-10-05 02:36:17 -07001354int SendStatisticsProxy::BoolSampleCounter::Percent(
asapersson66d4b372016-12-19 06:50:53 -08001355 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001356 return Fraction(min_required_samples, 100.0f);
1357}
1358
1359int SendStatisticsProxy::BoolSampleCounter::Permille(
asapersson66d4b372016-12-19 06:50:53 -08001360 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001361 return Fraction(min_required_samples, 1000.0f);
1362}
1363
1364int SendStatisticsProxy::BoolSampleCounter::Fraction(
asapersson66d4b372016-12-19 06:50:53 -08001365 int64_t min_required_samples,
1366 float multiplier) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001367 if (num_samples < min_required_samples || num_samples == 0)
1368 return -1;
1369 return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
1370}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001371} // namespace webrtc