blob: cf417f5c3a70eaf4b1fe5d0be359071cc7378f57 [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>
Tim Psiakiad13d2f2015-11-10 16:34:50 -080014#include <cmath>
Åsa Persson20317f92018-08-15 08:57:54 +020015#include <limits>
Åsa Perssonaa329e72017-12-15 15:54:44 +010016#include <utility>
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000017
Steve Antonbd631a02019-03-28 10:51:27 -070018#include "absl/algorithm/container.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "modules/video_coding/include/video_codec_interface.h"
20#include "rtc_base/checks.h"
21#include "rtc_base/logging.h"
Åsa Persson20317f92018-08-15 08:57:54 +020022#include "rtc_base/numerics/mod_ops.h"
Tommifef05002018-02-27 13:51:08 +010023#include "rtc_base/strings/string_builder.h"
asapersson8d75ac72017-09-15 06:41:15 -070024#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "system_wrappers/include/metrics.h"
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000026
27namespace webrtc {
asapersson2a0a2a42015-10-27 01:32:00 -070028namespace {
asapersson1aa420b2015-12-07 03:12:22 -080029const float kEncodeTimeWeigthFactor = 0.5f;
Åsa Persson0122e842017-10-16 12:19:23 +020030const size_t kMaxEncodedFrameMapSize = 150;
31const int64_t kMaxEncodedFrameWindowMs = 800;
Åsa Persson20317f92018-08-15 08:57:54 +020032const uint32_t kMaxEncodedFrameTimestampDiff = 900000; // 10 sec.
Åsa Persson0122e842017-10-16 12:19:23 +020033const int64_t kBucketSizeMs = 100;
34const size_t kBucketCount = 10;
asapersson1aa420b2015-12-07 03:12:22 -080035
asapersson8d75ac72017-09-15 06:41:15 -070036const char kVp8ForcedFallbackEncoderFieldTrial[] =
Åsa Persson45bbc8a2017-11-13 10:16:47 +010037 "WebRTC-VP8-Forced-Fallback-Encoder-v2";
asapersson8d75ac72017-09-15 06:41:15 -070038const char kVp8SwCodecName[] = "libvpx";
39
asapersson2a0a2a42015-10-27 01:32:00 -070040// Used by histograms. Values of entries should not be changed.
41enum HistogramCodecType {
42 kVideoUnknown = 0,
43 kVideoVp8 = 1,
44 kVideoVp9 = 2,
45 kVideoH264 = 3,
46 kVideoMax = 64,
47};
48
asaperssonc2148a52016-02-04 00:33:21 -080049const char* kRealtimePrefix = "WebRTC.Video.";
50const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
51
sprangb4a1ae52015-12-03 08:10:08 -080052const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
53 switch (content_type) {
54 case VideoEncoderConfig::ContentType::kRealtimeVideo:
asaperssonc2148a52016-02-04 00:33:21 -080055 return kRealtimePrefix;
sprangb4a1ae52015-12-03 08:10:08 -080056 case VideoEncoderConfig::ContentType::kScreen:
asaperssonc2148a52016-02-04 00:33:21 -080057 return kScreenPrefix;
sprangb4a1ae52015-12-03 08:10:08 -080058 }
59 RTC_NOTREACHED();
60 return nullptr;
61}
62
asapersson2a0a2a42015-10-27 01:32:00 -070063HistogramCodecType PayloadNameToHistogramCodecType(
64 const std::string& payload_name) {
kthelgason1cdddc92017-08-24 03:52:48 -070065 VideoCodecType codecType = PayloadStringToCodecType(payload_name);
66 switch (codecType) {
deadbeefc964d0b2017-04-03 10:03:35 -070067 case kVideoCodecVP8:
68 return kVideoVp8;
69 case kVideoCodecVP9:
70 return kVideoVp9;
71 case kVideoCodecH264:
72 return kVideoH264;
73 default:
74 return kVideoUnknown;
75 }
asapersson2a0a2a42015-10-27 01:32:00 -070076}
77
78void UpdateCodecTypeHistogram(const std::string& payload_name) {
asapersson28ba9272016-01-25 05:58:23 -080079 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
80 PayloadNameToHistogramCodecType(payload_name),
81 kVideoMax);
asapersson2a0a2a42015-10-27 01:32:00 -070082}
asapersson8d75ac72017-09-15 06:41:15 -070083
Niels Möllerd3b8c632018-08-27 15:33:42 +020084bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info,
85 int simulcast_index) {
86 return codec_info->codecType == kVideoCodecVP8 && simulcast_index == 0 &&
asapersson8d75ac72017-09-15 06:41:15 -070087 (codec_info->codecSpecific.VP8.temporalIdx == 0 ||
88 codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx);
89}
90
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020091absl::optional<int> GetFallbackMaxPixels(const std::string& group) {
asapersson8d75ac72017-09-15 06:41:15 -070092 if (group.empty())
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020093 return absl::nullopt;
asapersson8d75ac72017-09-15 06:41:15 -070094
asapersson8d75ac72017-09-15 06:41:15 -070095 int min_pixels;
Åsa Persson45bbc8a2017-11-13 10:16:47 +010096 int max_pixels;
97 int min_bps;
Åsa Perssonc3ed6302017-11-16 14:04:52 +010098 if (sscanf(group.c_str(), "-%d,%d,%d", &min_pixels, &max_pixels, &min_bps) !=
99 3) {
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200100 return absl::optional<int>();
asapersson8d75ac72017-09-15 06:41:15 -0700101 }
102
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100103 if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels)
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200104 return absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100105
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200106 return absl::optional<int>(max_pixels);
asapersson8d75ac72017-09-15 06:41:15 -0700107}
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100108
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200109absl::optional<int> GetFallbackMaxPixelsIfFieldTrialEnabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100110 std::string group =
111 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
112 return (group.find("Enabled") == 0) ? GetFallbackMaxPixels(group.substr(7))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200113 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100114}
115
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200116absl::optional<int> GetFallbackMaxPixelsIfFieldTrialDisabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100117 std::string group =
118 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
119 return (group.find("Disabled") == 0) ? GetFallbackMaxPixels(group.substr(8))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200120 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100121}
asapersson2a0a2a42015-10-27 01:32:00 -0700122} // namespace
123
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000124const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
125
sprangb4a1ae52015-12-03 08:10:08 -0800126SendStatisticsProxy::SendStatisticsProxy(
127 Clock* clock,
128 const VideoSendStream::Config& config,
129 VideoEncoderConfig::ContentType content_type)
asaperssond89920b2015-07-22 06:52:00 -0700130 : clock_(clock),
Niels Möller259a4972018-04-05 15:36:51 +0200131 payload_name_(config.rtp.payload_name),
perkj26091b12016-09-01 01:17:40 -0700132 rtp_config_(config.rtp),
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100133 fallback_max_pixels_(GetFallbackMaxPixelsIfFieldTrialEnabled()),
134 fallback_max_pixels_disabled_(GetFallbackMaxPixelsIfFieldTrialDisabled()),
sprangb4a1ae52015-12-03 08:10:08 -0800135 content_type_(content_type),
asapersson4374a092016-07-27 00:39:09 -0700136 start_ms_(clock->TimeInMilliseconds()),
asapersson1aa420b2015-12-07 03:12:22 -0800137 encode_time_(kEncodeTimeWeigthFactor),
asapersson0944a802017-04-07 00:57:58 -0700138 quality_downscales_(-1),
139 cpu_downscales_(-1),
Henrik Boströmce33b6a2019-05-28 17:42:38 +0200140 quality_limitation_reason_tracker_(clock_),
Åsa Persson0122e842017-10-16 12:19:23 +0200141 media_byte_rate_tracker_(kBucketSizeMs, kBucketCount),
142 encoded_frame_rate_tracker_(kBucketSizeMs, kBucketCount),
sprang07fb9be2016-02-24 07:55:00 -0800143 uma_container_(
144 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000145}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000146
sprang07fb9be2016-02-24 07:55:00 -0800147SendStatisticsProxy::~SendStatisticsProxy() {
148 rtc::CritScope lock(&crit_);
perkj26091b12016-09-01 01:17:40 -0700149 uma_container_->UpdateHistograms(rtp_config_, stats_);
asapersson4374a092016-07-27 00:39:09 -0700150
151 int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
asapersson1d02d3e2016-09-09 22:40:25 -0700152 RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
153 elapsed_sec);
asapersson4374a092016-07-27 00:39:09 -0700154
155 if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
perkj26091b12016-09-01 01:17:40 -0700156 UpdateCodecTypeHistogram(payload_name_);
sprang07fb9be2016-02-24 07:55:00 -0800157}
sprangb4a1ae52015-12-03 08:10:08 -0800158
Mirko Bonadei8fdcac32018-08-28 16:30:18 +0200159SendStatisticsProxy::FallbackEncoderInfo::FallbackEncoderInfo() = default;
160
sprangb4a1ae52015-12-03 08:10:08 -0800161SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
sprang07fb9be2016-02-24 07:55:00 -0800162 const char* prefix,
163 const VideoSendStream::Stats& stats,
164 Clock* const clock)
sprangb4a1ae52015-12-03 08:10:08 -0800165 : uma_prefix_(prefix),
sprang07fb9be2016-02-24 07:55:00 -0800166 clock_(clock),
Honghai Zhang82d78622016-05-06 11:29:15 -0700167 input_frame_rate_tracker_(100, 10u),
asapersson320e45a2016-11-29 01:40:35 -0800168 input_fps_counter_(clock, nullptr, true),
169 sent_fps_counter_(clock, nullptr, true),
asapersson93e1e232017-02-06 05:18:35 -0800170 total_byte_counter_(clock, nullptr, true),
171 media_byte_counter_(clock, nullptr, true),
172 rtx_byte_counter_(clock, nullptr, true),
173 padding_byte_counter_(clock, nullptr, true),
174 retransmit_byte_counter_(clock, nullptr, true),
175 fec_byte_counter_(clock, nullptr, true),
sprang07fb9be2016-02-24 07:55:00 -0800176 first_rtcp_stats_time_ms_(-1),
Erik Språng22c2b482016-03-01 09:40:42 +0100177 first_rtp_stats_time_ms_(-1),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100178 start_stats_(stats),
179 num_streams_(0),
180 num_pixels_highest_stream_(0) {
asapersson93e1e232017-02-06 05:18:35 -0800181 InitializeBitrateCounters(stats);
Åsa Persson20317f92018-08-15 08:57:54 +0200182 static_assert(
183 kMaxEncodedFrameTimestampDiff < std::numeric_limits<uint32_t>::max() / 2,
184 "has to be smaller than half range");
asapersson93e1e232017-02-06 05:18:35 -0800185}
sprangb4a1ae52015-12-03 08:10:08 -0800186
sprang07fb9be2016-02-24 07:55:00 -0800187SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
Åsa Persson24b4eda2015-06-16 10:17:01 +0200188
asapersson93e1e232017-02-06 05:18:35 -0800189void SendStatisticsProxy::UmaSamplesContainer::InitializeBitrateCounters(
190 const VideoSendStream::Stats& stats) {
191 for (const auto& it : stats.substreams) {
192 uint32_t ssrc = it.first;
193 total_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
194 ssrc);
195 padding_byte_counter_.SetLast(it.second.rtp_stats.transmitted.padding_bytes,
196 ssrc);
197 retransmit_byte_counter_.SetLast(
198 it.second.rtp_stats.retransmitted.TotalBytes(), ssrc);
199 fec_byte_counter_.SetLast(it.second.rtp_stats.fec.TotalBytes(), ssrc);
200 if (it.second.is_rtx) {
201 rtx_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
202 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100203 } else {
asapersson93e1e232017-02-06 05:18:35 -0800204 media_byte_counter_.SetLast(it.second.rtp_stats.MediaPayloadBytes(),
205 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100206 }
207 }
208}
209
Åsa Perssonaa329e72017-12-15 15:54:44 +0100210void SendStatisticsProxy::UmaSamplesContainer::RemoveOld(
211 int64_t now_ms,
212 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200213 while (!encoded_frames_.empty()) {
214 auto it = encoded_frames_.begin();
215 if (now_ms - it->second.send_ms < kMaxEncodedFrameWindowMs)
216 break;
217
218 // Use max per timestamp.
219 sent_width_counter_.Add(it->second.max_width);
220 sent_height_counter_.Add(it->second.max_height);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100221
222 // Check number of encoded streams per timestamp.
Niels Möllerd3b8c632018-08-27 15:33:42 +0200223 if (num_streams_ > static_cast<size_t>(it->second.max_simulcast_idx)) {
Åsa Perssonaa329e72017-12-15 15:54:44 +0100224 *is_limited_in_resolution = false;
225 if (num_streams_ > 1) {
226 int disabled_streams =
227 static_cast<int>(num_streams_ - 1 - it->second.max_simulcast_idx);
228 // Can be limited in resolution or framerate.
229 uint32_t pixels = it->second.max_width * it->second.max_height;
230 bool bw_limited_resolution =
231 disabled_streams > 0 && pixels < num_pixels_highest_stream_;
232 bw_limited_frame_counter_.Add(bw_limited_resolution);
233 if (bw_limited_resolution) {
234 bw_resolutions_disabled_counter_.Add(disabled_streams);
235 *is_limited_in_resolution = true;
236 }
237 }
238 }
Åsa Persson0122e842017-10-16 12:19:23 +0200239 encoded_frames_.erase(it);
240 }
241}
242
243bool SendStatisticsProxy::UmaSamplesContainer::InsertEncodedFrame(
Åsa Perssonaa329e72017-12-15 15:54:44 +0100244 const EncodedImage& encoded_frame,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200245 int simulcast_idx,
Åsa Perssonaa329e72017-12-15 15:54:44 +0100246 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200247 int64_t now_ms = clock_->TimeInMilliseconds();
Åsa Perssonaa329e72017-12-15 15:54:44 +0100248 RemoveOld(now_ms, is_limited_in_resolution);
Åsa Persson0122e842017-10-16 12:19:23 +0200249 if (encoded_frames_.size() > kMaxEncodedFrameMapSize) {
250 encoded_frames_.clear();
251 }
252
Åsa Persson20317f92018-08-15 08:57:54 +0200253 // Check for jump in timestamp.
254 if (!encoded_frames_.empty()) {
255 uint32_t oldest_timestamp = encoded_frames_.begin()->first;
Niels Möller23775882018-08-16 10:24:12 +0200256 if (ForwardDiff(oldest_timestamp, encoded_frame.Timestamp()) >
Åsa Persson20317f92018-08-15 08:57:54 +0200257 kMaxEncodedFrameTimestampDiff) {
258 // Gap detected, clear frames to have a sequence where newest timestamp
259 // is not too far away from oldest in order to distinguish old and new.
260 encoded_frames_.clear();
261 }
262 }
263
Niels Möller72bc8d62018-09-12 10:03:51 +0200264 auto it = encoded_frames_.find(encoded_frame.Timestamp());
Åsa Persson0122e842017-10-16 12:19:23 +0200265 if (it == encoded_frames_.end()) {
266 // First frame with this timestamp.
Åsa Perssonaa329e72017-12-15 15:54:44 +0100267 encoded_frames_.insert(
Niels Möller23775882018-08-16 10:24:12 +0200268 std::make_pair(encoded_frame.Timestamp(),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100269 Frame(now_ms, encoded_frame._encodedWidth,
270 encoded_frame._encodedHeight, simulcast_idx)));
Åsa Persson0122e842017-10-16 12:19:23 +0200271 sent_fps_counter_.Add(1);
272 return true;
273 }
274
275 it->second.max_width =
276 std::max(it->second.max_width, encoded_frame._encodedWidth);
277 it->second.max_height =
278 std::max(it->second.max_height, encoded_frame._encodedHeight);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100279 it->second.max_simulcast_idx =
280 std::max(it->second.max_simulcast_idx, simulcast_idx);
Åsa Persson0122e842017-10-16 12:19:23 +0200281 return false;
282}
283
sprang07fb9be2016-02-24 07:55:00 -0800284void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
Stefan Holmerdbdb3a02018-07-17 16:03:46 +0200285 const RtpConfig& rtp_config,
sprang07fb9be2016-02-24 07:55:00 -0800286 const VideoSendStream::Stats& current_stats) {
asaperssonc2148a52016-02-04 00:33:21 -0800287 RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
288 const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
asapersson320e45a2016-11-29 01:40:35 -0800289 const int kMinRequiredPeriodicSamples = 6;
Karl Wiberg881f1682018-03-08 15:03:23 +0100290 char log_stream_buf[8 * 1024];
291 rtc::SimpleStringBuilder log_stream(log_stream_buf);
perkj803d97f2016-11-01 11:45:46 -0700292 int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
293 int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700294 if (in_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700295 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
296 in_width);
297 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
298 in_height);
Tommifef05002018-02-27 13:51:08 +0100299 log_stream << uma_prefix_ << "InputWidthInPixels " << in_width << "\n"
300 << uma_prefix_ << "InputHeightInPixels " << in_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700301 }
asapersson320e45a2016-11-29 01:40:35 -0800302 AggregatedStats in_fps = input_fps_counter_.GetStats();
303 if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
304 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
305 in_fps.average);
Tommifef05002018-02-27 13:51:08 +0100306 log_stream << uma_prefix_ << "InputFramesPerSecond " << in_fps.ToString()
307 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800308 }
309
perkj803d97f2016-11-01 11:45:46 -0700310 int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
311 int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700312 if (sent_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700313 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
314 sent_width);
315 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
316 sent_height);
Tommifef05002018-02-27 13:51:08 +0100317 log_stream << uma_prefix_ << "SentWidthInPixels " << sent_width << "\n"
318 << uma_prefix_ << "SentHeightInPixels " << sent_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700319 }
asapersson320e45a2016-11-29 01:40:35 -0800320 AggregatedStats sent_fps = sent_fps_counter_.GetStats();
321 if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
322 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
323 sent_fps.average);
Tommifef05002018-02-27 13:51:08 +0100324 log_stream << uma_prefix_ << "SentFramesPerSecond " << sent_fps.ToString()
325 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800326 }
327
ilnikf4ded682017-08-30 02:30:20 -0700328 if (in_fps.num_samples > kMinRequiredPeriodicSamples &&
329 sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
330 int in_fps_avg = in_fps.average;
331 if (in_fps_avg > 0) {
332 int sent_fps_avg = sent_fps.average;
333 int sent_to_in_fps_ratio_percent =
334 (100 * sent_fps_avg + in_fps_avg / 2) / in_fps_avg;
335 // If reported period is small, it may happen that sent_fps is larger than
336 // input_fps briefly on average. This should be treated as 100% sent to
337 // input ratio.
338 if (sent_to_in_fps_ratio_percent > 100)
339 sent_to_in_fps_ratio_percent = 100;
340 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
341 uma_prefix_ + "SentToInputFpsRatioPercent",
342 sent_to_in_fps_ratio_percent);
Tommifef05002018-02-27 13:51:08 +0100343 log_stream << uma_prefix_ << "SentToInputFpsRatioPercent "
344 << sent_to_in_fps_ratio_percent << "\n";
ilnikf4ded682017-08-30 02:30:20 -0700345 }
346 }
347
perkj803d97f2016-11-01 11:45:46 -0700348 int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonc2148a52016-02-04 00:33:21 -0800349 if (encode_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700350 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
351 encode_ms);
Tommifef05002018-02-27 13:51:08 +0100352 log_stream << uma_prefix_ << "EncodeTimeInMs " << encode_ms << "\n";
asaperssonc2148a52016-02-04 00:33:21 -0800353 }
perkj803d97f2016-11-01 11:45:46 -0700354 int key_frames_permille =
355 key_frame_counter_.Permille(kMinRequiredMetricsSamples);
asaperssondec5ebf2015-10-05 02:36:17 -0700356 if (key_frames_permille != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700357 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
358 key_frames_permille);
Tommifef05002018-02-27 13:51:08 +0100359 log_stream << uma_prefix_ << "KeyFramesSentInPermille "
360 << key_frames_permille << "\n";
asaperssondec5ebf2015-10-05 02:36:17 -0700361 }
asapersson4306fc72015-10-19 00:35:21 -0700362 int quality_limited =
perkj803d97f2016-11-01 11:45:46 -0700363 quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700364 if (quality_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700365 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
366 uma_prefix_ + "QualityLimitedResolutionInPercent",
367 quality_limited);
Tommifef05002018-02-27 13:51:08 +0100368 log_stream << uma_prefix_ << "QualityLimitedResolutionInPercent "
369 << quality_limited << "\n";
asapersson4306fc72015-10-19 00:35:21 -0700370 }
perkj803d97f2016-11-01 11:45:46 -0700371 int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700372 if (downscales != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700373 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800374 kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
375 20);
asapersson4306fc72015-10-19 00:35:21 -0700376 }
perkj803d97f2016-11-01 11:45:46 -0700377 int cpu_limited =
378 cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
379 if (cpu_limited != -1) {
380 RTC_HISTOGRAMS_PERCENTAGE(
381 kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
382 }
383 int bw_limited =
384 bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700385 if (bw_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700386 RTC_HISTOGRAMS_PERCENTAGE(
asaperssonc2148a52016-02-04 00:33:21 -0800387 kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
388 bw_limited);
asaperssonda535c42015-10-19 23:32:41 -0700389 }
perkj803d97f2016-11-01 11:45:46 -0700390 int num_disabled =
391 bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700392 if (num_disabled != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700393 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800394 kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
395 num_disabled, 10);
asaperssonda535c42015-10-19 23:32:41 -0700396 }
perkj803d97f2016-11-01 11:45:46 -0700397 int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800398 if (delay_ms != -1)
asapersson1d02d3e2016-09-09 22:40:25 -0700399 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
400 delay_ms);
asaperssonf040b232015-11-04 00:59:03 -0800401
perkj803d97f2016-11-01 11:45:46 -0700402 int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800403 if (max_delay_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700404 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
405 max_delay_ms);
sprangb4a1ae52015-12-03 08:10:08 -0800406 }
sprang07fb9be2016-02-24 07:55:00 -0800407
asapersson118ef002016-03-31 00:00:19 -0700408 for (const auto& it : qp_counters_) {
perkj803d97f2016-11-01 11:45:46 -0700409 int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700410 if (qp_vp8 != -1) {
asapersson118ef002016-03-31 00:00:19 -0700411 int spatial_idx = it.first;
412 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700413 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
414 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700415 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700416 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
417 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700418 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700419 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
420 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700421 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700422 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
423 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700424 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100425 RTC_LOG(LS_WARNING)
426 << "QP stats not recorded for VP8 spatial idx " << spatial_idx;
asapersson118ef002016-03-31 00:00:19 -0700427 }
428 }
perkj803d97f2016-11-01 11:45:46 -0700429 int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700430 if (qp_vp9 != -1) {
431 int spatial_idx = it.first;
432 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700433 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
434 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700435 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700436 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
437 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700438 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700439 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
440 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700441 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700442 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
443 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700444 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100445 RTC_LOG(LS_WARNING)
446 << "QP stats not recorded for VP9 spatial layer " << spatial_idx;
asapersson5265fed2016-04-18 02:58:47 -0700447 }
448 }
asapersson827cab32016-11-02 09:08:47 -0700449 int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
450 if (qp_h264 != -1) {
451 int spatial_idx = it.first;
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200452 if (spatial_idx == -1) {
453 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264",
454 qp_h264);
455 } else if (spatial_idx == 0) {
456 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S0",
457 qp_h264);
458 } else if (spatial_idx == 1) {
459 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S1",
460 qp_h264);
461 } else if (spatial_idx == 2) {
462 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S2",
463 qp_h264);
464 } else {
465 RTC_LOG(LS_WARNING)
466 << "QP stats not recorded for H264 spatial idx " << spatial_idx;
467 }
asapersson827cab32016-11-02 09:08:47 -0700468 }
asapersson118ef002016-03-31 00:00:19 -0700469 }
470
asapersson0944a802017-04-07 00:57:58 -0700471 if (first_rtp_stats_time_ms_ != -1) {
asapersson09f05612017-05-15 23:40:18 -0700472 quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
473 int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700474 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
475 int quality_changes = current_stats.number_of_quality_adapt_changes -
476 start_stats_.number_of_quality_adapt_changes;
Åsa Persson875841d2018-01-08 08:49:53 +0100477 // Only base stats on changes during a call, discard initial changes.
478 int initial_changes =
479 initial_quality_changes_.down + initial_quality_changes_.up;
480 if (initial_changes <= quality_changes)
481 quality_changes -= initial_changes;
asapersson0944a802017-04-07 00:57:58 -0700482 RTC_HISTOGRAMS_COUNTS_100(kIndex,
483 uma_prefix_ + "AdaptChangesPerMinute.Quality",
484 quality_changes * 60 / elapsed_sec);
485 }
asapersson09f05612017-05-15 23:40:18 -0700486 cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
487 elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700488 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
489 int cpu_changes = current_stats.number_of_cpu_adapt_changes -
490 start_stats_.number_of_cpu_adapt_changes;
491 RTC_HISTOGRAMS_COUNTS_100(kIndex,
492 uma_prefix_ + "AdaptChangesPerMinute.Cpu",
493 cpu_changes * 60 / elapsed_sec);
494 }
asapersson6eca98b2017-04-04 23:40:50 -0700495 }
496
sprange2d83d62016-02-19 09:03:26 -0800497 if (first_rtcp_stats_time_ms_ != -1) {
sprang07fb9be2016-02-24 07:55:00 -0800498 int64_t elapsed_sec =
499 (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
500 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
501 int fraction_lost = report_block_stats_.FractionLostInPercent();
502 if (fraction_lost != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700503 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800504 kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
Tommifef05002018-02-27 13:51:08 +0100505 log_stream << uma_prefix_ << "SentPacketsLostInPercent "
Stefan Holmer0a5792e2018-10-05 13:47:12 +0200506 << fraction_lost << "\n";
sprang07fb9be2016-02-24 07:55:00 -0800507 }
508
509 // The RTCP packet type counters, delivered via the
510 // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
511 // life of the send stream and are not reset when switching content type.
512 // For the purpose of these statistics though, we want new counts when
513 // switching since we switch histogram name. On every reset of the
514 // UmaSamplesContainer, we save the initial state of the counters, so that
515 // we can calculate the delta here and aggregate over all ssrcs.
516 RtcpPacketTypeCounter counters;
perkj26091b12016-09-01 01:17:40 -0700517 for (uint32_t ssrc : rtp_config.ssrcs) {
sprang07fb9be2016-02-24 07:55:00 -0800518 auto kv = current_stats.substreams.find(ssrc);
519 if (kv == current_stats.substreams.end())
520 continue;
521
522 RtcpPacketTypeCounter stream_counters =
523 kv->second.rtcp_packet_type_counts;
524 kv = start_stats_.substreams.find(ssrc);
525 if (kv != start_stats_.substreams.end())
526 stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
527
528 counters.Add(stream_counters);
529 }
asapersson1d02d3e2016-09-09 22:40:25 -0700530 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
531 uma_prefix_ + "NackPacketsReceivedPerMinute",
532 counters.nack_packets * 60 / elapsed_sec);
533 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
534 uma_prefix_ + "FirPacketsReceivedPerMinute",
535 counters.fir_packets * 60 / elapsed_sec);
536 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
537 uma_prefix_ + "PliPacketsReceivedPerMinute",
538 counters.pli_packets * 60 / elapsed_sec);
sprang07fb9be2016-02-24 07:55:00 -0800539 if (counters.nack_requests > 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700540 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800541 kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
542 counters.UniqueNackRequestsInPercent());
543 }
sprange2d83d62016-02-19 09:03:26 -0800544 }
545 }
Erik Språng22c2b482016-03-01 09:40:42 +0100546
547 if (first_rtp_stats_time_ms_ != -1) {
548 int64_t elapsed_sec =
549 (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
550 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
asapersson66d4b372016-12-19 06:50:53 -0800551 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
552 target_rate_updates_.pause_resume_events);
Tommifef05002018-02-27 13:51:08 +0100553 log_stream << uma_prefix_ << "NumberOfPauseEvents "
554 << target_rate_updates_.pause_resume_events << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800555
556 int paused_time_percent =
557 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
558 if (paused_time_percent != -1) {
559 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
560 paused_time_percent);
Tommifef05002018-02-27 13:51:08 +0100561 log_stream << uma_prefix_ << "PausedTimeInPercent "
562 << paused_time_percent << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800563 }
asapersson93e1e232017-02-06 05:18:35 -0800564 }
565 }
asapersson66d4b372016-12-19 06:50:53 -0800566
asapersson8d75ac72017-09-15 06:41:15 -0700567 if (fallback_info_.is_possible) {
568 // Double interval since there is some time before fallback may occur.
569 const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000;
570 int64_t elapsed_ms = fallback_info_.elapsed_ms;
571 int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs);
572 if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) {
573 RTC_HISTOGRAMS_PERCENTAGE(
574 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8",
575 fallback_time_percent);
576 RTC_HISTOGRAMS_COUNTS_100(
577 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8",
578 fallback_info_.on_off_events * 60 / (elapsed_ms / 1000));
579 }
580 }
581
asapersson93e1e232017-02-06 05:18:35 -0800582 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats();
583 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
584 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps",
585 total_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100586 log_stream << uma_prefix_ << "BitrateSentInBps "
587 << total_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800588 }
589 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats();
590 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
591 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
592 media_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100593 log_stream << uma_prefix_ << "MediaBitrateSentInBps "
594 << media_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800595 }
596 AggregatedStats padding_bytes_per_sec = padding_byte_counter_.GetStats();
597 if (padding_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
598 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
599 uma_prefix_ + "PaddingBitrateSentInKbps",
600 padding_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100601 log_stream << uma_prefix_ << "PaddingBitrateSentInBps "
602 << padding_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800603 }
604 AggregatedStats retransmit_bytes_per_sec =
605 retransmit_byte_counter_.GetStats();
606 if (retransmit_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
607 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
608 uma_prefix_ + "RetransmittedBitrateSentInKbps",
609 retransmit_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100610 log_stream << uma_prefix_ << "RetransmittedBitrateSentInBps "
611 << retransmit_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800612 }
613 if (!rtp_config.rtx.ssrcs.empty()) {
614 AggregatedStats rtx_bytes_per_sec = rtx_byte_counter_.GetStats();
615 int rtx_bytes_per_sec_avg = -1;
616 if (rtx_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
617 rtx_bytes_per_sec_avg = rtx_bytes_per_sec.average;
Tommifef05002018-02-27 13:51:08 +0100618 log_stream << uma_prefix_ << "RtxBitrateSentInBps "
619 << rtx_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800620 } else if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
621 rtx_bytes_per_sec_avg = 0; // RTX enabled but no RTX data sent, record 0.
622 }
623 if (rtx_bytes_per_sec_avg != -1) {
624 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
625 rtx_bytes_per_sec_avg * 8 / 1000);
626 }
627 }
628 if (rtp_config.flexfec.payload_type != -1 ||
629 rtp_config.ulpfec.red_payload_type != -1) {
630 AggregatedStats fec_bytes_per_sec = fec_byte_counter_.GetStats();
631 if (fec_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
632 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "FecBitrateSentInKbps",
633 fec_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100634 log_stream << uma_prefix_ << "FecBitrateSentInBps "
635 << fec_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
Erik Språng22c2b482016-03-01 09:40:42 +0100636 }
637 }
Tommifef05002018-02-27 13:51:08 +0100638 log_stream << "Frames encoded " << current_stats.frames_encoded << "\n"
639 << uma_prefix_ << "DroppedFrames.Capturer "
640 << current_stats.frames_dropped_by_capturer << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200641 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
642 current_stats.frames_dropped_by_capturer);
Tommifef05002018-02-27 13:51:08 +0100643 log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
644 << current_stats.frames_dropped_by_encoder_queue << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200645 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
646 current_stats.frames_dropped_by_encoder_queue);
Tommifef05002018-02-27 13:51:08 +0100647 log_stream << uma_prefix_ << "DroppedFrames.Encoder "
648 << current_stats.frames_dropped_by_encoder << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200649 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Encoder",
650 current_stats.frames_dropped_by_encoder);
Tommifef05002018-02-27 13:51:08 +0100651 log_stream << uma_prefix_ << "DroppedFrames.Ratelimiter "
652 << current_stats.frames_dropped_by_rate_limiter;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200653 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Ratelimiter",
654 current_stats.frames_dropped_by_rate_limiter);
Jonas Olsson694a36f2018-02-16 13:09:41 +0100655
Tommifef05002018-02-27 13:51:08 +0100656 RTC_LOG(LS_INFO) << log_stream.str();
sprangb4a1ae52015-12-03 08:10:08 -0800657}
658
Pera48ddb72016-09-29 11:48:50 +0200659void SendStatisticsProxy::OnEncoderReconfigured(
660 const VideoEncoderConfig& config,
Niels Möller97e04882018-05-25 09:43:26 +0200661 const std::vector<VideoStream>& streams) {
sprangb4a1ae52015-12-03 08:10:08 -0800662 rtc::CritScope lock(&crit_);
Pera48ddb72016-09-29 11:48:50 +0200663
664 if (content_type_ != config.content_type) {
perkj26091b12016-09-01 01:17:40 -0700665 uma_container_->UpdateHistograms(rtp_config_, stats_);
Pera48ddb72016-09-29 11:48:50 +0200666 uma_container_.reset(new UmaSamplesContainer(
667 GetUmaPrefix(config.content_type), stats_, clock_));
668 content_type_ = config.content_type;
asaperssonf040b232015-11-04 00:59:03 -0800669 }
Åsa Perssonaa329e72017-12-15 15:54:44 +0100670 uma_container_->encoded_frames_.clear();
671 uma_container_->num_streams_ = streams.size();
672 uma_container_->num_pixels_highest_stream_ =
673 streams.empty() ? 0 : (streams.back().width * streams.back().height);
Åsa Persson24b4eda2015-06-16 10:17:01 +0200674}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000675
Niels Möller213618e2018-07-24 09:29:58 +0200676void SendStatisticsProxy::OnEncodedFrameTimeMeasured(int encode_time_ms,
677 int encode_usage_percent) {
Henrik Boström5684af52019-04-02 15:05:21 +0200678 RTC_DCHECK_GE(encode_time_ms, 0);
Peter Boströmf2f82832015-05-01 13:00:41 +0200679 rtc::CritScope lock(&crit_);
Peter Boströme4499152016-02-05 11:13:28 +0100680 uma_container_->encode_time_counter_.Add(encode_time_ms);
681 encode_time_.Apply(1.0f, encode_time_ms);
Oleh Prypin19929582019-04-23 08:50:04 +0200682 stats_.avg_encode_time_ms = std::round(encode_time_.filtered());
Henrik Boström5684af52019-04-02 15:05:21 +0200683 stats_.total_encode_time_ms += encode_time_ms;
Niels Möller213618e2018-07-24 09:29:58 +0200684 stats_.encode_usage_percent = encode_usage_percent;
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000685}
686
Peter Boström7083e112015-09-22 16:28:51 +0200687void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
asapersson0944a802017-04-07 00:57:58 -0700688 int64_t now_ms = clock_->TimeInMilliseconds();
Peter Boströmf2f82832015-05-01 13:00:41 +0200689 rtc::CritScope lock(&crit_);
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000690 stats_.suspended = is_suspended;
asapersson320e45a2016-11-29 01:40:35 -0800691 if (is_suspended) {
asapersson93e1e232017-02-06 05:18:35 -0800692 // Pause framerate (add min pause time since there may be frames/packets
693 // that are not yet sent).
694 const int64_t kMinMs = 500;
695 uma_container_->input_fps_counter_.ProcessAndPauseForDuration(kMinMs);
696 uma_container_->sent_fps_counter_.ProcessAndPauseForDuration(kMinMs);
697 // Pause bitrate stats.
698 uma_container_->total_byte_counter_.ProcessAndPauseForDuration(kMinMs);
699 uma_container_->media_byte_counter_.ProcessAndPauseForDuration(kMinMs);
700 uma_container_->rtx_byte_counter_.ProcessAndPauseForDuration(kMinMs);
701 uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
702 uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
703 uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
asapersson0944a802017-04-07 00:57:58 -0700704 // Stop adaptation stats.
asapersson09f05612017-05-15 23:40:18 -0700705 uma_container_->cpu_adapt_timer_.Stop(now_ms);
706 uma_container_->quality_adapt_timer_.Stop(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800707 } else {
asapersson0944a802017-04-07 00:57:58 -0700708 // Start adaptation stats if scaling is enabled.
709 if (cpu_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700710 uma_container_->cpu_adapt_timer_.Start(now_ms);
asapersson0944a802017-04-07 00:57:58 -0700711 if (quality_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700712 uma_container_->quality_adapt_timer_.Start(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800713 // Stop pause explicitly for stats that may be zero/not updated for some
714 // time.
715 uma_container_->rtx_byte_counter_.ProcessAndStopPause();
716 uma_container_->padding_byte_counter_.ProcessAndStopPause();
717 uma_container_->retransmit_byte_counter_.ProcessAndStopPause();
718 uma_container_->fec_byte_counter_.ProcessAndStopPause();
asapersson320e45a2016-11-29 01:40:35 -0800719 }
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000720}
721
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000722VideoSendStream::Stats SendStatisticsProxy::GetStats() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200723 rtc::CritScope lock(&crit_);
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000724 PurgeOldStats();
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000725 stats_.input_frame_rate =
sprangb4a1ae52015-12-03 08:10:08 -0800726 round(uma_container_->input_frame_rate_tracker_.ComputeRate());
ilnik50864a82017-09-06 12:32:35 -0700727 stats_.content_type =
728 content_type_ == VideoEncoderConfig::ContentType::kRealtimeVideo
729 ? VideoContentType::UNSPECIFIED
730 : VideoContentType::SCREENSHARE;
Åsa Persson0122e842017-10-16 12:19:23 +0200731 stats_.encode_frame_rate = round(encoded_frame_rate_tracker_.ComputeRate());
732 stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
Henrik Boströmce33b6a2019-05-28 17:42:38 +0200733 stats_.quality_limitation_durations_ms =
734 quality_limitation_reason_tracker_.DurationsMs();
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000735 return stats_;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000736}
737
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000738void SendStatisticsProxy::PurgeOldStats() {
Peter Boström20f3f942015-05-15 11:33:39 +0200739 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000740 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
741 stats_.substreams.begin();
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000742 it != stats_.substreams.end(); ++it) {
743 uint32_t ssrc = it->first;
Peter Boström20f3f942015-05-15 11:33:39 +0200744 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
745 it->second.width = 0;
746 it->second.height = 0;
747 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000748 }
749}
750
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000751VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
752 uint32_t ssrc) {
753 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
754 stats_.substreams.find(ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000755 if (it != stats_.substreams.end())
756 return &it->second;
757
Steve Antonbd631a02019-03-28 10:51:27 -0700758 bool is_media = absl::c_linear_search(rtp_config_.ssrcs, ssrc);
brandtr3d200bd2017-01-16 06:59:19 -0800759 bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
760 ssrc == rtp_config_.flexfec.ssrc;
Steve Antonbd631a02019-03-28 10:51:27 -0700761 bool is_rtx = absl::c_linear_search(rtp_config_.rtx.ssrcs, ssrc);
brandtrcd188f62016-11-15 08:21:52 -0800762 if (!is_media && !is_flexfec && !is_rtx)
763 return nullptr;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000764
asapersson2e5cfcd2016-08-11 08:41:18 -0700765 // Insert new entry and return ptr.
766 VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
767 entry->is_rtx = is_rtx;
asaperssona6a699a2016-11-25 03:52:46 -0800768 entry->is_flexfec = is_flexfec;
asapersson2e5cfcd2016-08-11 08:41:18 -0700769
770 return entry;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000771}
772
Peter Boström20f3f942015-05-15 11:33:39 +0200773void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
774 rtc::CritScope lock(&crit_);
775 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200776 if (!stats)
Peter Boström20f3f942015-05-15 11:33:39 +0200777 return;
778
779 stats->total_bitrate_bps = 0;
780 stats->retransmit_bitrate_bps = 0;
781 stats->height = 0;
782 stats->width = 0;
783}
784
perkjf5b2e512016-07-05 08:34:04 -0700785void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200786 rtc::CritScope lock(&crit_);
asapersson66d4b372016-12-19 06:50:53 -0800787 if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
788 return; // Start on first non-zero bitrate, may initially be zero.
789
790 int64_t now = clock_->TimeInMilliseconds();
791 if (uma_container_->target_rate_updates_.last_ms != -1) {
792 bool was_paused = stats_.target_media_bitrate_bps == 0;
793 int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
794 uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
795
796 // Use last to not include update when stream is stopped and video disabled.
797 if (uma_container_->target_rate_updates_.last_paused_or_resumed)
798 ++uma_container_->target_rate_updates_.pause_resume_events;
799
800 // Check if video is paused/resumed.
801 uma_container_->target_rate_updates_.last_paused_or_resumed =
802 (bitrate_bps == 0) != was_paused;
803 }
804 uma_container_->target_rate_updates_.last_ms = now;
805
pbos@webrtc.org891d4832015-02-26 13:15:22 +0000806 stats_.target_media_bitrate_bps = bitrate_bps;
807}
808
asapersson8d75ac72017-09-15 06:41:15 -0700809void SendStatisticsProxy::UpdateEncoderFallbackStats(
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100810 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200811 int pixels,
812 int simulcast_index) {
813 UpdateFallbackDisabledStats(codec_info, pixels, simulcast_index);
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100814
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100815 if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
asapersson8d75ac72017-09-15 06:41:15 -0700816 return;
817 }
818
Niels Möllerd3b8c632018-08-27 15:33:42 +0200819 if (!IsForcedFallbackPossible(codec_info, simulcast_index)) {
asapersson8d75ac72017-09-15 06:41:15 -0700820 uma_container_->fallback_info_.is_possible = false;
821 return;
822 }
823
824 FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_;
825
826 const int64_t now_ms = clock_->TimeInMilliseconds();
827 bool is_active = fallback_info->is_active;
Erik Språnge2fd86a2018-10-24 11:32:39 +0200828 if (encoder_changed_) {
asapersson8d75ac72017-09-15 06:41:15 -0700829 // Implementation changed.
Erik Språnge2fd86a2018-10-24 11:32:39 +0200830 const bool last_was_vp8_software =
831 encoder_changed_->previous_encoder_implementation == kVp8SwCodecName;
832 is_active = encoder_changed_->new_encoder_implementation == kVp8SwCodecName;
833 encoder_changed_.reset();
834 if (!is_active && !last_was_vp8_software) {
asapersson8d75ac72017-09-15 06:41:15 -0700835 // First or not a VP8 SW change, update stats on next call.
836 return;
837 }
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100838 if (is_active && (pixels > *fallback_max_pixels_)) {
839 // Pixels should not be above |fallback_max_pixels_|. If above skip to
840 // avoid fallbacks due to failure.
841 fallback_info->is_possible = false;
842 return;
asapersson8d75ac72017-09-15 06:41:15 -0700843 }
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100844 stats_.has_entered_low_resolution = true;
asapersson8d75ac72017-09-15 06:41:15 -0700845 ++fallback_info->on_off_events;
846 }
847
848 if (fallback_info->last_update_ms) {
849 int64_t diff_ms = now_ms - *(fallback_info->last_update_ms);
850 // If the time diff since last update is greater than |max_frame_diff_ms|,
851 // video is considered paused/muted and the change is not included.
852 if (diff_ms < fallback_info->max_frame_diff_ms) {
853 uma_container_->fallback_active_counter_.Add(fallback_info->is_active,
854 diff_ms);
855 fallback_info->elapsed_ms += diff_ms;
856 }
857 }
858 fallback_info->is_active = is_active;
859 fallback_info->last_update_ms.emplace(now_ms);
860}
861
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100862void SendStatisticsProxy::UpdateFallbackDisabledStats(
863 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200864 int pixels,
865 int simulcast_index) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100866 if (!fallback_max_pixels_disabled_ ||
867 !uma_container_->fallback_info_disabled_.is_possible ||
868 stats_.has_entered_low_resolution) {
869 return;
870 }
871
Niels Möllerd3b8c632018-08-27 15:33:42 +0200872 if (!IsForcedFallbackPossible(codec_info, simulcast_index) ||
Erik Språnge2fd86a2018-10-24 11:32:39 +0200873 stats_.encoder_implementation_name == kVp8SwCodecName) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100874 uma_container_->fallback_info_disabled_.is_possible = false;
875 return;
876 }
877
878 if (pixels <= *fallback_max_pixels_disabled_ ||
879 uma_container_->fallback_info_disabled_.min_pixel_limit_reached) {
880 stats_.has_entered_low_resolution = true;
881 }
882}
883
884void SendStatisticsProxy::OnMinPixelLimitReached() {
885 rtc::CritScope lock(&crit_);
886 uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true;
887}
888
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000889void SendStatisticsProxy::OnSendEncodedImage(
890 const EncodedImage& encoded_image,
kjellander02b3d272016-04-20 05:05:54 -0700891 const CodecSpecificInfo* codec_info) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200892 // Simulcast is used for VP8, H264 and Generic.
893 int simulcast_idx =
894 (codec_info && (codec_info->codecType == kVideoCodecVP8 ||
895 codec_info->codecType == kVideoCodecH264 ||
896 codec_info->codecType == kVideoCodecGeneric))
897 ? encoded_image.SpatialIndex().value_or(0)
898 : 0;
kjellander02b3d272016-04-20 05:05:54 -0700899
perkj275afc52016-09-01 00:21:16 -0700900 rtc::CritScope lock(&crit_);
sakal43536c32016-10-24 01:46:43 -0700901 ++stats_.frames_encoded;
Henrik Boström23aff9b2019-05-20 15:15:38 +0200902 // The current encode frame rate is based on previously encoded frames.
903 double encode_frame_rate = encoded_frame_rate_tracker_.ComputeRate();
904 // We assume that less than 1 FPS is not a trustworthy estimate - perhaps we
905 // just started encoding for the first time or after a pause. Assuming frame
906 // rate is at least 1 FPS is conservative to avoid too large increments.
907 if (encode_frame_rate < 1.0)
908 encode_frame_rate = 1.0;
909 double target_frame_size_bytes =
910 stats_.target_media_bitrate_bps / (8.0 * encode_frame_rate);
911 // |stats_.target_media_bitrate_bps| is set in
912 // SendStatisticsProxy::OnSetEncoderTargetRate.
913 stats_.total_encoded_bytes_target += round(target_frame_size_bytes);
kjellander02b3d272016-04-20 05:05:54 -0700914 if (codec_info) {
Erik Språnge2fd86a2018-10-24 11:32:39 +0200915 UpdateEncoderFallbackStats(
916 codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
917 simulcast_idx);
kjellander02b3d272016-04-20 05:05:54 -0700918 }
919
Niels Möllerd3b8c632018-08-27 15:33:42 +0200920 if (static_cast<size_t>(simulcast_idx) >= rtp_config_.ssrcs.size()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100921 RTC_LOG(LS_ERROR) << "Encoded image outside simulcast range ("
922 << simulcast_idx << " >= " << rtp_config_.ssrcs.size()
923 << ").";
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000924 return;
925 }
perkj26091b12016-09-01 01:17:40 -0700926 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000927
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000928 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200929 if (!stats)
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000930 return;
931
Sergey Silkinbb081a62018-09-04 18:34:22 +0200932 // Report resolution of top spatial layer in case of VP9 SVC.
933 bool is_svc_low_spatial_layer =
934 (codec_info && codec_info->codecType == kVideoCodecVP9)
935 ? !codec_info->codecSpecific.VP9.end_of_picture
936 : false;
937
938 if (!stats->width || !stats->height || !is_svc_low_spatial_layer) {
939 stats->width = encoded_image._encodedWidth;
940 stats->height = encoded_image._encodedHeight;
941 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
942 }
asaperssond89920b2015-07-22 06:52:00 -0700943
sprangb4a1ae52015-12-03 08:10:08 -0800944 uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
Niels Möller8f7ce222019-03-21 15:43:58 +0100945 VideoFrameType::kVideoFrameKey);
asapersson4306fc72015-10-19 00:35:21 -0700946
sakal87da4042016-10-31 06:53:47 -0700947 if (encoded_image.qp_ != -1) {
948 if (!stats_.qp_sum)
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100949 stats_.qp_sum = 0;
sakal87da4042016-10-31 06:53:47 -0700950 *stats_.qp_sum += encoded_image.qp_;
951
952 if (codec_info) {
953 if (codec_info->codecType == kVideoCodecVP8) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200954 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
sakal87da4042016-10-31 06:53:47 -0700955 uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
956 } else if (codec_info->codecType == kVideoCodecVP9) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200957 int spatial_idx = encoded_image.SpatialIndex().value_or(-1);
sakal87da4042016-10-31 06:53:47 -0700958 uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
asapersson827cab32016-11-02 09:08:47 -0700959 } else if (codec_info->codecType == kVideoCodecH264) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200960 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
asapersson827cab32016-11-02 09:08:47 -0700961 uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
sakal87da4042016-10-31 06:53:47 -0700962 }
asapersson5265fed2016-04-18 02:58:47 -0700963 }
asapersson118ef002016-03-31 00:00:19 -0700964 }
965
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100966 // If any of the simulcast streams have a huge frame, it should be counted
967 // as a single difficult input frame.
968 // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200969 if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) {
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100970 if (!last_outlier_timestamp_ ||
971 *last_outlier_timestamp_ < encoded_image.capture_time_ms_) {
972 last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_);
973 ++stats_.huge_frames_sent;
974 }
975 }
976
Niels Möller77536a22019-01-15 08:50:01 +0100977 media_byte_rate_tracker_.AddSamples(encoded_image.size());
Åsa Perssonaa329e72017-12-15 15:54:44 +0100978
979 // Initialize to current since |is_limited_in_resolution| is only updated
980 // when an encoded frame is removed from the EncodedFrameMap.
981 bool is_limited_in_resolution = stats_.bw_limited_resolution;
982 if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx,
983 &is_limited_in_resolution)) {
Åsa Persson0122e842017-10-16 12:19:23 +0200984 encoded_frame_rate_tracker_.AddSamples(1);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100985 }
986
987 stats_.bw_limited_resolution =
988 is_limited_in_resolution || quality_downscales_ > 0;
989
990 if (quality_downscales_ != -1) {
991 uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
992 if (quality_downscales_ > 0)
993 uma_container_->quality_downscales_counter_.Add(quality_downscales_);
994 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000995}
996
Erik Språnge2fd86a2018-10-24 11:32:39 +0200997void SendStatisticsProxy::OnEncoderImplementationChanged(
998 const std::string& implementation_name) {
999 rtc::CritScope lock(&crit_);
1000 encoder_changed_ = EncoderChangeEvent{stats_.encoder_implementation_name,
1001 implementation_name};
1002 stats_.encoder_implementation_name = implementation_name;
1003}
1004
Niels Möller213618e2018-07-24 09:29:58 +02001005int SendStatisticsProxy::GetInputFrameRate() const {
1006 rtc::CritScope lock(&crit_);
1007 return round(uma_container_->input_frame_rate_tracker_.ComputeRate());
1008}
1009
Per69b332d2016-06-02 15:45:42 +02001010int SendStatisticsProxy::GetSendFrameRate() const {
1011 rtc::CritScope lock(&crit_);
Åsa Persson0122e842017-10-16 12:19:23 +02001012 return round(encoded_frame_rate_tracker_.ComputeRate());
Per69b332d2016-06-02 15:45:42 +02001013}
1014
asaperssond89920b2015-07-22 06:52:00 -07001015void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001016 rtc::CritScope lock(&crit_);
sprangb4a1ae52015-12-03 08:10:08 -08001017 uma_container_->input_frame_rate_tracker_.AddSamples(1);
asapersson320e45a2016-11-29 01:40:35 -08001018 uma_container_->input_fps_counter_.Add(1);
sprangb4a1ae52015-12-03 08:10:08 -08001019 uma_container_->input_width_counter_.Add(width);
1020 uma_container_->input_height_counter_.Add(height);
asaperssonf4e44af2017-04-19 02:01:06 -07001021 if (cpu_downscales_ >= 0) {
1022 uma_container_->cpu_limited_frame_counter_.Add(
1023 stats_.cpu_limited_resolution);
1024 }
Åsa Perssond29b54c2017-10-19 17:32:17 +02001025 if (encoded_frame_rate_tracker_.TotalSampleCount() == 0) {
1026 // Set start time now instead of when first key frame is encoded to avoid a
1027 // too high initial estimate.
1028 encoded_frame_rate_tracker_.AddSamples(0);
1029 }
perkj803d97f2016-11-01 11:45:46 -07001030}
1031
Niels Möller213618e2018-07-24 09:29:58 +02001032void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001033 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001034 switch (reason) {
1035 case DropReason::kSource:
1036 ++stats_.frames_dropped_by_capturer;
1037 break;
1038 case DropReason::kEncoderQueue:
1039 ++stats_.frames_dropped_by_encoder_queue;
1040 break;
1041 case DropReason::kEncoder:
1042 ++stats_.frames_dropped_by_encoder;
1043 break;
1044 case DropReason::kMediaOptimization:
1045 ++stats_.frames_dropped_by_rate_limiter;
1046 break;
1047 }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001048}
1049
Niels Möller213618e2018-07-24 09:29:58 +02001050void SendStatisticsProxy::OnAdaptationChanged(
1051 AdaptationReason reason,
1052 const AdaptationSteps& cpu_counts,
1053 const AdaptationSteps& quality_counts) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001054 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001055 switch (reason) {
1056 case AdaptationReason::kNone:
1057 SetAdaptTimer(cpu_counts, &uma_container_->cpu_adapt_timer_);
1058 SetAdaptTimer(quality_counts, &uma_container_->quality_adapt_timer_);
1059 break;
1060 case AdaptationReason::kCpu:
1061 ++stats_.number_of_cpu_adapt_changes;
1062 break;
1063 case AdaptationReason::kQuality:
1064 TryUpdateInitialQualityResolutionAdaptUp(quality_counts);
1065 ++stats_.number_of_quality_adapt_changes;
1066 break;
1067 }
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001068
1069 bool is_cpu_limited = cpu_counts.num_resolution_reductions > 0 ||
1070 cpu_counts.num_framerate_reductions > 0;
1071 bool is_bandwidth_limited = quality_counts.num_resolution_reductions > 0 ||
1072 quality_counts.num_framerate_reductions > 0;
1073 if (is_bandwidth_limited) {
1074 // We may be both CPU limited and bandwidth limited at the same time but
1075 // there is no way to express this in standardized stats. Heuristically,
1076 // bandwidth is more likely to be a limiting factor than CPU, and more
1077 // likely to vary over time, so only when we aren't bandwidth limited do we
1078 // want to know about our CPU being the bottleneck.
1079 quality_limitation_reason_tracker_.SetReason(
1080 QualityLimitationReason::kBandwidth);
1081 } else if (is_cpu_limited) {
1082 quality_limitation_reason_tracker_.SetReason(QualityLimitationReason::kCpu);
1083 } else {
1084 quality_limitation_reason_tracker_.SetReason(
1085 QualityLimitationReason::kNone);
1086 }
1087
asapersson09f05612017-05-15 23:40:18 -07001088 UpdateAdaptationStats(cpu_counts, quality_counts);
1089}
1090
1091void SendStatisticsProxy::UpdateAdaptationStats(
Niels Möller213618e2018-07-24 09:29:58 +02001092 const AdaptationSteps& cpu_counts,
1093 const AdaptationSteps& quality_counts) {
1094 cpu_downscales_ = cpu_counts.num_resolution_reductions.value_or(-1);
1095 quality_downscales_ = quality_counts.num_resolution_reductions.value_or(-1);
asapersson09f05612017-05-15 23:40:18 -07001096
Niels Möller213618e2018-07-24 09:29: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;
Henrik Boströmce33b6a2019-05-28 17:42:38 +02001101 stats_.quality_limitation_reason =
1102 quality_limitation_reason_tracker_.current_reason();
1103 // |stats_.quality_limitation_durations_ms| depends on the current time
1104 // when it is polled; it is updated in SendStatisticsProxy::GetStats().
asapersson09f05612017-05-15 23:40:18 -07001105}
1106
Åsa Persson875841d2018-01-08 08:49:53 +01001107// TODO(asapersson): Include fps changes.
1108void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
1109 rtc::CritScope lock(&crit_);
1110 ++uma_container_->initial_quality_changes_.down;
1111}
1112
1113void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
Niels Möller213618e2018-07-24 09:29:58 +02001114 const AdaptationSteps& quality_counts) {
Åsa Persson875841d2018-01-08 08:49:53 +01001115 if (uma_container_->initial_quality_changes_.down == 0)
1116 return;
1117
1118 if (quality_downscales_ > 0 &&
Niels Möller213618e2018-07-24 09:29:58 +02001119 quality_counts.num_resolution_reductions.value_or(-1) <
1120 quality_downscales_) {
Åsa Persson875841d2018-01-08 08:49:53 +01001121 // Adapting up in quality.
1122 if (uma_container_->initial_quality_changes_.down >
1123 uma_container_->initial_quality_changes_.up) {
1124 ++uma_container_->initial_quality_changes_.up;
1125 }
1126 }
1127}
1128
Niels Möller213618e2018-07-24 09:29:58 +02001129void SendStatisticsProxy::SetAdaptTimer(const AdaptationSteps& counts,
1130 StatsTimer* timer) {
1131 if (counts.num_resolution_reductions || counts.num_framerate_reductions) {
asapersson09f05612017-05-15 23:40:18 -07001132 // Adaptation enabled.
1133 if (!stats_.suspended)
1134 timer->Start(clock_->TimeInMilliseconds());
1135 return;
1136 }
1137 timer->Stop(clock_->TimeInMilliseconds());
kthelgason876222f2016-11-29 01:44:11 -08001138}
1139
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001140void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
1141 uint32_t ssrc,
1142 const RtcpPacketTypeCounter& packet_counter) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001143 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001144 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001145 if (!stats)
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001146 return;
1147
1148 stats->rtcp_packet_type_counts = packet_counter;
sprang07fb9be2016-02-24 07:55:00 -08001149 if (uma_container_->first_rtcp_stats_time_ms_ == -1)
1150 uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001151}
1152
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001153void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
1154 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001155 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001156 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001157 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001158 return;
1159
1160 stats->rtcp_stats = statistics;
sprange2d83d62016-02-19 09:03:26 -08001161 uma_container_->report_block_stats_.Store(statistics, 0, ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001162}
1163
Peter Boströmd1d66ba2016-02-08 14:07:14 +01001164void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {}
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001165
Henrik Boström87e3f9d2019-05-27 10:44:24 +02001166void SendStatisticsProxy::OnReportBlockDataUpdated(
1167 ReportBlockData report_block_data) {
1168 rtc::CritScope lock(&crit_);
1169 VideoSendStream::StreamStats* stats =
1170 GetStatsEntry(report_block_data.report_block().source_ssrc);
1171 if (!stats)
1172 return;
1173 stats->report_block_data = std::move(report_block_data);
1174}
1175
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001176void SendStatisticsProxy::DataCountersUpdated(
1177 const StreamDataCounters& counters,
1178 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001179 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001180 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
asapersson93e1e232017-02-06 05:18:35 -08001181 RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc " << ssrc;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001182
asaperssona6a699a2016-11-25 03:52:46 -08001183 if (stats->is_flexfec) {
1184 // The same counters are reported for both the media ssrc and flexfec ssrc.
1185 // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
1186 return;
1187 }
1188
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001189 stats->rtp_stats = counters;
asapersson6eca98b2017-04-04 23:40:50 -07001190 if (uma_container_->first_rtp_stats_time_ms_ == -1) {
1191 int64_t now_ms = clock_->TimeInMilliseconds();
1192 uma_container_->first_rtp_stats_time_ms_ = now_ms;
asapersson09f05612017-05-15 23:40:18 -07001193 uma_container_->cpu_adapt_timer_.Restart(now_ms);
1194 uma_container_->quality_adapt_timer_.Restart(now_ms);
asapersson6eca98b2017-04-04 23:40:50 -07001195 }
asapersson93e1e232017-02-06 05:18:35 -08001196
1197 uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),
1198 ssrc);
1199 uma_container_->padding_byte_counter_.Set(counters.transmitted.padding_bytes,
1200 ssrc);
1201 uma_container_->retransmit_byte_counter_.Set(
1202 counters.retransmitted.TotalBytes(), ssrc);
1203 uma_container_->fec_byte_counter_.Set(counters.fec.TotalBytes(), ssrc);
1204 if (stats->is_rtx) {
1205 uma_container_->rtx_byte_counter_.Set(counters.transmitted.TotalBytes(),
1206 ssrc);
1207 } else {
1208 uma_container_->media_byte_counter_.Set(counters.MediaPayloadBytes(), ssrc);
1209 }
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001210}
1211
sprangcd349d92016-07-13 09:11:28 -07001212void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
1213 uint32_t retransmit_bitrate_bps,
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001214 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001215 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001216 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001217 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001218 return;
1219
sprangcd349d92016-07-13 09:11:28 -07001220 stats->total_bitrate_bps = total_bitrate_bps;
1221 stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001222}
1223
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001224void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
1225 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001226 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001227 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001228 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001229 return;
1230
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001231 stats->frame_counts = frame_counts;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001232}
1233
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001234void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
1235 int max_delay_ms,
Henrik Boström9fe18342019-05-16 18:38:20 +02001236 uint64_t total_delay_ms,
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001237 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001238 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001239 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001240 if (!stats)
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001241 return;
1242 stats->avg_delay_ms = avg_delay_ms;
1243 stats->max_delay_ms = max_delay_ms;
Henrik Boström9fe18342019-05-16 18:38:20 +02001244 stats->total_packet_send_delay_ms = total_delay_ms;
asaperssonf040b232015-11-04 00:59:03 -08001245
sprangb4a1ae52015-12-03 08:10:08 -08001246 uma_container_->delay_counter_.Add(avg_delay_ms);
1247 uma_container_->max_delay_counter_.Add(max_delay_ms);
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001248}
1249
asapersson6eca98b2017-04-04 23:40:50 -07001250void SendStatisticsProxy::StatsTimer::Start(int64_t now_ms) {
1251 if (start_ms == -1)
1252 start_ms = now_ms;
1253}
1254
1255void SendStatisticsProxy::StatsTimer::Stop(int64_t now_ms) {
1256 if (start_ms != -1) {
1257 total_ms += now_ms - start_ms;
1258 start_ms = -1;
1259 }
1260}
1261
1262void SendStatisticsProxy::StatsTimer::Restart(int64_t now_ms) {
1263 total_ms = 0;
1264 if (start_ms != -1)
1265 start_ms = now_ms;
1266}
1267
asaperssond89920b2015-07-22 06:52:00 -07001268void SendStatisticsProxy::SampleCounter::Add(int sample) {
1269 sum += sample;
1270 ++num_samples;
1271}
1272
asapersson66d4b372016-12-19 06:50:53 -08001273int SendStatisticsProxy::SampleCounter::Avg(
1274 int64_t min_required_samples) const {
asaperssond89920b2015-07-22 06:52:00 -07001275 if (num_samples < min_required_samples || num_samples == 0)
1276 return -1;
asapersson66d4b372016-12-19 06:50:53 -08001277 return static_cast<int>((sum + (num_samples / 2)) / num_samples);
asaperssond89920b2015-07-22 06:52:00 -07001278}
1279
asaperssondec5ebf2015-10-05 02:36:17 -07001280void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
1281 if (sample)
1282 ++sum;
1283 ++num_samples;
1284}
1285
asapersson66d4b372016-12-19 06:50:53 -08001286void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
1287 if (sample)
1288 sum += count;
1289 num_samples += count;
1290}
asaperssondec5ebf2015-10-05 02:36:17 -07001291int SendStatisticsProxy::BoolSampleCounter::Percent(
asapersson66d4b372016-12-19 06:50:53 -08001292 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001293 return Fraction(min_required_samples, 100.0f);
1294}
1295
1296int SendStatisticsProxy::BoolSampleCounter::Permille(
asapersson66d4b372016-12-19 06:50:53 -08001297 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001298 return Fraction(min_required_samples, 1000.0f);
1299}
1300
1301int SendStatisticsProxy::BoolSampleCounter::Fraction(
asapersson66d4b372016-12-19 06:50:53 -08001302 int64_t min_required_samples,
1303 float multiplier) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001304 if (num_samples < min_required_samples || num_samples == 0)
1305 return -1;
1306 return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
1307}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001308} // namespace webrtc