blob: 5c99a761cfe38164173afd43aa9ffdb892dfb371 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/video_coding/include/video_codec_interface.h"
19#include "rtc_base/checks.h"
20#include "rtc_base/logging.h"
Åsa Persson20317f92018-08-15 08:57:54 +020021#include "rtc_base/numerics/mod_ops.h"
Tommifef05002018-02-27 13:51:08 +010022#include "rtc_base/strings/string_builder.h"
asapersson8d75ac72017-09-15 06:41:15 -070023#include "system_wrappers/include/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "system_wrappers/include/metrics.h"
sprang@webrtc.orgccd42842014-01-07 09:54:34 +000025
26namespace webrtc {
asapersson2a0a2a42015-10-27 01:32:00 -070027namespace {
asapersson1aa420b2015-12-07 03:12:22 -080028const float kEncodeTimeWeigthFactor = 0.5f;
Åsa Persson0122e842017-10-16 12:19:23 +020029const size_t kMaxEncodedFrameMapSize = 150;
30const int64_t kMaxEncodedFrameWindowMs = 800;
Åsa Persson20317f92018-08-15 08:57:54 +020031const uint32_t kMaxEncodedFrameTimestampDiff = 900000; // 10 sec.
Åsa Persson0122e842017-10-16 12:19:23 +020032const int64_t kBucketSizeMs = 100;
33const size_t kBucketCount = 10;
asapersson1aa420b2015-12-07 03:12:22 -080034
asapersson8d75ac72017-09-15 06:41:15 -070035const char kVp8ForcedFallbackEncoderFieldTrial[] =
Åsa Persson45bbc8a2017-11-13 10:16:47 +010036 "WebRTC-VP8-Forced-Fallback-Encoder-v2";
asapersson8d75ac72017-09-15 06:41:15 -070037const char kVp8SwCodecName[] = "libvpx";
38
asapersson2a0a2a42015-10-27 01:32:00 -070039// Used by histograms. Values of entries should not be changed.
40enum HistogramCodecType {
41 kVideoUnknown = 0,
42 kVideoVp8 = 1,
43 kVideoVp9 = 2,
44 kVideoH264 = 3,
45 kVideoMax = 64,
46};
47
asaperssonc2148a52016-02-04 00:33:21 -080048const char* kRealtimePrefix = "WebRTC.Video.";
49const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
50
sprangb4a1ae52015-12-03 08:10:08 -080051const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
52 switch (content_type) {
53 case VideoEncoderConfig::ContentType::kRealtimeVideo:
asaperssonc2148a52016-02-04 00:33:21 -080054 return kRealtimePrefix;
sprangb4a1ae52015-12-03 08:10:08 -080055 case VideoEncoderConfig::ContentType::kScreen:
asaperssonc2148a52016-02-04 00:33:21 -080056 return kScreenPrefix;
sprangb4a1ae52015-12-03 08:10:08 -080057 }
58 RTC_NOTREACHED();
59 return nullptr;
60}
61
asapersson2a0a2a42015-10-27 01:32:00 -070062HistogramCodecType PayloadNameToHistogramCodecType(
63 const std::string& payload_name) {
kthelgason1cdddc92017-08-24 03:52:48 -070064 VideoCodecType codecType = PayloadStringToCodecType(payload_name);
65 switch (codecType) {
deadbeefc964d0b2017-04-03 10:03:35 -070066 case kVideoCodecVP8:
67 return kVideoVp8;
68 case kVideoCodecVP9:
69 return kVideoVp9;
70 case kVideoCodecH264:
71 return kVideoH264;
72 default:
73 return kVideoUnknown;
74 }
asapersson2a0a2a42015-10-27 01:32:00 -070075}
76
77void UpdateCodecTypeHistogram(const std::string& payload_name) {
asapersson28ba9272016-01-25 05:58:23 -080078 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
79 PayloadNameToHistogramCodecType(payload_name),
80 kVideoMax);
asapersson2a0a2a42015-10-27 01:32:00 -070081}
asapersson8d75ac72017-09-15 06:41:15 -070082
Niels Möllerd3b8c632018-08-27 15:33:42 +020083bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info,
84 int simulcast_index) {
85 return codec_info->codecType == kVideoCodecVP8 && simulcast_index == 0 &&
asapersson8d75ac72017-09-15 06:41:15 -070086 (codec_info->codecSpecific.VP8.temporalIdx == 0 ||
87 codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx);
88}
89
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020090absl::optional<int> GetFallbackMaxPixels(const std::string& group) {
asapersson8d75ac72017-09-15 06:41:15 -070091 if (group.empty())
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020092 return absl::nullopt;
asapersson8d75ac72017-09-15 06:41:15 -070093
asapersson8d75ac72017-09-15 06:41:15 -070094 int min_pixels;
Åsa Persson45bbc8a2017-11-13 10:16:47 +010095 int max_pixels;
96 int min_bps;
Åsa Perssonc3ed6302017-11-16 14:04:52 +010097 if (sscanf(group.c_str(), "-%d,%d,%d", &min_pixels, &max_pixels, &min_bps) !=
98 3) {
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020099 return absl::optional<int>();
asapersson8d75ac72017-09-15 06:41:15 -0700100 }
101
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100102 if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels)
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200103 return absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100104
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200105 return absl::optional<int>(max_pixels);
asapersson8d75ac72017-09-15 06:41:15 -0700106}
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100107
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200108absl::optional<int> GetFallbackMaxPixelsIfFieldTrialEnabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100109 std::string group =
110 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
111 return (group.find("Enabled") == 0) ? GetFallbackMaxPixels(group.substr(7))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200112 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100113}
114
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200115absl::optional<int> GetFallbackMaxPixelsIfFieldTrialDisabled() {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100116 std::string group =
117 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial);
118 return (group.find("Disabled") == 0) ? GetFallbackMaxPixels(group.substr(8))
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200119 : absl::optional<int>();
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100120}
asapersson2a0a2a42015-10-27 01:32:00 -0700121} // namespace
122
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000123const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
124
sprangb4a1ae52015-12-03 08:10:08 -0800125SendStatisticsProxy::SendStatisticsProxy(
126 Clock* clock,
127 const VideoSendStream::Config& config,
128 VideoEncoderConfig::ContentType content_type)
asaperssond89920b2015-07-22 06:52:00 -0700129 : clock_(clock),
Niels Möller259a4972018-04-05 15:36:51 +0200130 payload_name_(config.rtp.payload_name),
perkj26091b12016-09-01 01:17:40 -0700131 rtp_config_(config.rtp),
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100132 fallback_max_pixels_(GetFallbackMaxPixelsIfFieldTrialEnabled()),
133 fallback_max_pixels_disabled_(GetFallbackMaxPixelsIfFieldTrialDisabled()),
sprangb4a1ae52015-12-03 08:10:08 -0800134 content_type_(content_type),
asapersson4374a092016-07-27 00:39:09 -0700135 start_ms_(clock->TimeInMilliseconds()),
asapersson1aa420b2015-12-07 03:12:22 -0800136 encode_time_(kEncodeTimeWeigthFactor),
asapersson0944a802017-04-07 00:57:58 -0700137 quality_downscales_(-1),
138 cpu_downscales_(-1),
Åsa Persson0122e842017-10-16 12:19:23 +0200139 media_byte_rate_tracker_(kBucketSizeMs, kBucketCount),
140 encoded_frame_rate_tracker_(kBucketSizeMs, kBucketCount),
sprang07fb9be2016-02-24 07:55:00 -0800141 uma_container_(
142 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
pbos@webrtc.orgde1429e2014-04-28 13:00:21 +0000143}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000144
sprang07fb9be2016-02-24 07:55:00 -0800145SendStatisticsProxy::~SendStatisticsProxy() {
146 rtc::CritScope lock(&crit_);
perkj26091b12016-09-01 01:17:40 -0700147 uma_container_->UpdateHistograms(rtp_config_, stats_);
asapersson4374a092016-07-27 00:39:09 -0700148
149 int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
asapersson1d02d3e2016-09-09 22:40:25 -0700150 RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
151 elapsed_sec);
asapersson4374a092016-07-27 00:39:09 -0700152
153 if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
perkj26091b12016-09-01 01:17:40 -0700154 UpdateCodecTypeHistogram(payload_name_);
sprang07fb9be2016-02-24 07:55:00 -0800155}
sprangb4a1ae52015-12-03 08:10:08 -0800156
Mirko Bonadei8fdcac32018-08-28 16:30:18 +0200157SendStatisticsProxy::FallbackEncoderInfo::FallbackEncoderInfo() = default;
158
sprangb4a1ae52015-12-03 08:10:08 -0800159SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
sprang07fb9be2016-02-24 07:55:00 -0800160 const char* prefix,
161 const VideoSendStream::Stats& stats,
162 Clock* const clock)
sprangb4a1ae52015-12-03 08:10:08 -0800163 : uma_prefix_(prefix),
sprang07fb9be2016-02-24 07:55:00 -0800164 clock_(clock),
Honghai Zhang82d78622016-05-06 11:29:15 -0700165 input_frame_rate_tracker_(100, 10u),
asapersson320e45a2016-11-29 01:40:35 -0800166 input_fps_counter_(clock, nullptr, true),
167 sent_fps_counter_(clock, nullptr, true),
asapersson93e1e232017-02-06 05:18:35 -0800168 total_byte_counter_(clock, nullptr, true),
169 media_byte_counter_(clock, nullptr, true),
170 rtx_byte_counter_(clock, nullptr, true),
171 padding_byte_counter_(clock, nullptr, true),
172 retransmit_byte_counter_(clock, nullptr, true),
173 fec_byte_counter_(clock, nullptr, true),
sprang07fb9be2016-02-24 07:55:00 -0800174 first_rtcp_stats_time_ms_(-1),
Erik Språng22c2b482016-03-01 09:40:42 +0100175 first_rtp_stats_time_ms_(-1),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100176 start_stats_(stats),
177 num_streams_(0),
178 num_pixels_highest_stream_(0) {
asapersson93e1e232017-02-06 05:18:35 -0800179 InitializeBitrateCounters(stats);
Åsa Persson20317f92018-08-15 08:57:54 +0200180 static_assert(
181 kMaxEncodedFrameTimestampDiff < std::numeric_limits<uint32_t>::max() / 2,
182 "has to be smaller than half range");
asapersson93e1e232017-02-06 05:18:35 -0800183}
sprangb4a1ae52015-12-03 08:10:08 -0800184
sprang07fb9be2016-02-24 07:55:00 -0800185SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
Åsa Persson24b4eda2015-06-16 10:17:01 +0200186
asapersson93e1e232017-02-06 05:18:35 -0800187void SendStatisticsProxy::UmaSamplesContainer::InitializeBitrateCounters(
188 const VideoSendStream::Stats& stats) {
189 for (const auto& it : stats.substreams) {
190 uint32_t ssrc = it.first;
191 total_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
192 ssrc);
193 padding_byte_counter_.SetLast(it.second.rtp_stats.transmitted.padding_bytes,
194 ssrc);
195 retransmit_byte_counter_.SetLast(
196 it.second.rtp_stats.retransmitted.TotalBytes(), ssrc);
197 fec_byte_counter_.SetLast(it.second.rtp_stats.fec.TotalBytes(), ssrc);
198 if (it.second.is_rtx) {
199 rtx_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
200 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100201 } else {
asapersson93e1e232017-02-06 05:18:35 -0800202 media_byte_counter_.SetLast(it.second.rtp_stats.MediaPayloadBytes(),
203 ssrc);
Erik Språng22c2b482016-03-01 09:40:42 +0100204 }
205 }
206}
207
Åsa Perssonaa329e72017-12-15 15:54:44 +0100208void SendStatisticsProxy::UmaSamplesContainer::RemoveOld(
209 int64_t now_ms,
210 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200211 while (!encoded_frames_.empty()) {
212 auto it = encoded_frames_.begin();
213 if (now_ms - it->second.send_ms < kMaxEncodedFrameWindowMs)
214 break;
215
216 // Use max per timestamp.
217 sent_width_counter_.Add(it->second.max_width);
218 sent_height_counter_.Add(it->second.max_height);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100219
220 // Check number of encoded streams per timestamp.
Niels Möllerd3b8c632018-08-27 15:33:42 +0200221 if (num_streams_ > static_cast<size_t>(it->second.max_simulcast_idx)) {
Åsa Perssonaa329e72017-12-15 15:54:44 +0100222 *is_limited_in_resolution = false;
223 if (num_streams_ > 1) {
224 int disabled_streams =
225 static_cast<int>(num_streams_ - 1 - it->second.max_simulcast_idx);
226 // Can be limited in resolution or framerate.
227 uint32_t pixels = it->second.max_width * it->second.max_height;
228 bool bw_limited_resolution =
229 disabled_streams > 0 && pixels < num_pixels_highest_stream_;
230 bw_limited_frame_counter_.Add(bw_limited_resolution);
231 if (bw_limited_resolution) {
232 bw_resolutions_disabled_counter_.Add(disabled_streams);
233 *is_limited_in_resolution = true;
234 }
235 }
236 }
Åsa Persson0122e842017-10-16 12:19:23 +0200237 encoded_frames_.erase(it);
238 }
239}
240
241bool SendStatisticsProxy::UmaSamplesContainer::InsertEncodedFrame(
Åsa Perssonaa329e72017-12-15 15:54:44 +0100242 const EncodedImage& encoded_frame,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200243 int simulcast_idx,
Åsa Perssonaa329e72017-12-15 15:54:44 +0100244 bool* is_limited_in_resolution) {
Åsa Persson0122e842017-10-16 12:19:23 +0200245 int64_t now_ms = clock_->TimeInMilliseconds();
Åsa Perssonaa329e72017-12-15 15:54:44 +0100246 RemoveOld(now_ms, is_limited_in_resolution);
Åsa Persson0122e842017-10-16 12:19:23 +0200247 if (encoded_frames_.size() > kMaxEncodedFrameMapSize) {
248 encoded_frames_.clear();
249 }
250
Åsa Persson20317f92018-08-15 08:57:54 +0200251 // Check for jump in timestamp.
252 if (!encoded_frames_.empty()) {
253 uint32_t oldest_timestamp = encoded_frames_.begin()->first;
Niels Möller23775882018-08-16 10:24:12 +0200254 if (ForwardDiff(oldest_timestamp, encoded_frame.Timestamp()) >
Åsa Persson20317f92018-08-15 08:57:54 +0200255 kMaxEncodedFrameTimestampDiff) {
256 // Gap detected, clear frames to have a sequence where newest timestamp
257 // is not too far away from oldest in order to distinguish old and new.
258 encoded_frames_.clear();
259 }
260 }
261
Niels Möller72bc8d62018-09-12 10:03:51 +0200262 auto it = encoded_frames_.find(encoded_frame.Timestamp());
Åsa Persson0122e842017-10-16 12:19:23 +0200263 if (it == encoded_frames_.end()) {
264 // First frame with this timestamp.
Åsa Perssonaa329e72017-12-15 15:54:44 +0100265 encoded_frames_.insert(
Niels Möller23775882018-08-16 10:24:12 +0200266 std::make_pair(encoded_frame.Timestamp(),
Åsa Perssonaa329e72017-12-15 15:54:44 +0100267 Frame(now_ms, encoded_frame._encodedWidth,
268 encoded_frame._encodedHeight, simulcast_idx)));
Åsa Persson0122e842017-10-16 12:19:23 +0200269 sent_fps_counter_.Add(1);
270 return true;
271 }
272
273 it->second.max_width =
274 std::max(it->second.max_width, encoded_frame._encodedWidth);
275 it->second.max_height =
276 std::max(it->second.max_height, encoded_frame._encodedHeight);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100277 it->second.max_simulcast_idx =
278 std::max(it->second.max_simulcast_idx, simulcast_idx);
Åsa Persson0122e842017-10-16 12:19:23 +0200279 return false;
280}
281
sprang07fb9be2016-02-24 07:55:00 -0800282void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
Stefan Holmerdbdb3a02018-07-17 16:03:46 +0200283 const RtpConfig& rtp_config,
sprang07fb9be2016-02-24 07:55:00 -0800284 const VideoSendStream::Stats& current_stats) {
asaperssonc2148a52016-02-04 00:33:21 -0800285 RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
286 const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
asapersson320e45a2016-11-29 01:40:35 -0800287 const int kMinRequiredPeriodicSamples = 6;
Karl Wiberg881f1682018-03-08 15:03:23 +0100288 char log_stream_buf[8 * 1024];
289 rtc::SimpleStringBuilder log_stream(log_stream_buf);
perkj803d97f2016-11-01 11:45:46 -0700290 int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
291 int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700292 if (in_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700293 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
294 in_width);
295 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
296 in_height);
Tommifef05002018-02-27 13:51:08 +0100297 log_stream << uma_prefix_ << "InputWidthInPixels " << in_width << "\n"
298 << uma_prefix_ << "InputHeightInPixels " << in_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700299 }
asapersson320e45a2016-11-29 01:40:35 -0800300 AggregatedStats in_fps = input_fps_counter_.GetStats();
301 if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
302 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
303 in_fps.average);
Tommifef05002018-02-27 13:51:08 +0100304 log_stream << uma_prefix_ << "InputFramesPerSecond " << in_fps.ToString()
305 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800306 }
307
perkj803d97f2016-11-01 11:45:46 -0700308 int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
309 int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
asaperssond89920b2015-07-22 06:52:00 -0700310 if (sent_width != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700311 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
312 sent_width);
313 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
314 sent_height);
Tommifef05002018-02-27 13:51:08 +0100315 log_stream << uma_prefix_ << "SentWidthInPixels " << sent_width << "\n"
316 << uma_prefix_ << "SentHeightInPixels " << sent_height << "\n";
asaperssond89920b2015-07-22 06:52:00 -0700317 }
asapersson320e45a2016-11-29 01:40:35 -0800318 AggregatedStats sent_fps = sent_fps_counter_.GetStats();
319 if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
320 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
321 sent_fps.average);
Tommifef05002018-02-27 13:51:08 +0100322 log_stream << uma_prefix_ << "SentFramesPerSecond " << sent_fps.ToString()
323 << "\n";
asapersson320e45a2016-11-29 01:40:35 -0800324 }
325
ilnikf4ded682017-08-30 02:30:20 -0700326 if (in_fps.num_samples > kMinRequiredPeriodicSamples &&
327 sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
328 int in_fps_avg = in_fps.average;
329 if (in_fps_avg > 0) {
330 int sent_fps_avg = sent_fps.average;
331 int sent_to_in_fps_ratio_percent =
332 (100 * sent_fps_avg + in_fps_avg / 2) / in_fps_avg;
333 // If reported period is small, it may happen that sent_fps is larger than
334 // input_fps briefly on average. This should be treated as 100% sent to
335 // input ratio.
336 if (sent_to_in_fps_ratio_percent > 100)
337 sent_to_in_fps_ratio_percent = 100;
338 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
339 uma_prefix_ + "SentToInputFpsRatioPercent",
340 sent_to_in_fps_ratio_percent);
Tommifef05002018-02-27 13:51:08 +0100341 log_stream << uma_prefix_ << "SentToInputFpsRatioPercent "
342 << sent_to_in_fps_ratio_percent << "\n";
ilnikf4ded682017-08-30 02:30:20 -0700343 }
344 }
345
perkj803d97f2016-11-01 11:45:46 -0700346 int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonc2148a52016-02-04 00:33:21 -0800347 if (encode_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700348 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
349 encode_ms);
Tommifef05002018-02-27 13:51:08 +0100350 log_stream << uma_prefix_ << "EncodeTimeInMs " << encode_ms << "\n";
asaperssonc2148a52016-02-04 00:33:21 -0800351 }
perkj803d97f2016-11-01 11:45:46 -0700352 int key_frames_permille =
353 key_frame_counter_.Permille(kMinRequiredMetricsSamples);
asaperssondec5ebf2015-10-05 02:36:17 -0700354 if (key_frames_permille != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700355 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
356 key_frames_permille);
Tommifef05002018-02-27 13:51:08 +0100357 log_stream << uma_prefix_ << "KeyFramesSentInPermille "
358 << key_frames_permille << "\n";
asaperssondec5ebf2015-10-05 02:36:17 -0700359 }
asapersson4306fc72015-10-19 00:35:21 -0700360 int quality_limited =
perkj803d97f2016-11-01 11:45:46 -0700361 quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700362 if (quality_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700363 RTC_HISTOGRAMS_PERCENTAGE(kIndex,
364 uma_prefix_ + "QualityLimitedResolutionInPercent",
365 quality_limited);
Tommifef05002018-02-27 13:51:08 +0100366 log_stream << uma_prefix_ << "QualityLimitedResolutionInPercent "
367 << quality_limited << "\n";
asapersson4306fc72015-10-19 00:35:21 -0700368 }
perkj803d97f2016-11-01 11:45:46 -0700369 int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
asapersson4306fc72015-10-19 00:35:21 -0700370 if (downscales != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700371 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800372 kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
373 20);
asapersson4306fc72015-10-19 00:35:21 -0700374 }
perkj803d97f2016-11-01 11:45:46 -0700375 int cpu_limited =
376 cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
377 if (cpu_limited != -1) {
378 RTC_HISTOGRAMS_PERCENTAGE(
379 kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
380 }
381 int bw_limited =
382 bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700383 if (bw_limited != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700384 RTC_HISTOGRAMS_PERCENTAGE(
asaperssonc2148a52016-02-04 00:33:21 -0800385 kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
386 bw_limited);
asaperssonda535c42015-10-19 23:32:41 -0700387 }
perkj803d97f2016-11-01 11:45:46 -0700388 int num_disabled =
389 bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonda535c42015-10-19 23:32:41 -0700390 if (num_disabled != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700391 RTC_HISTOGRAMS_ENUMERATION(
asaperssonc2148a52016-02-04 00:33:21 -0800392 kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
393 num_disabled, 10);
asaperssonda535c42015-10-19 23:32:41 -0700394 }
perkj803d97f2016-11-01 11:45:46 -0700395 int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800396 if (delay_ms != -1)
asapersson1d02d3e2016-09-09 22:40:25 -0700397 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
398 delay_ms);
asaperssonf040b232015-11-04 00:59:03 -0800399
perkj803d97f2016-11-01 11:45:46 -0700400 int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
asaperssonf040b232015-11-04 00:59:03 -0800401 if (max_delay_ms != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700402 RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
403 max_delay_ms);
sprangb4a1ae52015-12-03 08:10:08 -0800404 }
sprang07fb9be2016-02-24 07:55:00 -0800405
asapersson118ef002016-03-31 00:00:19 -0700406 for (const auto& it : qp_counters_) {
perkj803d97f2016-11-01 11:45:46 -0700407 int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700408 if (qp_vp8 != -1) {
asapersson118ef002016-03-31 00:00:19 -0700409 int spatial_idx = it.first;
410 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700411 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
412 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700413 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700414 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
415 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700416 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700417 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
418 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700419 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700420 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
421 qp_vp8);
asapersson118ef002016-03-31 00:00:19 -0700422 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100423 RTC_LOG(LS_WARNING)
424 << "QP stats not recorded for VP8 spatial idx " << spatial_idx;
asapersson118ef002016-03-31 00:00:19 -0700425 }
426 }
perkj803d97f2016-11-01 11:45:46 -0700427 int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
asapersson5265fed2016-04-18 02:58:47 -0700428 if (qp_vp9 != -1) {
429 int spatial_idx = it.first;
430 if (spatial_idx == -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700431 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
432 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700433 } else if (spatial_idx == 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700434 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
435 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700436 } else if (spatial_idx == 1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700437 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
438 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700439 } else if (spatial_idx == 2) {
asapersson1d02d3e2016-09-09 22:40:25 -0700440 RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
441 qp_vp9);
asapersson5265fed2016-04-18 02:58:47 -0700442 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100443 RTC_LOG(LS_WARNING)
444 << "QP stats not recorded for VP9 spatial layer " << spatial_idx;
asapersson5265fed2016-04-18 02:58:47 -0700445 }
446 }
asapersson827cab32016-11-02 09:08:47 -0700447 int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
448 if (qp_h264 != -1) {
449 int spatial_idx = it.first;
Sergio Garcia Murillo43800f92018-06-21 16:16:38 +0200450 if (spatial_idx == -1) {
451 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264",
452 qp_h264);
453 } else if (spatial_idx == 0) {
454 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S0",
455 qp_h264);
456 } else if (spatial_idx == 1) {
457 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S1",
458 qp_h264);
459 } else if (spatial_idx == 2) {
460 RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S2",
461 qp_h264);
462 } else {
463 RTC_LOG(LS_WARNING)
464 << "QP stats not recorded for H264 spatial idx " << spatial_idx;
465 }
asapersson827cab32016-11-02 09:08:47 -0700466 }
asapersson118ef002016-03-31 00:00:19 -0700467 }
468
asapersson0944a802017-04-07 00:57:58 -0700469 if (first_rtp_stats_time_ms_ != -1) {
asapersson09f05612017-05-15 23:40:18 -0700470 quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
471 int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700472 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
473 int quality_changes = current_stats.number_of_quality_adapt_changes -
474 start_stats_.number_of_quality_adapt_changes;
Åsa Persson875841d2018-01-08 08:49:53 +0100475 // Only base stats on changes during a call, discard initial changes.
476 int initial_changes =
477 initial_quality_changes_.down + initial_quality_changes_.up;
478 if (initial_changes <= quality_changes)
479 quality_changes -= initial_changes;
asapersson0944a802017-04-07 00:57:58 -0700480 RTC_HISTOGRAMS_COUNTS_100(kIndex,
481 uma_prefix_ + "AdaptChangesPerMinute.Quality",
482 quality_changes * 60 / elapsed_sec);
483 }
asapersson09f05612017-05-15 23:40:18 -0700484 cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
485 elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
asapersson0944a802017-04-07 00:57:58 -0700486 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
487 int cpu_changes = current_stats.number_of_cpu_adapt_changes -
488 start_stats_.number_of_cpu_adapt_changes;
489 RTC_HISTOGRAMS_COUNTS_100(kIndex,
490 uma_prefix_ + "AdaptChangesPerMinute.Cpu",
491 cpu_changes * 60 / elapsed_sec);
492 }
asapersson6eca98b2017-04-04 23:40:50 -0700493 }
494
sprange2d83d62016-02-19 09:03:26 -0800495 if (first_rtcp_stats_time_ms_ != -1) {
sprang07fb9be2016-02-24 07:55:00 -0800496 int64_t elapsed_sec =
497 (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
498 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
499 int fraction_lost = report_block_stats_.FractionLostInPercent();
500 if (fraction_lost != -1) {
asapersson1d02d3e2016-09-09 22:40:25 -0700501 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800502 kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
Tommifef05002018-02-27 13:51:08 +0100503 log_stream << uma_prefix_ << "SentPacketsLostInPercent "
Stefan Holmer0a5792e2018-10-05 13:47:12 +0200504 << fraction_lost << "\n";
sprang07fb9be2016-02-24 07:55:00 -0800505 }
506
507 // The RTCP packet type counters, delivered via the
508 // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
509 // life of the send stream and are not reset when switching content type.
510 // For the purpose of these statistics though, we want new counts when
511 // switching since we switch histogram name. On every reset of the
512 // UmaSamplesContainer, we save the initial state of the counters, so that
513 // we can calculate the delta here and aggregate over all ssrcs.
514 RtcpPacketTypeCounter counters;
perkj26091b12016-09-01 01:17:40 -0700515 for (uint32_t ssrc : rtp_config.ssrcs) {
sprang07fb9be2016-02-24 07:55:00 -0800516 auto kv = current_stats.substreams.find(ssrc);
517 if (kv == current_stats.substreams.end())
518 continue;
519
520 RtcpPacketTypeCounter stream_counters =
521 kv->second.rtcp_packet_type_counts;
522 kv = start_stats_.substreams.find(ssrc);
523 if (kv != start_stats_.substreams.end())
524 stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
525
526 counters.Add(stream_counters);
527 }
asapersson1d02d3e2016-09-09 22:40:25 -0700528 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
529 uma_prefix_ + "NackPacketsReceivedPerMinute",
530 counters.nack_packets * 60 / elapsed_sec);
531 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
532 uma_prefix_ + "FirPacketsReceivedPerMinute",
533 counters.fir_packets * 60 / elapsed_sec);
534 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
535 uma_prefix_ + "PliPacketsReceivedPerMinute",
536 counters.pli_packets * 60 / elapsed_sec);
sprang07fb9be2016-02-24 07:55:00 -0800537 if (counters.nack_requests > 0) {
asapersson1d02d3e2016-09-09 22:40:25 -0700538 RTC_HISTOGRAMS_PERCENTAGE(
sprang07fb9be2016-02-24 07:55:00 -0800539 kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
540 counters.UniqueNackRequestsInPercent());
541 }
sprange2d83d62016-02-19 09:03:26 -0800542 }
543 }
Erik Språng22c2b482016-03-01 09:40:42 +0100544
545 if (first_rtp_stats_time_ms_ != -1) {
546 int64_t elapsed_sec =
547 (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
548 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
asapersson66d4b372016-12-19 06:50:53 -0800549 RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
550 target_rate_updates_.pause_resume_events);
Tommifef05002018-02-27 13:51:08 +0100551 log_stream << uma_prefix_ << "NumberOfPauseEvents "
552 << target_rate_updates_.pause_resume_events << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800553
554 int paused_time_percent =
555 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
556 if (paused_time_percent != -1) {
557 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
558 paused_time_percent);
Tommifef05002018-02-27 13:51:08 +0100559 log_stream << uma_prefix_ << "PausedTimeInPercent "
560 << paused_time_percent << "\n";
asapersson66d4b372016-12-19 06:50:53 -0800561 }
asapersson93e1e232017-02-06 05:18:35 -0800562 }
563 }
asapersson66d4b372016-12-19 06:50:53 -0800564
asapersson8d75ac72017-09-15 06:41:15 -0700565 if (fallback_info_.is_possible) {
566 // Double interval since there is some time before fallback may occur.
567 const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000;
568 int64_t elapsed_ms = fallback_info_.elapsed_ms;
569 int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs);
570 if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) {
571 RTC_HISTOGRAMS_PERCENTAGE(
572 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8",
573 fallback_time_percent);
574 RTC_HISTOGRAMS_COUNTS_100(
575 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8",
576 fallback_info_.on_off_events * 60 / (elapsed_ms / 1000));
577 }
578 }
579
asapersson93e1e232017-02-06 05:18:35 -0800580 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats();
581 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
582 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps",
583 total_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100584 log_stream << uma_prefix_ << "BitrateSentInBps "
585 << total_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800586 }
587 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats();
588 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
589 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
590 media_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100591 log_stream << uma_prefix_ << "MediaBitrateSentInBps "
592 << media_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800593 }
594 AggregatedStats padding_bytes_per_sec = padding_byte_counter_.GetStats();
595 if (padding_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
596 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
597 uma_prefix_ + "PaddingBitrateSentInKbps",
598 padding_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100599 log_stream << uma_prefix_ << "PaddingBitrateSentInBps "
600 << padding_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800601 }
602 AggregatedStats retransmit_bytes_per_sec =
603 retransmit_byte_counter_.GetStats();
604 if (retransmit_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
605 RTC_HISTOGRAMS_COUNTS_10000(kIndex,
606 uma_prefix_ + "RetransmittedBitrateSentInKbps",
607 retransmit_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100608 log_stream << uma_prefix_ << "RetransmittedBitrateSentInBps "
609 << retransmit_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800610 }
611 if (!rtp_config.rtx.ssrcs.empty()) {
612 AggregatedStats rtx_bytes_per_sec = rtx_byte_counter_.GetStats();
613 int rtx_bytes_per_sec_avg = -1;
614 if (rtx_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
615 rtx_bytes_per_sec_avg = rtx_bytes_per_sec.average;
Tommifef05002018-02-27 13:51:08 +0100616 log_stream << uma_prefix_ << "RtxBitrateSentInBps "
617 << rtx_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
asapersson93e1e232017-02-06 05:18:35 -0800618 } else if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
619 rtx_bytes_per_sec_avg = 0; // RTX enabled but no RTX data sent, record 0.
620 }
621 if (rtx_bytes_per_sec_avg != -1) {
622 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
623 rtx_bytes_per_sec_avg * 8 / 1000);
624 }
625 }
626 if (rtp_config.flexfec.payload_type != -1 ||
627 rtp_config.ulpfec.red_payload_type != -1) {
628 AggregatedStats fec_bytes_per_sec = fec_byte_counter_.GetStats();
629 if (fec_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
630 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "FecBitrateSentInKbps",
631 fec_bytes_per_sec.average * 8 / 1000);
Tommifef05002018-02-27 13:51:08 +0100632 log_stream << uma_prefix_ << "FecBitrateSentInBps "
633 << fec_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
Erik Språng22c2b482016-03-01 09:40:42 +0100634 }
635 }
Tommifef05002018-02-27 13:51:08 +0100636 log_stream << "Frames encoded " << current_stats.frames_encoded << "\n"
637 << uma_prefix_ << "DroppedFrames.Capturer "
638 << current_stats.frames_dropped_by_capturer << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200639 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
640 current_stats.frames_dropped_by_capturer);
Tommifef05002018-02-27 13:51:08 +0100641 log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
642 << current_stats.frames_dropped_by_encoder_queue << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200643 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
644 current_stats.frames_dropped_by_encoder_queue);
Tommifef05002018-02-27 13:51:08 +0100645 log_stream << uma_prefix_ << "DroppedFrames.Encoder "
646 << current_stats.frames_dropped_by_encoder << "\n";
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200647 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Encoder",
648 current_stats.frames_dropped_by_encoder);
Tommifef05002018-02-27 13:51:08 +0100649 log_stream << uma_prefix_ << "DroppedFrames.Ratelimiter "
650 << current_stats.frames_dropped_by_rate_limiter;
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +0200651 RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Ratelimiter",
652 current_stats.frames_dropped_by_rate_limiter);
Jonas Olsson694a36f2018-02-16 13:09:41 +0100653
Tommifef05002018-02-27 13:51:08 +0100654 RTC_LOG(LS_INFO) << log_stream.str();
sprangb4a1ae52015-12-03 08:10:08 -0800655}
656
Pera48ddb72016-09-29 11:48:50 +0200657void SendStatisticsProxy::OnEncoderReconfigured(
658 const VideoEncoderConfig& config,
Niels Möller97e04882018-05-25 09:43:26 +0200659 const std::vector<VideoStream>& streams) {
sprangb4a1ae52015-12-03 08:10:08 -0800660 rtc::CritScope lock(&crit_);
Pera48ddb72016-09-29 11:48:50 +0200661
662 if (content_type_ != config.content_type) {
perkj26091b12016-09-01 01:17:40 -0700663 uma_container_->UpdateHistograms(rtp_config_, stats_);
Pera48ddb72016-09-29 11:48:50 +0200664 uma_container_.reset(new UmaSamplesContainer(
665 GetUmaPrefix(config.content_type), stats_, clock_));
666 content_type_ = config.content_type;
asaperssonf040b232015-11-04 00:59:03 -0800667 }
Åsa Perssonaa329e72017-12-15 15:54:44 +0100668 uma_container_->encoded_frames_.clear();
669 uma_container_->num_streams_ = streams.size();
670 uma_container_->num_pixels_highest_stream_ =
671 streams.empty() ? 0 : (streams.back().width * streams.back().height);
Åsa Persson24b4eda2015-06-16 10:17:01 +0200672}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000673
Niels Möller213618e2018-07-24 09:29:58 +0200674void SendStatisticsProxy::OnEncodedFrameTimeMeasured(int encode_time_ms,
675 int encode_usage_percent) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200676 rtc::CritScope lock(&crit_);
Peter Boströme4499152016-02-05 11:13:28 +0100677 uma_container_->encode_time_counter_.Add(encode_time_ms);
678 encode_time_.Apply(1.0f, encode_time_ms);
679 stats_.avg_encode_time_ms = round(encode_time_.filtered());
Niels Möller213618e2018-07-24 09:29:58 +0200680 stats_.encode_usage_percent = encode_usage_percent;
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000681}
682
Peter Boström7083e112015-09-22 16:28:51 +0200683void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
asapersson0944a802017-04-07 00:57:58 -0700684 int64_t now_ms = clock_->TimeInMilliseconds();
Peter Boströmf2f82832015-05-01 13:00:41 +0200685 rtc::CritScope lock(&crit_);
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000686 stats_.suspended = is_suspended;
asapersson320e45a2016-11-29 01:40:35 -0800687 if (is_suspended) {
asapersson93e1e232017-02-06 05:18:35 -0800688 // Pause framerate (add min pause time since there may be frames/packets
689 // that are not yet sent).
690 const int64_t kMinMs = 500;
691 uma_container_->input_fps_counter_.ProcessAndPauseForDuration(kMinMs);
692 uma_container_->sent_fps_counter_.ProcessAndPauseForDuration(kMinMs);
693 // Pause bitrate stats.
694 uma_container_->total_byte_counter_.ProcessAndPauseForDuration(kMinMs);
695 uma_container_->media_byte_counter_.ProcessAndPauseForDuration(kMinMs);
696 uma_container_->rtx_byte_counter_.ProcessAndPauseForDuration(kMinMs);
697 uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
698 uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
699 uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
asapersson0944a802017-04-07 00:57:58 -0700700 // Stop adaptation stats.
asapersson09f05612017-05-15 23:40:18 -0700701 uma_container_->cpu_adapt_timer_.Stop(now_ms);
702 uma_container_->quality_adapt_timer_.Stop(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800703 } else {
asapersson0944a802017-04-07 00:57:58 -0700704 // Start adaptation stats if scaling is enabled.
705 if (cpu_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700706 uma_container_->cpu_adapt_timer_.Start(now_ms);
asapersson0944a802017-04-07 00:57:58 -0700707 if (quality_downscales_ >= 0)
asapersson09f05612017-05-15 23:40:18 -0700708 uma_container_->quality_adapt_timer_.Start(now_ms);
asapersson93e1e232017-02-06 05:18:35 -0800709 // Stop pause explicitly for stats that may be zero/not updated for some
710 // time.
711 uma_container_->rtx_byte_counter_.ProcessAndStopPause();
712 uma_container_->padding_byte_counter_.ProcessAndStopPause();
713 uma_container_->retransmit_byte_counter_.ProcessAndStopPause();
714 uma_container_->fec_byte_counter_.ProcessAndStopPause();
asapersson320e45a2016-11-29 01:40:35 -0800715 }
henrik.lundin@webrtc.orgb10363f2014-03-13 13:31:21 +0000716}
717
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000718VideoSendStream::Stats SendStatisticsProxy::GetStats() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200719 rtc::CritScope lock(&crit_);
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000720 PurgeOldStats();
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000721 stats_.input_frame_rate =
sprangb4a1ae52015-12-03 08:10:08 -0800722 round(uma_container_->input_frame_rate_tracker_.ComputeRate());
ilnik50864a82017-09-06 12:32:35 -0700723 stats_.content_type =
724 content_type_ == VideoEncoderConfig::ContentType::kRealtimeVideo
725 ? VideoContentType::UNSPECIFIED
726 : VideoContentType::SCREENSHARE;
Åsa Persson0122e842017-10-16 12:19:23 +0200727 stats_.encode_frame_rate = round(encoded_frame_rate_tracker_.ComputeRate());
728 stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000729 return stats_;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000730}
731
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000732void SendStatisticsProxy::PurgeOldStats() {
Peter Boström20f3f942015-05-15 11:33:39 +0200733 int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000734 for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
735 stats_.substreams.begin();
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000736 it != stats_.substreams.end(); ++it) {
737 uint32_t ssrc = it->first;
Peter Boström20f3f942015-05-15 11:33:39 +0200738 if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
739 it->second.width = 0;
740 it->second.height = 0;
741 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000742 }
743}
744
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000745VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
746 uint32_t ssrc) {
747 std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
748 stats_.substreams.find(ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000749 if (it != stats_.substreams.end())
750 return &it->second;
751
brandtrcd188f62016-11-15 08:21:52 -0800752 bool is_media = std::find(rtp_config_.ssrcs.begin(), rtp_config_.ssrcs.end(),
753 ssrc) != rtp_config_.ssrcs.end();
brandtr3d200bd2017-01-16 06:59:19 -0800754 bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
755 ssrc == rtp_config_.flexfec.ssrc;
brandtrcd188f62016-11-15 08:21:52 -0800756 bool is_rtx =
757 std::find(rtp_config_.rtx.ssrcs.begin(), rtp_config_.rtx.ssrcs.end(),
758 ssrc) != rtp_config_.rtx.ssrcs.end();
759 if (!is_media && !is_flexfec && !is_rtx)
760 return nullptr;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000761
asapersson2e5cfcd2016-08-11 08:41:18 -0700762 // Insert new entry and return ptr.
763 VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
764 entry->is_rtx = is_rtx;
asaperssona6a699a2016-11-25 03:52:46 -0800765 entry->is_flexfec = is_flexfec;
asapersson2e5cfcd2016-08-11 08:41:18 -0700766
767 return entry;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000768}
769
Peter Boström20f3f942015-05-15 11:33:39 +0200770void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
771 rtc::CritScope lock(&crit_);
772 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200773 if (!stats)
Peter Boström20f3f942015-05-15 11:33:39 +0200774 return;
775
776 stats->total_bitrate_bps = 0;
777 stats->retransmit_bitrate_bps = 0;
778 stats->height = 0;
779 stats->width = 0;
780}
781
perkjf5b2e512016-07-05 08:34:04 -0700782void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
Peter Boströmf2f82832015-05-01 13:00:41 +0200783 rtc::CritScope lock(&crit_);
asapersson66d4b372016-12-19 06:50:53 -0800784 if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
785 return; // Start on first non-zero bitrate, may initially be zero.
786
787 int64_t now = clock_->TimeInMilliseconds();
788 if (uma_container_->target_rate_updates_.last_ms != -1) {
789 bool was_paused = stats_.target_media_bitrate_bps == 0;
790 int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
791 uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
792
793 // Use last to not include update when stream is stopped and video disabled.
794 if (uma_container_->target_rate_updates_.last_paused_or_resumed)
795 ++uma_container_->target_rate_updates_.pause_resume_events;
796
797 // Check if video is paused/resumed.
798 uma_container_->target_rate_updates_.last_paused_or_resumed =
799 (bitrate_bps == 0) != was_paused;
800 }
801 uma_container_->target_rate_updates_.last_ms = now;
802
pbos@webrtc.org891d4832015-02-26 13:15:22 +0000803 stats_.target_media_bitrate_bps = bitrate_bps;
804}
805
asapersson8d75ac72017-09-15 06:41:15 -0700806void SendStatisticsProxy::UpdateEncoderFallbackStats(
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100807 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200808 int pixels,
809 int simulcast_index) {
810 UpdateFallbackDisabledStats(codec_info, pixels, simulcast_index);
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100811
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100812 if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
asapersson8d75ac72017-09-15 06:41:15 -0700813 return;
814 }
815
Niels Möllerd3b8c632018-08-27 15:33:42 +0200816 if (!IsForcedFallbackPossible(codec_info, simulcast_index)) {
asapersson8d75ac72017-09-15 06:41:15 -0700817 uma_container_->fallback_info_.is_possible = false;
818 return;
819 }
820
821 FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_;
822
823 const int64_t now_ms = clock_->TimeInMilliseconds();
824 bool is_active = fallback_info->is_active;
Erik Språnge2fd86a2018-10-24 11:32:39 +0200825 if (encoder_changed_) {
asapersson8d75ac72017-09-15 06:41:15 -0700826 // Implementation changed.
Erik Språnge2fd86a2018-10-24 11:32:39 +0200827 const bool last_was_vp8_software =
828 encoder_changed_->previous_encoder_implementation == kVp8SwCodecName;
829 is_active = encoder_changed_->new_encoder_implementation == kVp8SwCodecName;
830 encoder_changed_.reset();
831 if (!is_active && !last_was_vp8_software) {
asapersson8d75ac72017-09-15 06:41:15 -0700832 // First or not a VP8 SW change, update stats on next call.
833 return;
834 }
Åsa Persson45bbc8a2017-11-13 10:16:47 +0100835 if (is_active && (pixels > *fallback_max_pixels_)) {
836 // Pixels should not be above |fallback_max_pixels_|. If above skip to
837 // avoid fallbacks due to failure.
838 fallback_info->is_possible = false;
839 return;
asapersson8d75ac72017-09-15 06:41:15 -0700840 }
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100841 stats_.has_entered_low_resolution = true;
asapersson8d75ac72017-09-15 06:41:15 -0700842 ++fallback_info->on_off_events;
843 }
844
845 if (fallback_info->last_update_ms) {
846 int64_t diff_ms = now_ms - *(fallback_info->last_update_ms);
847 // If the time diff since last update is greater than |max_frame_diff_ms|,
848 // video is considered paused/muted and the change is not included.
849 if (diff_ms < fallback_info->max_frame_diff_ms) {
850 uma_container_->fallback_active_counter_.Add(fallback_info->is_active,
851 diff_ms);
852 fallback_info->elapsed_ms += diff_ms;
853 }
854 }
855 fallback_info->is_active = is_active;
856 fallback_info->last_update_ms.emplace(now_ms);
857}
858
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100859void SendStatisticsProxy::UpdateFallbackDisabledStats(
860 const CodecSpecificInfo* codec_info,
Niels Möllerd3b8c632018-08-27 15:33:42 +0200861 int pixels,
862 int simulcast_index) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100863 if (!fallback_max_pixels_disabled_ ||
864 !uma_container_->fallback_info_disabled_.is_possible ||
865 stats_.has_entered_low_resolution) {
866 return;
867 }
868
Niels Möllerd3b8c632018-08-27 15:33:42 +0200869 if (!IsForcedFallbackPossible(codec_info, simulcast_index) ||
Erik Språnge2fd86a2018-10-24 11:32:39 +0200870 stats_.encoder_implementation_name == kVp8SwCodecName) {
Åsa Perssonc3ed6302017-11-16 14:04:52 +0100871 uma_container_->fallback_info_disabled_.is_possible = false;
872 return;
873 }
874
875 if (pixels <= *fallback_max_pixels_disabled_ ||
876 uma_container_->fallback_info_disabled_.min_pixel_limit_reached) {
877 stats_.has_entered_low_resolution = true;
878 }
879}
880
881void SendStatisticsProxy::OnMinPixelLimitReached() {
882 rtc::CritScope lock(&crit_);
883 uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true;
884}
885
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000886void SendStatisticsProxy::OnSendEncodedImage(
887 const EncodedImage& encoded_image,
kjellander02b3d272016-04-20 05:05:54 -0700888 const CodecSpecificInfo* codec_info) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200889 // Simulcast is used for VP8, H264 and Generic.
890 int simulcast_idx =
891 (codec_info && (codec_info->codecType == kVideoCodecVP8 ||
892 codec_info->codecType == kVideoCodecH264 ||
893 codec_info->codecType == kVideoCodecGeneric))
894 ? encoded_image.SpatialIndex().value_or(0)
895 : 0;
kjellander02b3d272016-04-20 05:05:54 -0700896
perkj275afc52016-09-01 00:21:16 -0700897 rtc::CritScope lock(&crit_);
sakal43536c32016-10-24 01:46:43 -0700898 ++stats_.frames_encoded;
kjellander02b3d272016-04-20 05:05:54 -0700899 if (codec_info) {
Erik Språnge2fd86a2018-10-24 11:32:39 +0200900 UpdateEncoderFallbackStats(
901 codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
902 simulcast_idx);
kjellander02b3d272016-04-20 05:05:54 -0700903 }
904
Niels Möllerd3b8c632018-08-27 15:33:42 +0200905 if (static_cast<size_t>(simulcast_idx) >= rtp_config_.ssrcs.size()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100906 RTC_LOG(LS_ERROR) << "Encoded image outside simulcast range ("
907 << simulcast_idx << " >= " << rtp_config_.ssrcs.size()
908 << ").";
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000909 return;
910 }
perkj26091b12016-09-01 01:17:40 -0700911 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000912
pbos@webrtc.org09c77b92015-02-25 10:42:16 +0000913 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +0200914 if (!stats)
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000915 return;
916
Sergey Silkinbb081a62018-09-04 18:34:22 +0200917 // Report resolution of top spatial layer in case of VP9 SVC.
918 bool is_svc_low_spatial_layer =
919 (codec_info && codec_info->codecType == kVideoCodecVP9)
920 ? !codec_info->codecSpecific.VP9.end_of_picture
921 : false;
922
923 if (!stats->width || !stats->height || !is_svc_low_spatial_layer) {
924 stats->width = encoded_image._encodedWidth;
925 stats->height = encoded_image._encodedHeight;
926 update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
927 }
asaperssond89920b2015-07-22 06:52:00 -0700928
sprangb4a1ae52015-12-03 08:10:08 -0800929 uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
Niels Möller8f7ce222019-03-21 15:43:58 +0100930 VideoFrameType::kVideoFrameKey);
asapersson4306fc72015-10-19 00:35:21 -0700931
sakal87da4042016-10-31 06:53:47 -0700932 if (encoded_image.qp_ != -1) {
933 if (!stats_.qp_sum)
Oskar Sundbom8e07c132018-01-08 16:45:42 +0100934 stats_.qp_sum = 0;
sakal87da4042016-10-31 06:53:47 -0700935 *stats_.qp_sum += encoded_image.qp_;
936
937 if (codec_info) {
938 if (codec_info->codecType == kVideoCodecVP8) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200939 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
sakal87da4042016-10-31 06:53:47 -0700940 uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
941 } else if (codec_info->codecType == kVideoCodecVP9) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200942 int spatial_idx = encoded_image.SpatialIndex().value_or(-1);
sakal87da4042016-10-31 06:53:47 -0700943 uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
asapersson827cab32016-11-02 09:08:47 -0700944 } else if (codec_info->codecType == kVideoCodecH264) {
Niels Möllerd3b8c632018-08-27 15:33:42 +0200945 int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
asapersson827cab32016-11-02 09:08:47 -0700946 uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
sakal87da4042016-10-31 06:53:47 -0700947 }
asapersson5265fed2016-04-18 02:58:47 -0700948 }
asapersson118ef002016-03-31 00:00:19 -0700949 }
950
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100951 // If any of the simulcast streams have a huge frame, it should be counted
952 // as a single difficult input frame.
953 // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
Ilya Nikolaevskiyb6c462d2018-06-05 15:21:32 +0200954 if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) {
Ilya Nikolaevskiy70473fc2018-02-28 16:35:03 +0100955 if (!last_outlier_timestamp_ ||
956 *last_outlier_timestamp_ < encoded_image.capture_time_ms_) {
957 last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_);
958 ++stats_.huge_frames_sent;
959 }
960 }
961
Niels Möller77536a22019-01-15 08:50:01 +0100962 media_byte_rate_tracker_.AddSamples(encoded_image.size());
Åsa Perssonaa329e72017-12-15 15:54:44 +0100963
964 // Initialize to current since |is_limited_in_resolution| is only updated
965 // when an encoded frame is removed from the EncodedFrameMap.
966 bool is_limited_in_resolution = stats_.bw_limited_resolution;
967 if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx,
968 &is_limited_in_resolution)) {
Åsa Persson0122e842017-10-16 12:19:23 +0200969 encoded_frame_rate_tracker_.AddSamples(1);
Åsa Perssonaa329e72017-12-15 15:54:44 +0100970 }
971
972 stats_.bw_limited_resolution =
973 is_limited_in_resolution || quality_downscales_ > 0;
974
975 if (quality_downscales_ != -1) {
976 uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
977 if (quality_downscales_ > 0)
978 uma_container_->quality_downscales_counter_.Add(quality_downscales_);
979 }
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000980}
981
Erik Språnge2fd86a2018-10-24 11:32:39 +0200982void SendStatisticsProxy::OnEncoderImplementationChanged(
983 const std::string& implementation_name) {
984 rtc::CritScope lock(&crit_);
985 encoder_changed_ = EncoderChangeEvent{stats_.encoder_implementation_name,
986 implementation_name};
987 stats_.encoder_implementation_name = implementation_name;
988}
989
Niels Möller213618e2018-07-24 09:29:58 +0200990int SendStatisticsProxy::GetInputFrameRate() const {
991 rtc::CritScope lock(&crit_);
992 return round(uma_container_->input_frame_rate_tracker_.ComputeRate());
993}
994
Per69b332d2016-06-02 15:45:42 +0200995int SendStatisticsProxy::GetSendFrameRate() const {
996 rtc::CritScope lock(&crit_);
Åsa Persson0122e842017-10-16 12:19:23 +0200997 return round(encoded_frame_rate_tracker_.ComputeRate());
Per69b332d2016-06-02 15:45:42 +0200998}
999
asaperssond89920b2015-07-22 06:52:00 -07001000void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001001 rtc::CritScope lock(&crit_);
sprangb4a1ae52015-12-03 08:10:08 -08001002 uma_container_->input_frame_rate_tracker_.AddSamples(1);
asapersson320e45a2016-11-29 01:40:35 -08001003 uma_container_->input_fps_counter_.Add(1);
sprangb4a1ae52015-12-03 08:10:08 -08001004 uma_container_->input_width_counter_.Add(width);
1005 uma_container_->input_height_counter_.Add(height);
asaperssonf4e44af2017-04-19 02:01:06 -07001006 if (cpu_downscales_ >= 0) {
1007 uma_container_->cpu_limited_frame_counter_.Add(
1008 stats_.cpu_limited_resolution);
1009 }
Åsa Perssond29b54c2017-10-19 17:32:17 +02001010 if (encoded_frame_rate_tracker_.TotalSampleCount() == 0) {
1011 // Set start time now instead of when first key frame is encoded to avoid a
1012 // too high initial estimate.
1013 encoded_frame_rate_tracker_.AddSamples(0);
1014 }
perkj803d97f2016-11-01 11:45:46 -07001015}
1016
Niels Möller213618e2018-07-24 09:29:58 +02001017void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001018 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001019 switch (reason) {
1020 case DropReason::kSource:
1021 ++stats_.frames_dropped_by_capturer;
1022 break;
1023 case DropReason::kEncoderQueue:
1024 ++stats_.frames_dropped_by_encoder_queue;
1025 break;
1026 case DropReason::kEncoder:
1027 ++stats_.frames_dropped_by_encoder;
1028 break;
1029 case DropReason::kMediaOptimization:
1030 ++stats_.frames_dropped_by_rate_limiter;
1031 break;
1032 }
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001033}
1034
Niels Möller213618e2018-07-24 09:29:58 +02001035void SendStatisticsProxy::OnAdaptationChanged(
1036 AdaptationReason reason,
1037 const AdaptationSteps& cpu_counts,
1038 const AdaptationSteps& quality_counts) {
Ilya Nikolaevskiyd79314f2017-10-23 10:45:37 +02001039 rtc::CritScope lock(&crit_);
Niels Möller213618e2018-07-24 09:29:58 +02001040 switch (reason) {
1041 case AdaptationReason::kNone:
1042 SetAdaptTimer(cpu_counts, &uma_container_->cpu_adapt_timer_);
1043 SetAdaptTimer(quality_counts, &uma_container_->quality_adapt_timer_);
1044 break;
1045 case AdaptationReason::kCpu:
1046 ++stats_.number_of_cpu_adapt_changes;
1047 break;
1048 case AdaptationReason::kQuality:
1049 TryUpdateInitialQualityResolutionAdaptUp(quality_counts);
1050 ++stats_.number_of_quality_adapt_changes;
1051 break;
1052 }
asapersson09f05612017-05-15 23:40:18 -07001053 UpdateAdaptationStats(cpu_counts, quality_counts);
1054}
1055
1056void SendStatisticsProxy::UpdateAdaptationStats(
Niels Möller213618e2018-07-24 09:29:58 +02001057 const AdaptationSteps& cpu_counts,
1058 const AdaptationSteps& quality_counts) {
1059 cpu_downscales_ = cpu_counts.num_resolution_reductions.value_or(-1);
1060 quality_downscales_ = quality_counts.num_resolution_reductions.value_or(-1);
asapersson09f05612017-05-15 23:40:18 -07001061
Niels Möller213618e2018-07-24 09:29:58 +02001062 stats_.cpu_limited_resolution = cpu_counts.num_resolution_reductions > 0;
1063 stats_.cpu_limited_framerate = cpu_counts.num_framerate_reductions > 0;
1064 stats_.bw_limited_resolution = quality_counts.num_resolution_reductions > 0;
1065 stats_.bw_limited_framerate = quality_counts.num_framerate_reductions > 0;
asapersson09f05612017-05-15 23:40:18 -07001066}
1067
Åsa Persson875841d2018-01-08 08:49:53 +01001068// TODO(asapersson): Include fps changes.
1069void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
1070 rtc::CritScope lock(&crit_);
1071 ++uma_container_->initial_quality_changes_.down;
1072}
1073
1074void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
Niels Möller213618e2018-07-24 09:29:58 +02001075 const AdaptationSteps& quality_counts) {
Åsa Persson875841d2018-01-08 08:49:53 +01001076 if (uma_container_->initial_quality_changes_.down == 0)
1077 return;
1078
1079 if (quality_downscales_ > 0 &&
Niels Möller213618e2018-07-24 09:29:58 +02001080 quality_counts.num_resolution_reductions.value_or(-1) <
1081 quality_downscales_) {
Åsa Persson875841d2018-01-08 08:49:53 +01001082 // Adapting up in quality.
1083 if (uma_container_->initial_quality_changes_.down >
1084 uma_container_->initial_quality_changes_.up) {
1085 ++uma_container_->initial_quality_changes_.up;
1086 }
1087 }
1088}
1089
Niels Möller213618e2018-07-24 09:29:58 +02001090void SendStatisticsProxy::SetAdaptTimer(const AdaptationSteps& counts,
1091 StatsTimer* timer) {
1092 if (counts.num_resolution_reductions || counts.num_framerate_reductions) {
asapersson09f05612017-05-15 23:40:18 -07001093 // Adaptation enabled.
1094 if (!stats_.suspended)
1095 timer->Start(clock_->TimeInMilliseconds());
1096 return;
1097 }
1098 timer->Stop(clock_->TimeInMilliseconds());
kthelgason876222f2016-11-29 01:44:11 -08001099}
1100
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001101void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
1102 uint32_t ssrc,
1103 const RtcpPacketTypeCounter& packet_counter) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001104 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001105 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001106 if (!stats)
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001107 return;
1108
1109 stats->rtcp_packet_type_counts = packet_counter;
sprang07fb9be2016-02-24 07:55:00 -08001110 if (uma_container_->first_rtcp_stats_time_ms_ == -1)
1111 uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
pbos@webrtc.org1d0fa5d2015-02-19 12:47:00 +00001112}
1113
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001114void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
1115 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001116 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001117 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001118 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001119 return;
1120
1121 stats->rtcp_stats = statistics;
sprange2d83d62016-02-19 09:03:26 -08001122 uma_container_->report_block_stats_.Store(statistics, 0, ssrc);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001123}
1124
Peter Boströmd1d66ba2016-02-08 14:07:14 +01001125void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {}
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001126
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001127void SendStatisticsProxy::DataCountersUpdated(
1128 const StreamDataCounters& counters,
1129 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001130 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001131 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
asapersson93e1e232017-02-06 05:18:35 -08001132 RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc " << ssrc;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001133
asaperssona6a699a2016-11-25 03:52:46 -08001134 if (stats->is_flexfec) {
1135 // The same counters are reported for both the media ssrc and flexfec ssrc.
1136 // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
1137 return;
1138 }
1139
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001140 stats->rtp_stats = counters;
asapersson6eca98b2017-04-04 23:40:50 -07001141 if (uma_container_->first_rtp_stats_time_ms_ == -1) {
1142 int64_t now_ms = clock_->TimeInMilliseconds();
1143 uma_container_->first_rtp_stats_time_ms_ = now_ms;
asapersson09f05612017-05-15 23:40:18 -07001144 uma_container_->cpu_adapt_timer_.Restart(now_ms);
1145 uma_container_->quality_adapt_timer_.Restart(now_ms);
asapersson6eca98b2017-04-04 23:40:50 -07001146 }
asapersson93e1e232017-02-06 05:18:35 -08001147
1148 uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),
1149 ssrc);
1150 uma_container_->padding_byte_counter_.Set(counters.transmitted.padding_bytes,
1151 ssrc);
1152 uma_container_->retransmit_byte_counter_.Set(
1153 counters.retransmitted.TotalBytes(), ssrc);
1154 uma_container_->fec_byte_counter_.Set(counters.fec.TotalBytes(), ssrc);
1155 if (stats->is_rtx) {
1156 uma_container_->rtx_byte_counter_.Set(counters.transmitted.TotalBytes(),
1157 ssrc);
1158 } else {
1159 uma_container_->media_byte_counter_.Set(counters.MediaPayloadBytes(), ssrc);
1160 }
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001161}
1162
sprangcd349d92016-07-13 09:11:28 -07001163void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
1164 uint32_t retransmit_bitrate_bps,
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001165 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001166 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001167 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001168 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001169 return;
1170
sprangcd349d92016-07-13 09:11:28 -07001171 stats->total_bitrate_bps = total_bitrate_bps;
1172 stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001173}
1174
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001175void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
1176 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001177 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001178 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001179 if (!stats)
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001180 return;
1181
pbos@webrtc.orgce4e9a32014-12-18 13:50:16 +00001182 stats->frame_counts = frame_counts;
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001183}
1184
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001185void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
1186 int max_delay_ms,
1187 uint32_t ssrc) {
Peter Boströmf2f82832015-05-01 13:00:41 +02001188 rtc::CritScope lock(&crit_);
pbos@webrtc.org09c77b92015-02-25 10:42:16 +00001189 VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
Peter Boström74f6e9e2016-04-04 17:56:10 +02001190 if (!stats)
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001191 return;
1192 stats->avg_delay_ms = avg_delay_ms;
1193 stats->max_delay_ms = max_delay_ms;
asaperssonf040b232015-11-04 00:59:03 -08001194
sprangb4a1ae52015-12-03 08:10:08 -08001195 uma_container_->delay_counter_.Add(avg_delay_ms);
1196 uma_container_->max_delay_counter_.Add(max_delay_ms);
stefan@webrtc.org168f23f2014-07-11 13:44:02 +00001197}
1198
asapersson6eca98b2017-04-04 23:40:50 -07001199void SendStatisticsProxy::StatsTimer::Start(int64_t now_ms) {
1200 if (start_ms == -1)
1201 start_ms = now_ms;
1202}
1203
1204void SendStatisticsProxy::StatsTimer::Stop(int64_t now_ms) {
1205 if (start_ms != -1) {
1206 total_ms += now_ms - start_ms;
1207 start_ms = -1;
1208 }
1209}
1210
1211void SendStatisticsProxy::StatsTimer::Restart(int64_t now_ms) {
1212 total_ms = 0;
1213 if (start_ms != -1)
1214 start_ms = now_ms;
1215}
1216
asaperssond89920b2015-07-22 06:52:00 -07001217void SendStatisticsProxy::SampleCounter::Add(int sample) {
1218 sum += sample;
1219 ++num_samples;
1220}
1221
asapersson66d4b372016-12-19 06:50:53 -08001222int SendStatisticsProxy::SampleCounter::Avg(
1223 int64_t min_required_samples) const {
asaperssond89920b2015-07-22 06:52:00 -07001224 if (num_samples < min_required_samples || num_samples == 0)
1225 return -1;
asapersson66d4b372016-12-19 06:50:53 -08001226 return static_cast<int>((sum + (num_samples / 2)) / num_samples);
asaperssond89920b2015-07-22 06:52:00 -07001227}
1228
asaperssondec5ebf2015-10-05 02:36:17 -07001229void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
1230 if (sample)
1231 ++sum;
1232 ++num_samples;
1233}
1234
asapersson66d4b372016-12-19 06:50:53 -08001235void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
1236 if (sample)
1237 sum += count;
1238 num_samples += count;
1239}
asaperssondec5ebf2015-10-05 02:36:17 -07001240int SendStatisticsProxy::BoolSampleCounter::Percent(
asapersson66d4b372016-12-19 06:50:53 -08001241 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001242 return Fraction(min_required_samples, 100.0f);
1243}
1244
1245int SendStatisticsProxy::BoolSampleCounter::Permille(
asapersson66d4b372016-12-19 06:50:53 -08001246 int64_t min_required_samples) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001247 return Fraction(min_required_samples, 1000.0f);
1248}
1249
1250int SendStatisticsProxy::BoolSampleCounter::Fraction(
asapersson66d4b372016-12-19 06:50:53 -08001251 int64_t min_required_samples,
1252 float multiplier) const {
asaperssondec5ebf2015-10-05 02:36:17 -07001253 if (num_samples < min_required_samples || num_samples == 0)
1254 return -1;
1255 return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
1256}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +00001257} // namespace webrtc